]> git.tdb.fi Git - libs/gui.git/blobdiff - source/graphics/quartz/quartzloader.cpp
Support for using Quartz to load images on OS X
[libs/gui.git] / source / graphics / quartz / quartzloader.cpp
diff --git a/source/graphics/quartz/quartzloader.cpp b/source/graphics/quartz/quartzloader.cpp
new file mode 100644 (file)
index 0000000..23f7abb
--- /dev/null
@@ -0,0 +1,146 @@
+#include <algorithm>
+#include <CoreGraphics/CGColorSpace.h>
+#include <CoreGraphics/CGImage.h>
+#include <ImageIO/CGImageSource.h>
+// Avoid messing up sigc++ headers
+#undef nil
+#include "quartzloader.h"
+
+using namespace std;
+
+namespace {
+
+size_t get_bytes(void *info, void *buffer, size_t count)
+{
+       Msp::IO::Seekable *io = reinterpret_cast<Msp::IO::Seekable *>(info);
+       char *cbuf = reinterpret_cast<char *>(buffer);
+       return io->read(cbuf, count);
+}
+
+off_t skip_forward(void *info, off_t count)
+{
+       Msp::IO::Seekable *io = reinterpret_cast<Msp::IO::Seekable *>(info);
+
+       off_t i = 0;
+       char buf[1024];
+       while(i<count)
+               i += io->read(buf, min<unsigned>(sizeof(buf), count-i));
+
+       return i;
+}
+
+void rewind(void *info)
+{
+       Msp::IO::Seekable *io = reinterpret_cast<Msp::IO::Seekable *>(info);
+       io->seek(0, Msp::IO::S_BEG);
+}
+
+CGDataProviderSequentialCallbacks callbacks =
+{
+       0,
+       &get_bytes,
+       &skip_forward,
+       &rewind,
+       0
+};
+
+} // namespace
+
+
+namespace Msp {
+namespace Graphics {
+
+struct QuartzLoader::Private
+{
+       CGDataProviderRef provider;
+       CGImageSourceRef source;
+};
+
+
+ImageLoader::Register<QuartzLoader> QuartzLoader::reg;
+
+QuartzLoader::QuartzLoader(IO::Seekable &io):
+       priv(new Private)
+{
+       priv->provider = CGDataProviderCreateSequential(&io, &callbacks);
+       priv->source = CGImageSourceCreateWithDataProvider(priv->provider, 0);
+}
+
+QuartzLoader::~QuartzLoader()
+{
+       CFRelease(priv->source);
+       CFRelease(priv->provider);
+       delete priv;
+}
+
+bool QuartzLoader::detect(const string &sig)
+{
+       CGImageSourceRef source = CGImageSourceCreateIncremental(0);
+       CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(sig.data()), sig.size());
+       CGImageSourceUpdateData(source, data, false);
+       CGImageSourceStatus status = CGImageSourceGetStatus(source);
+       CFRelease(data);
+       CFRelease(source);
+
+       return status==kCGImageStatusIncomplete;
+}
+
+void QuartzLoader::load(Image::Data &data)
+{
+       CGImageRef image = CGImageSourceCreateImageAtIndex(priv->source, 0, 0);
+
+       try
+       {
+               data.width = CGImageGetWidth(image);
+               data.height = CGImageGetHeight(image);
+
+               CGColorSpaceRef color = CGImageGetColorSpace(image);
+               CGColorSpaceModel model = CGColorSpaceGetModel(color);
+               CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image);
+               if(model==kCGColorSpaceModelRGB)
+               {
+                       if(alpha==kCGImageAlphaLast)
+                               data.fmt = RGBA;
+                       else if(alpha==kCGImageAlphaNone || alpha==kCGImageAlphaNoneSkipFirst || alpha==kCGImageAlphaNoneSkipLast)
+                               data.fmt = RGB;
+                       else
+                               throw unsupported_image_format("unknown alpha mode");
+               }
+               else
+                       throw unsupported_image_format("unknown colorspace");
+
+               unsigned bytes_per_pixel = (CGImageGetBitsPerPixel(image)+7)/8;
+
+               CGDataProviderRef dp = CGImageGetDataProvider(image);
+               CFDataRef image_data = CGDataProviderCopyData(dp);
+               data.data = new char[data.width*data.height*bytes_per_pixel];
+               CFDataGetBytes(image_data, CFRangeMake(0, CFDataGetLength(image_data)), reinterpret_cast<UInt8 *>(data.data));
+               CFRelease(image_data);
+
+               CFRelease(image);
+
+               if(alpha==kCGImageAlphaNoneSkipFirst || alpha==kCGImageAlphaNoneSkipLast)
+               {
+                       const char *src = data.data;
+                       if(alpha==kCGImageAlphaNoneSkipFirst)
+                               ++src;
+                       char *dest = data.data;
+                       for(unsigned y=0; y<data.height; ++y)
+                               for(unsigned x=0; x<data.width; ++x)
+                               {
+                                       for(unsigned i=0; i<3; ++i)
+                                               dest[i] = src[i];
+                                       dest += 3;
+                                       src += 4;
+                               }
+               }
+       }
+       catch(...)
+       {
+               CFRelease(image);
+               throw;
+       }
+}
+
+} // namespace Graphics
+} // namespace Msp