use CoreImage for decoding/encoding images on osx/ios
This commit is contained in:
15
configure
vendored
15
configure
vendored
@@ -6,6 +6,10 @@ shopt -s expand_aliases
|
||||
|
||||
CONFIG_FILE=${1:-config.gypi}
|
||||
|
||||
if [ `uname -s` = 'Darwin' ]; then
|
||||
MASON_PLATFORM=${MASON_PLATFORM:-osx}
|
||||
fi
|
||||
|
||||
function finish {
|
||||
>&2 echo -en "\033[0m";
|
||||
}
|
||||
@@ -34,11 +38,16 @@ if [[ ! -d ~/.mason ]]; then
|
||||
fi
|
||||
alias mason='~/.mason/mason'
|
||||
|
||||
|
||||
case $MASON_PLATFORM in
|
||||
case ${MASON_PLATFORM} in
|
||||
'ios')
|
||||
SQLITE_VERSION=system
|
||||
LIBPNG_VERSION=1.6.13
|
||||
LIBUV_VERSION=0.10.28
|
||||
ZLIB_VERSION=system
|
||||
BOOST_VERSION=system
|
||||
;;
|
||||
'osx')
|
||||
GLFW_VERSION=a21f2377
|
||||
SQLITE_VERSION=system
|
||||
LIBUV_VERSION=0.10.28
|
||||
ZLIB_VERSION=system
|
||||
BOOST_VERSION=system
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
],
|
||||
'variables': {
|
||||
'cflags_cc': [
|
||||
'<@(png_cflags)',
|
||||
'<@(uv_cflags)',
|
||||
'<@(sqlite3_cflags)',
|
||||
'<@(zlib_cflags)',
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
'../platform/darwin/log_nslog.mm',
|
||||
'../platform/darwin/string_nsstring.mm',
|
||||
'../platform/darwin/http_request_baton_cocoa.mm',
|
||||
'../platform/darwin/image.mm',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
@@ -44,7 +45,13 @@
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
]
|
||||
],
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
'-framework ImageIO',
|
||||
'-framework MobileCoreServices',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
'hard_dependency': 1,
|
||||
'variables': {
|
||||
'cflags_cc': [
|
||||
'<@(png_cflags)',
|
||||
'<@(uv_cflags)',
|
||||
'<@(curl_cflags)',
|
||||
],
|
||||
@@ -14,6 +15,7 @@
|
||||
'<@(uv_cflags)',
|
||||
],
|
||||
'ldflags': [
|
||||
'<@(png_ldflags)',
|
||||
'<@(uv_ldflags)',
|
||||
'<@(curl_ldflags)',
|
||||
],
|
||||
@@ -23,6 +25,7 @@
|
||||
'../platform/default/log_stderr.cpp',
|
||||
'../platform/default/string_stdlib.cpp',
|
||||
'../platform/default/http_request_baton_curl.cpp',
|
||||
'../platform/default/image.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
'../platform/darwin/log_nslog.mm',
|
||||
'../platform/darwin/string_nsstring.mm',
|
||||
'../platform/darwin/http_request_baton_cocoa.mm',
|
||||
'../platform/darwin/image.mm',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
@@ -20,7 +21,13 @@
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
]
|
||||
],
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
'-framework ImageIO',
|
||||
'-framework CoreServices',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
void createContext();
|
||||
|
||||
void resize(uint16_t width, uint16_t height, float pixelRatio);
|
||||
const std::unique_ptr<uint32_t[]> readPixels();
|
||||
std::unique_ptr<uint32_t[]> readPixels();
|
||||
|
||||
void notify();
|
||||
void notify_map_change(MapChange change, timestamp delay = 0);
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
std::string compress_png(int width, int height, void *rgba, bool flip = false);
|
||||
std::string compress_png(int width, int height, void *rgba);
|
||||
|
||||
|
||||
class Image {
|
||||
public:
|
||||
Image(const std::string &img, bool flip = false);
|
||||
Image(const std::string &img);
|
||||
|
||||
inline const char *getData() const { return img.get(); }
|
||||
inline uint32_t getWidth() const { return width; }
|
||||
|
||||
125
platform/darwin/image.mm
Normal file
125
platform/darwin/image.mm
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <mbgl/util/image.hpp>
|
||||
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#else
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
std::string compress_png(int width, int height, void *rgba) {
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rgba, width * height * 4, NULL);
|
||||
if (!provider) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (!color_space) {
|
||||
CGDataProviderRelease(provider);
|
||||
return "";
|
||||
}
|
||||
|
||||
CGImageRef image = CGImageCreate(width, height, 8, 32, 4 * width, color_space,
|
||||
kCGBitmapByteOrderDefault | kCGImageAlphaLast, provider, NULL, false,
|
||||
kCGRenderingIntentDefault);
|
||||
if (!image) {
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGDataProviderRelease(provider);
|
||||
return "";
|
||||
}
|
||||
|
||||
CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
if (!data) {
|
||||
CGImageRelease(image);
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGDataProviderRelease(provider);
|
||||
return "";
|
||||
}
|
||||
|
||||
CGImageDestinationRef image_destination = CGImageDestinationCreateWithData(data, kUTTypePNG, 1, NULL);
|
||||
if (!image_destination) {
|
||||
CFRelease(data);
|
||||
CGImageRelease(image);
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGDataProviderRelease(provider);
|
||||
return "";
|
||||
}
|
||||
|
||||
CGImageDestinationAddImage(image_destination, image, NULL);
|
||||
CGImageDestinationFinalize(image_destination);
|
||||
|
||||
const std::string result {
|
||||
reinterpret_cast<const char *>(CFDataGetBytePtr(data)),
|
||||
static_cast<size_t>(CFDataGetLength(data))
|
||||
};
|
||||
|
||||
CFRelease(image_destination);
|
||||
CFRelease(data);
|
||||
CGImageRelease(image);
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGDataProviderRelease(provider);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Image::Image(const std::string &source_data) {
|
||||
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const unsigned char *>(source_data.data()), source_data.size(), kCFAllocatorNull);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGImageSourceRef image_source = CGImageSourceCreateWithData(data, NULL);
|
||||
if (!image_source) {
|
||||
CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
|
||||
if (!image) {
|
||||
CFRelease(image_source);
|
||||
CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (!color_space) {
|
||||
CGImageRelease(image);
|
||||
CFRelease(image_source);
|
||||
CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGRect rect = {{ 0, 0 }, { static_cast<CGFloat>(width), static_cast<CGFloat>(height) }};
|
||||
|
||||
img = ::std::unique_ptr<char[]>(new char[width * height * 4]());
|
||||
CGContextRef context = CGBitmapContextCreate(img.get(), width, height, 8, width * 4,
|
||||
color_space, kCGImageAlphaPremultipliedLast);
|
||||
if (!context) {
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGImageRelease(image);
|
||||
CFRelease(image_source);
|
||||
CFRelease(data);
|
||||
width = 0;
|
||||
height = 0;
|
||||
img.release();
|
||||
return;
|
||||
}
|
||||
|
||||
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
||||
CGContextDrawImage(context, rect, image);
|
||||
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(color_space);
|
||||
CGImageRelease(image);
|
||||
CFRelease(image_source);
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -181,11 +181,11 @@ void HeadlessView::resize(uint16_t width, uint16_t height, float pixelRatio) {
|
||||
make_inactive();
|
||||
}
|
||||
|
||||
const std::unique_ptr<uint32_t[]> HeadlessView::readPixels() {
|
||||
std::unique_ptr<uint32_t[]> HeadlessView::readPixels() {
|
||||
const unsigned int w = width_ * pixelRatio_;
|
||||
const unsigned int h = height_ * pixelRatio_;
|
||||
|
||||
std::unique_ptr<uint32_t[]> pixels(new uint32_t[w * h]);
|
||||
auto pixels = std::unique_ptr<uint32_t[]>(new uint32_t[w * h]);
|
||||
|
||||
make_active();
|
||||
glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
@@ -11,7 +10,7 @@
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
std::string compress_png(int width, int height, void *rgba, bool flip) {
|
||||
std::string compress_png(int width, int height, void *rgba) {
|
||||
png_voidp error_ptr = 0;
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, NULL, NULL);
|
||||
if (!png_ptr) {
|
||||
@@ -48,7 +47,7 @@ std::string compress_png(int width, int height, void *rgba, bool flip) {
|
||||
} pointers(height);
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
pointers.rows[flip ? height - 1 - i : i] = (png_bytep)((png_bytep)rgba + width * 4 * i);
|
||||
pointers.rows[i] = (png_bytep)((png_bytep)rgba + width * 4 * i);
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, pointers.rows);
|
||||
@@ -87,7 +86,7 @@ void warningHandler(png_structp, png_const_charp error_msg) {
|
||||
fprintf(stderr, "PNG: %s\n", error_msg);
|
||||
}
|
||||
|
||||
Image::Image(const std::string &data, bool flip) {
|
||||
Image::Image(const std::string &data) {
|
||||
Buffer buffer(data);
|
||||
|
||||
if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) {
|
||||
@@ -136,6 +135,8 @@ Image::Image(const std::string &data, bool flip) {
|
||||
if (png_get_gAMA(png, info, &gamma))
|
||||
png_set_gamma(png, 2.2, gamma);
|
||||
|
||||
png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2);
|
||||
|
||||
png_read_update_info(png, info);
|
||||
|
||||
png_size_t rowbytes = png_get_rowbytes(png, info);
|
||||
@@ -152,7 +153,7 @@ Image::Image(const std::string &data, bool flip) {
|
||||
png_bytep *rows = nullptr;
|
||||
} pointers(height);
|
||||
for (unsigned i = 0; i < height; ++i) {
|
||||
pointers.rows[flip ? height - 1 - i : i] = (png_bytep)(surface + (i * rowbytes));
|
||||
pointers.rows[i] = (png_bytep)(surface + (i * rowbytes));
|
||||
}
|
||||
|
||||
// Read image data
|
||||
@@ -72,18 +72,7 @@ void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, con
|
||||
dst += dst_y * dst_stride + dst_x;
|
||||
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
const uint8_t *s = reinterpret_cast<const uint8_t *>(src + x);
|
||||
uint8_t *d = reinterpret_cast<uint8_t *>(dst + x);
|
||||
|
||||
// Premultiply the bitmap.
|
||||
// Note: We don't need to clamp the component values to 0..255, since
|
||||
// the source value is already 0..255 and the operation means they will
|
||||
// stay within the range of 0..255 and won't overflow.
|
||||
const uint8_t a = s[3];
|
||||
d[0] = s[0] * a / 255;
|
||||
d[1] = s[1] * a / 255;
|
||||
d[2] = s[2] * a / 255;
|
||||
d[3] = a;
|
||||
dst[x] = src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,13 +92,25 @@ TEST_P(HeadlessTest, render) {
|
||||
map.setLonLatZoom(longitude, latitude, zoom);
|
||||
map.setBearing(bearing);
|
||||
|
||||
|
||||
// Run the loop. It will terminate when we don't have any further listeners.
|
||||
map.run();
|
||||
|
||||
const unsigned int w = width * pixelRatio;
|
||||
const unsigned int h = height * pixelRatio;
|
||||
|
||||
const std::string image = util::compress_png(w, h, view.readPixels().get(), true);
|
||||
auto pixels = view.readPixels();
|
||||
|
||||
const int stride = w * 4;
|
||||
auto tmp = std::unique_ptr<char[]>(new char[stride]());
|
||||
char *rgba = reinterpret_cast<char *>(pixels.get());
|
||||
for (int i = 0, j = height - 2; i < j; i++, j--) {
|
||||
memcpy(tmp.get(), rgba + i * stride, stride);
|
||||
memcpy(rgba + i * stride, rgba + j * stride, stride);
|
||||
memcpy(rgba + j * stride, tmp.get(), stride);
|
||||
}
|
||||
|
||||
const std::string image = util::compress_png(w, h, pixels.get());
|
||||
util::write_file(actual_image, image);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user