This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[macos,ios] don't roundtrip through encodePNG when converting images
- Loading branch information
Showing
9 changed files
with
129 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,31 @@ | ||
#import "UIImage+MGLAdditions.h" | ||
|
||
#include <mbgl/util/image+MGLAdditions.hpp> | ||
|
||
@implementation UIImage (MGLAdditions) | ||
|
||
- (nullable instancetype)initWithMGLSpriteImage:(const mbgl::SpriteImage *)spriteImage | ||
{ | ||
std::string png = encodePNG(spriteImage->image); | ||
NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()]; | ||
if (self = [self initWithData:data scale:spriteImage->pixelRatio]) | ||
CGImageRef image = CGImageFromMGLPremultipliedImage(spriteImage->image.clone()); | ||
if (!image) { | ||
return nil; | ||
} | ||
|
||
if (self = [self initWithCGImage:image scale:spriteImage->pixelRatio orientation:UIImageOrientationUp]) | ||
{ | ||
if (spriteImage->sdf) | ||
{ | ||
self = [self imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; | ||
} | ||
} | ||
CGImageRelease(image); | ||
return self; | ||
} | ||
|
||
- (std::unique_ptr<mbgl::SpriteImage>)mgl_spriteImage | ||
{ | ||
CGImageRef cgImage = self.CGImage; | ||
size_t width = CGImageGetWidth(cgImage); | ||
size_t height = CGImageGetHeight(cgImage); | ||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | ||
mbgl::PremultipliedImage cPremultipliedImage({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); | ||
size_t bytesPerPixel = 4; | ||
size_t bytesPerRow = bytesPerPixel * width; | ||
size_t bitsPerComponent = 8; | ||
|
||
CGContextRef context = CGBitmapContextCreate(cPremultipliedImage.data.get(), | ||
width, height, bitsPerComponent, bytesPerRow, | ||
colorSpace, kCGImageAlphaPremultipliedLast); | ||
|
||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); | ||
CGContextRelease(context); | ||
CGColorSpaceRelease(colorSpace); | ||
|
||
- (std::unique_ptr<mbgl::SpriteImage>)mgl_spriteImage { | ||
BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate; | ||
return std::make_unique<mbgl::SpriteImage>(std::move(cPremultipliedImage), float(self.scale), isTemplate); | ||
return std::make_unique<mbgl::SpriteImage>(MGLPremultipliedImageFromCGImage(self.CGImage), | ||
float(self.scale), isTemplate); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters