--- /dev/null
+#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
--- /dev/null
+#ifndef MSP_GRAPHICS_QUARTZLOADER_H_
+#define MSP_GRAPHICS_QUARTZLOADER_H_
+
+#include <msp/graphics/imageloader.h>
+
+namespace Msp {
+namespace Graphics {
+
+class QuartzLoader: public ImageLoader
+{
+private:
+ struct Private;
+
+ Private *priv;
+
+ static Register<QuartzLoader> reg;
+
+public:
+ QuartzLoader(IO::Seekable &);
+ virtual ~QuartzLoader();
+
+ /* Experimentally found value, works for png and jpeg at least. 8 bytes
+ is not enough for Quartz to recognize png. */
+ static unsigned get_signature_size() { return 12; }
+ static bool detect(const std::string &);
+
+ virtual void load(Image::Data &);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif