Merge pull request #527 from mapbox/platform-image

Use platform-specific routines for decoding/encoding images
This commit is contained in:
Konstantin Käfer
2014-10-31 17:06:32 -04:00
20 changed files with 224 additions and 59 deletions

10
configure vendored
View File

@@ -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";
}
@@ -18,11 +22,9 @@ 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
@@ -30,7 +32,7 @@ case $MASON_PLATFORM in
*)
GLFW_VERSION=a21f2377
SQLITE_VERSION=system
LIBPNG_VERSION=system
LIBPNG_VERSION=1.6.13
LIBCURL_VERSION=system
LIBUV_VERSION=0.10.28
ZLIB_VERSION=system

View File

@@ -12,6 +12,10 @@
'sources': [
'gtest-all.cc'
],
'link_settings': {
'xcode_settings': { 'OTHER_LDFLAGS': [ '-lpthread' ] },
'ldflags': [ '-lpthread' ],
},
'direct_dependent_settings': {
'include_dirs': [
'.',

View File

@@ -22,7 +22,6 @@
'conditions': [
['OS == "linux"', {
'other_ldflags': [
'-L<(boost_root)/lib',
'<@(glfw3_static_libs)',
'<@(glfw3_ldflags)',
]

View File

@@ -10,7 +10,6 @@
],
'variables': {
'cflags_cc': [
'<@(png_cflags)',
'<@(uv_cflags)',
'<@(sqlite3_cflags)',
'<@(zlib_cflags)',

View File

@@ -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',
],
},
},
},
],

View File

@@ -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,10 +25,16 @@
'../platform/default/log_stderr.cpp',
'../platform/default/string_stdlib.cpp',
'../platform/default/http_request_baton_curl.cpp',
'../platform/default/image.cpp',
],
'include_dirs': [
'../include',
],
'link_settings': {
'libraries': [
'<@(png_static_libs)',
],
},
'conditions': [
['OS == "mac"', {
'xcode_settings': {
@@ -34,7 +42,6 @@
'OTHER_CFLAGS': [ '<@(cflags)' ],
}
}, {
'ldflags': [ '<@(ldflags)' ],
'cflags_cc': [ '<@(cflags_cc)' ],
'cflags': [ '<@(cflags)' ],
}]

View File

@@ -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',
],
},
},
},
],

View File

@@ -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);

View File

@@ -2,31 +2,29 @@
#define MBGL_UTIL_IMAGE
#include <string>
#include <cstring>
#include <stdexcept>
#include <memory>
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();
Image(const std::string &img);
inline const char *getData() const { return img; }
inline const char *getData() const { return img.get(); }
inline uint32_t getWidth() const { return width; }
inline uint32_t getHeight() const { return height; }
inline operator bool() const { return img && width && height; }
private:
// loaded image dimensions
uint32_t width = 0, height = 0;
// the raw image data
char *img = nullptr;
std::unique_ptr<char[]> img;
};

View File

@@ -22,7 +22,7 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) {
result.append(pos, brace);
pos = brace;
if (pos != end) {
for (brace++; brace != end && std::isalnum(*brace); brace++);
for (brace++; brace != end && (std::isalnum(*brace) || *brace == '_'); brace++);
if (brace != end && *brace == '}') {
result.append(lookup({ pos + 1, brace }));
pos = brace + 1;

View File

@@ -22,13 +22,24 @@
'<@(glfw3_cflags)',
'-I<(boost_root)/include',
],
'libraries': [
'<@(png_ldflags)',
'<@(sqlite3_ldflags)',
'<@(glfw3_static_libs)',
'<@(glfw3_ldflags)',
'<@(curl_ldflags)',
'<@(zlib_ldflags)',
'variables': {
'ldflags': [
'<@(png_ldflags)',
'<@(sqlite3_ldflags)',
'<@(glfw3_static_libs)',
'<@(glfw3_ldflags)',
'<@(curl_ldflags)',
'<@(zlib_ldflags)',
],
},
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
}
}, {
'libraries': [ '<@(ldflags)' ],
}]
],
'dependencies': [
'../mapboxgl.gyp:mbgl-standalone',

View File

@@ -40,8 +40,7 @@
'<@(sqlite3_ldflags)',
'<@(glfw3_static_libs)',
'<@(glfw3_ldflags)',
'<@(curl_ldflags)',
'<@(png_ldflags)'
'<@(zlib_ldflags)',
]
},
'conditions': [

125
platform/darwin/image.mm Normal file
View 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);
}
}
}

View File

@@ -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());

View File

@@ -4,9 +4,14 @@
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <cstring>
std::string mbgl::util::compress_png(int width, int height, void *rgba, bool flip) {
namespace mbgl {
namespace util {
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) {
@@ -43,7 +48,7 @@ std::string mbgl::util::compress_png(int width, int height, void *rgba, bool fli
} 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);
@@ -54,9 +59,6 @@ std::string mbgl::util::compress_png(int width, int height, void *rgba, bool fli
}
using namespace mbgl::util;
struct Buffer {
Buffer(const std::string& data_)
: data(data_.data()), length(data_.size()) {}
@@ -72,7 +74,7 @@ void readCallback(png_structp png, png_bytep data, png_size_t length) {
if (reader->pos + length > reader->length) {
png_error(png, "Read Error");
} else {
memcpy(data, reader->data + reader->pos, length);
std::memcpy(data, reader->data + reader->pos, length);
reader->pos += length;
}
}
@@ -85,7 +87,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)) {
@@ -134,14 +136,16 @@ 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);
assert(width * 4 == rowbytes);
img = static_cast<char*>(::operator new(width * height * 4));
img = ::std::unique_ptr<char[]>(new char[width * height * 4]());
char *surface = img;
char *surface = img.get();
assert(surface);
struct ptrs {
@@ -150,7 +154,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
@@ -163,14 +167,12 @@ Image::Image(const std::string &data, bool flip) {
fprintf(stderr, "loading PNG failed: %s\n", e.what());
png_destroy_read_struct(&png, &info, nullptr);
if (img) {
::operator delete(img);
img = nullptr;
img.reset();
}
width = 0;
height = 0;
}
}
Image::~Image() {
::operator delete(img),img = nullptr;
}
}

View File

@@ -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];
}
}
}
@@ -158,6 +147,7 @@ void SpriteAtlas::allocate() {
void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) {
if (!sprite->raster) return;
const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
if (!src_img) return;
allocate();
uint32_t *dst_img = reinterpret_cast<uint32_t *>(data);

View File

@@ -102,6 +102,9 @@ bool Sprite::isLoaded() const {
void Sprite::parseImage() {
raster = std::make_unique<util::Image>(image);
if (!*raster) {
raster.reset();
}
image.clear();
loadedImage = true;
}

View File

@@ -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 - 1; 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);
}
}

View File

@@ -8,7 +8,7 @@
'<@(uv_ldflags)',
'<@(sqlite3_ldflags)',
'<@(curl_ldflags)',
'<@(png_ldflags)'
'<@(png_ldflags)',
],
},
'targets': [