diff options
Diffstat (limited to 'platform/darwin')
-rw-r--r-- | platform/darwin/mbgl/util/image+MGLAdditions.hpp | 12 | ||||
-rw-r--r-- | platform/darwin/src/image.mm | 173 |
2 files changed, 89 insertions, 96 deletions
diff --git a/platform/darwin/mbgl/util/image+MGLAdditions.hpp b/platform/darwin/mbgl/util/image+MGLAdditions.hpp new file mode 100644 index 0000000000..c738b4523d --- /dev/null +++ b/platform/darwin/mbgl/util/image+MGLAdditions.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include <mbgl/util/image.hpp> + +#include <CoreGraphics/CGImage.h> + +// Creates a CGImage from a PremultipliedImage, taking over the memory ownership. +CGImageRef CGImageFromMGLPremultipliedImage(mbgl::PremultipliedImage&&); + +// Creates a PremultipliedImage by copying the pixels of the CGImage. +// Does not alter the retain count of the supplied CGImage. +mbgl::PremultipliedImage MGLPremultipliedImageFromCGImage(CGImageRef); diff --git a/platform/darwin/src/image.mm b/platform/darwin/src/image.mm index 3a707d4a36..57b680fbdb 100644 --- a/platform/darwin/src/image.mm +++ b/platform/darwin/src/image.mm @@ -1,126 +1,107 @@ -#include <mbgl/util/image.hpp> +#include <mbgl/util/image+MGLAdditions.hpp> #import <ImageIO/ImageIO.h> -#if TARGET_OS_IPHONE -#import <MobileCoreServices/MobileCoreServices.h> -#else -#import <CoreServices/CoreServices.h> -#endif - -namespace mbgl { - -std::string encodePNG(const PremultipliedImage& src) { - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, src.data.get(), src.bytes(), NULL); +namespace { + +template <typename T, typename S, void (*Releaser)(S)> +struct CFHandle { + CFHandle(T t_): t(t_) {} + ~CFHandle() { Releaser(t); } + T operator*() { return t; } + operator bool() { return t; } +private: + T t; +}; + +} // namespace + +using CGImageHandle = CFHandle<CGImageRef, CGImageRef, CGImageRelease>; +using CFDataHandle = CFHandle<CFDataRef, CFTypeRef, CFRelease>; +using CGImageSourceHandle = CFHandle<CGImageSourceRef, CFTypeRef, CFRelease>; +using CGDataProviderHandle = CFHandle<CGDataProviderRef, CGDataProviderRef, CGDataProviderRelease>; +using CGColorSpaceHandle = CFHandle<CGColorSpaceRef, CGColorSpaceRef, CGColorSpaceRelease>; +using CGContextHandle = CFHandle<CGContextRef, CGContextRef, CGContextRelease>; + +CGImageRef CGImageFromMGLPremultipliedImage(mbgl::PremultipliedImage&& src) { + // We're converting the PremultipliedImage's backing store to a CGDataProvider, and are taking + // over ownership of the memory. + CGDataProviderHandle provider(CGDataProviderCreateWithData( + NULL, src.data.get(), src.bytes(), [](void*, const void* data, size_t) { + delete[] reinterpret_cast<const decltype(src.data)::element_type*>(data); + })); if (!provider) { - return ""; + return nil; } - CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); - if (!color_space) { - CGDataProviderRelease(provider); - return ""; - } + // If we successfully created the provider, it will take over management of the memory segment. + src.data.release(); - CGImageRef image = - CGImageCreate(src.size.width, src.size.height, 8, 32, 4 * src.size.width, color_space, - kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, provider, NULL, - false, kCGRenderingIntentDefault); - if (!image) { - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); - return ""; + CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB()); + if (!colorSpace) { + return nil; } - CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); - if (!data) { - CGImageRelease(image); - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); - return ""; - } + constexpr const size_t bitsPerComponent = 8; + constexpr const size_t bytesPerPixel = 4; + constexpr const size_t bitsPerPixel = bitsPerComponent * bytesPerPixel; + const size_t bytesPerRow = bytesPerPixel * src.size.width; - CGImageDestinationRef image_destination = CGImageDestinationCreateWithData(data, kUTTypePNG, 1, NULL); - if (!image_destination) { - CFRelease(data); - CGImageRelease(image); - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); - return ""; + return CGImageCreate(src.size.width, src.size.height, bitsPerComponent, bitsPerPixel, + bytesPerRow, *colorSpace, + kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, *provider, + NULL, false, kCGRenderingIntentDefault); +} + +mbgl::PremultipliedImage MGLPremultipliedImageFromCGImage(CGImageRef src) { + const size_t width = CGImageGetWidth(src); + const size_t height = CGImageGetHeight(src); + + mbgl::PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); + + CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB()); + if (!colorSpace) { + throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed"); } - CGImageDestinationAddImage(image_destination, image, NULL); - CGImageDestinationFinalize(image_destination); + constexpr const size_t bitsPerComponent = 8; + constexpr const size_t bytesPerPixel = 4; + const size_t bytesPerRow = bytesPerPixel * width; - const std::string result { - reinterpret_cast<const char *>(CFDataGetBytePtr(data)), - static_cast<size_t>(CFDataGetLength(data)) - }; + CGContextHandle context(CGBitmapContextCreate( + image.data.get(), width, height, bitsPerComponent, bytesPerRow, *colorSpace, + kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast)); + if (!context) { + throw std::runtime_error("CGBitmapContextCreate failed"); + } - CFRelease(image_destination); - CFRelease(data); - CGImageRelease(image); - CGColorSpaceRelease(color_space); - CGDataProviderRelease(provider); + CGContextSetBlendMode(*context, kCGBlendModeCopy); + CGContextDrawImage(*context, CGRectMake(0, 0, width, height), src); - return result; + return image; } -PremultipliedImage decodeImage(const std::string &source_data) { - CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const unsigned char *>(source_data.data()), source_data.size(), kCFAllocatorNull); +namespace mbgl { + +PremultipliedImage decodeImage(const std::string& source) { + CFDataHandle data(CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, reinterpret_cast<const unsigned char*>(source.data()), source.size(), + kCFAllocatorNull)); if (!data) { throw std::runtime_error("CFDataCreateWithBytesNoCopy failed"); } - CGImageSourceRef image_source = CGImageSourceCreateWithData(data, NULL); - if (!image_source) { - CFRelease(data); + CGImageSourceHandle imageSource(CGImageSourceCreateWithData(*data, NULL)); + if (!imageSource) { throw std::runtime_error("CGImageSourceCreateWithData failed"); } - CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL); + CGImageHandle image(CGImageSourceCreateImageAtIndex(*imageSource, 0, NULL)); if (!image) { - CFRelease(image_source); - CFRelease(data); throw std::runtime_error("CGImageSourceCreateImageAtIndex failed"); } - CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); - if (!color_space) { - CGImageRelease(image); - CFRelease(image_source); - CFRelease(data); - throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed"); - } - - PremultipliedImage result({ static_cast<uint32_t>(CGImageGetWidth(image)), - static_cast<uint32_t>(CGImageGetHeight(image)) }); - - CGContextRef context = - CGBitmapContextCreate(result.data.get(), result.size.width, result.size.height, 8, - result.stride(), color_space, kCGImageAlphaPremultipliedLast); - if (!context) { - CGColorSpaceRelease(color_space); - CGImageRelease(image); - CFRelease(image_source); - CFRelease(data); - throw std::runtime_error("CGBitmapContextCreate failed"); - } - - CGContextSetBlendMode(context, kCGBlendModeCopy); - - CGRect rect = { { 0, 0 }, - { static_cast<CGFloat>(result.size.width), - static_cast<CGFloat>(result.size.height) } }; - CGContextDrawImage(context, rect, image); - - CGContextRelease(context); - CGColorSpaceRelease(color_space); - CGImageRelease(image); - CFRelease(image_source); - CFRelease(data); - - return result; + return MGLPremultipliedImageFromCGImage(*image); } -} +} // namespace mbgl |