From 925757a29754bac7980b905b830d851811c50ba6 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 8 Oct 2013 13:39:23 +0300 Subject: [PATCH] Support for using Quartz to load images on OS X --- Build | 19 +++ source/graphics/quartz/quartzloader.cpp | 146 ++++++++++++++++++++++++ source/graphics/quartz/quartzloader.h | 33 ++++++ 3 files changed, 198 insertions(+) create mode 100644 source/graphics/quartz/quartzloader.cpp create mode 100644 source/graphics/quartz/quartzloader.h diff --git a/Build b/Build index 4862e3d..b96da4d 100644 --- 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 index 0000000..23f7abb --- /dev/null +++ b/source/graphics/quartz/quartzloader.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +// 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(info); + char *cbuf = reinterpret_cast(buffer); + return io->read(cbuf, count); +} + +off_t skip_forward(void *info, off_t count) +{ + Msp::IO::Seekable *io = reinterpret_cast(info); + + off_t i = 0; + char buf[1024]; + while(iread(buf, min(sizeof(buf), count-i)); + + return i; +} + +void rewind(void *info) +{ + Msp::IO::Seekable *io = reinterpret_cast(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::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(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(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 + +namespace Msp { +namespace Graphics { + +class QuartzLoader: public ImageLoader +{ +private: + struct Private; + + Private *priv; + + static Register 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 -- 2.45.2