]> git.tdb.fi Git - libs/gui.git/commitdiff
Support for using Quartz to load images on OS X
authorMikko Rasa <tdb@tdb.fi>
Tue, 8 Oct 2013 10:39:23 +0000 (13:39 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 8 Oct 2013 10:39:23 +0000 (13:39 +0300)
Build
source/graphics/quartz/quartzloader.cpp [new file with mode: 0644]
source/graphics/quartz/quartzloader.h [new file with mode: 0644]

diff --git a/Build b/Build
index 4862e3d19589073928578698ab8dc4f0a504dcb6..b96da4d66d3388b53b0c062a25fff18b33af1e1b 100644 (file)
--- a/Build
+++ b/Build
@@ -43,6 +43,21 @@ package "mspgui"
                require "libpng";
        };
 
+       if_arch "darwin"
+       {
+               feature "quartz" "Include Quartz support for loading image files"
+               {
+                       default "yes";
+               };
+               if_feature "quartz"
+               {
+                       build_info
+                       {
+                               library "ApplicationServices.framework";
+                       };
+               };
+       };
+
        feature "opengl" "Include support for OpenGL contexts"
        {
                default "yes";
@@ -83,6 +98,10 @@ package "mspgui"
                };
                if_arch "darwin"
                {
+                       if_feature "quartz"
+                       {
+                               source "source/graphics/quartz";
+                       };
                        overlay "cocoa";
                        if_feature "opengl"
                        {
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
diff --git a/source/graphics/quartz/quartzloader.h b/source/graphics/quartz/quartzloader.h
new file mode 100644 (file)
index 0000000..d3bef7b
--- /dev/null
@@ -0,0 +1,33 @@
+#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