diff --git a/README.md b/README.md index 998618f7b..3c8fb2fd3 100644 --- a/README.md +++ b/README.md @@ -93,17 +93,14 @@ Once you're done installing the build dependencies, you can get started by runni ./configure -which downloads all other dependencies that we need to build manually with [Mason](https://github.com/mapbox/mason). There's a good chance that there already are binary files and that you don't need to compile anything. - -Then, you can then proceed to build the library like: - - make mbgl - -Or proceed to building the debug application with: +Then, you can then proceed to build the library: + git submodule update --init make linux -Be sure to setup an access token per below before running `build/Release/mapbox-gl`. +Set an access token as described below, and then run: + + make run-linux # Troubleshooting diff --git a/configure b/configure index 8c8a07c1d..b0a5e5332 100755 --- a/configure +++ b/configure @@ -19,21 +19,22 @@ trap finish EXIT case ${MASON_PLATFORM} in 'ios') - SQLITE_VERSION=system - LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system - BOOST_VERSION=system - ;; + SQLITE_VERSION=system + LIBUV_VERSION=0.10.28 + ZLIB_VERSION=system + BOOST_VERSION=system + ;; *) - GLFW_VERSION=a21f2377 - SQLITE_VERSION=system - LIBPNG_VERSION=1.6.13 - LIBCURL_VERSION=system - LIBUV_VERSION=0.10.28 - ZLIB_VERSION=system - BOOST_VERSION=system - NUNICODE_VERSION=1.4 - ;; + GLFW_VERSION=a21f2377 + SQLITE_VERSION=system + LIBPNG_VERSION=1.6.13 + LIBJPEG_VERSION=v8d + LIBCURL_VERSION=system + LIBUV_VERSION=0.10.28 + ZLIB_VERSION=system + BOOST_VERSION=system + NUNICODE_VERSION=1.4 + ;; esac function abort { >&2 echo -e "\033[1m\033[31m$1\033[0m"; exit 1; } @@ -91,6 +92,13 @@ if [ ! -z ${LIBPNG_VERSION} ]; then CONFIG+=" 'png_ldflags': $(quote_flags $(mason ldflags libpng ${LIBPNG_VERSION})),"$LN fi +if [ ! -z ${LIBJPEG_VERSION} ]; then + mason install jpeg ${LIBJPEG_VERSION} + CONFIG+=" 'jpeg_static_libs': $(quote_flags $(mason static_libs jpeg ${LIBJPEG_VERSION})),"$LN + CONFIG+=" 'jpeg_cflags': $(quote_flags $(mason cflags jpeg ${LIBJPEG_VERSION})),"$LN + CONFIG+=" 'jpeg_ldflags': $(quote_flags $(mason ldflags jpeg ${LIBJPEG_VERSION})),"$LN +fi + if [ ! -z ${SQLITE_VERSION} ]; then mason install sqlite ${SQLITE_VERSION} CONFIG+=" 'sqlite3_static_libs': $(quote_flags $(mason static_libs sqlite ${SQLITE_VERSION})),"$LN diff --git a/gyp/install.gypi b/gyp/install.gypi index 7b8cf6220..d46900dff 100644 --- a/gyp/install.gypi +++ b/gyp/install.gypi @@ -23,6 +23,7 @@ ['OS == "linux"', { 'other_ldflags': [ '<@(png_static_libs)', + '<@(jpeg_static_libs)', '<@(glfw3_static_libs)', '<@(glfw3_ldflags)', ] diff --git a/gyp/mbgl-linux.gypi b/gyp/mbgl-linux.gypi index 823ba91c3..aee928685 100644 --- a/gyp/mbgl-linux.gypi +++ b/gyp/mbgl-linux.gypi @@ -8,6 +8,7 @@ 'variables': { 'cflags_cc': [ '<@(png_cflags)', + '<@(jpeg_cflags)', '<@(uv_cflags)', '<@(curl_cflags)', '<@(nu_cflags)', @@ -19,6 +20,7 @@ ], 'ldflags': [ '<@(png_ldflags)', + '<@(jpeg_ldflags)', '<@(uv_ldflags)', '<@(curl_ldflags)', '<@(nu_ldflags)', @@ -30,6 +32,9 @@ '../platform/default/string_stdlib.cpp', '../platform/default/http_request_baton_curl.cpp', '../platform/default/image.cpp', + '../platform/default/image_reader.cpp', + '../platform/default/png_reader.cpp', + '../platform/default/jpeg_reader.cpp', ], 'include_dirs': [ '../include', diff --git a/include/mbgl/platform/default/image_reader.hpp b/include/mbgl/platform/default/image_reader.hpp new file mode 100644 index 000000000..985e4874c --- /dev/null +++ b/include/mbgl/platform/default/image_reader.hpp @@ -0,0 +1,42 @@ +#ifndef MBGL_UTIL_IMAGE_READER_HPP +#define MBGL_UTIL_IMAGE_READER_HPP + +#include +#include +// stl +#include +#include + +namespace mbgl { namespace util { + +class ImageReaderException : public std::exception +{ +private: + std::string message_; +public: + ImageReaderException(std::string const& message) + : message_(message) {} + + ~ImageReaderException() throw() {} + + virtual const char* what() const throw() + { + return message_.c_str(); + } +}; + +struct ImageReader : private noncopyable +{ + virtual unsigned width() const=0; + virtual unsigned height() const=0; + virtual bool hasAlpha() const=0; + virtual bool premultipliedAlpha() const=0; + virtual void read(unsigned x,unsigned y, unsigned width, unsigned height, char* image)=0; + virtual ~ImageReader() {} +}; + +std::unique_ptr getImageReader(char const* data, size_t size); + +}} + +#endif diff --git a/include/mbgl/platform/default/jpeg_reader.hpp b/include/mbgl/platform/default/jpeg_reader.hpp new file mode 100644 index 000000000..7122f24cd --- /dev/null +++ b/include/mbgl/platform/default/jpeg_reader.hpp @@ -0,0 +1,69 @@ +#ifndef MBGL_UTIL_JPEG_READER_HPP +#define MBGL_UTIL_JPEG_READER_HPP + +#include + +// jpeg +extern "C" +{ +#include +} + +#include + +namespace mbgl { namespace util { + +template +class JpegReader : public ImageReader +{ +public: + using source_type = T; + using input_stream = boost::iostreams::stream; + const static unsigned BUF_SIZE = 4096; +private: + struct jpeg_stream_wrapper + { + jpeg_source_mgr manager; + input_stream * stream; + JOCTET buffer[BUF_SIZE]; + }; + + struct jpeg_info_guard + { + jpeg_info_guard(jpeg_decompress_struct * cinfo) + : i_(cinfo) {} + + ~jpeg_info_guard() + { + jpeg_destroy_decompress(i_); + } + jpeg_decompress_struct * i_; + }; + +private: + source_type source_; + input_stream stream_; + unsigned width_; + unsigned height_; +public: + JpegReader(char const* data, size_t size); + ~JpegReader(); + unsigned width() const; + unsigned height() const; + inline bool hasAlpha() const { return false; } + inline bool premultipliedAlpha() const { return true; } + void read(unsigned x,unsigned y, unsigned w, unsigned h, char *image); +private: + void init(); + static void on_error(j_common_ptr cinfo); + static void on_error_message(j_common_ptr cinfo); + static void init_source(j_decompress_ptr cinfo); + static boolean fill_input_buffer(j_decompress_ptr cinfo); + static void skip(j_decompress_ptr cinfo, long count); + static void term(j_decompress_ptr cinfo); + static void attach_stream(j_decompress_ptr cinfo, input_stream* in); +}; + +}} + +#endif // MBGL_UTIL_JPEG_READER_HPP diff --git a/include/mbgl/platform/default/png_reader.hpp b/include/mbgl/platform/default/png_reader.hpp new file mode 100644 index 000000000..66debd4fb --- /dev/null +++ b/include/mbgl/platform/default/png_reader.hpp @@ -0,0 +1,62 @@ +#ifndef MBGL_UTIL_PNG_READER_HPP +#define MBGL_UTIL_PNG_READER_HPP + +#include + +extern "C" +{ +#include +} + +#include +#include + +#include + +namespace mbgl { namespace util { + +template +class PngReader : public ImageReader +{ + using source_type = T; + using input_stream = boost::iostreams::stream; + + struct png_struct_guard + { + png_struct_guard(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) + : p_(png_ptr_ptr), + i_(info_ptr_ptr) {} + + ~png_struct_guard() + { + png_destroy_read_struct(p_,i_,0); + } + png_structpp p_; + png_infopp i_; + }; + +private: + source_type source_; + input_stream stream_; + unsigned width_; + unsigned height_; + int bit_depth_; + int color_type_; + bool has_alpha_; +public: + PngReader(char const* data, std::size_t size); + ~PngReader(); + unsigned width() const; + unsigned height() const; + inline bool hasAlpha() const { return has_alpha_; } + bool premultipliedAlpha() const { return true; } // png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2) + void read(unsigned x,unsigned y, unsigned width, unsigned height, char * image); +private: + void init(); + static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length); +}; + + +}} + +#endif // MBGL_UTIL_PNG_READER_HPP diff --git a/include/mbgl/util/uv_detail.hpp b/include/mbgl/util/uv_detail.hpp index 3bbfa4ac5..5da222b04 100644 --- a/include/mbgl/util/uv_detail.hpp +++ b/include/mbgl/util/uv_detail.hpp @@ -129,8 +129,8 @@ public: uv_worker_init(w, loop, count, name); } inline ~worker() { - uv_worker_close(w, [](uv_worker_t *worker) { - delete worker; + uv_worker_close(w, [](uv_worker_t *worker_) { + delete worker_; }); } inline void add(void *data, uv_worker_cb work_cb, uv_worker_after_cb after_work_cb) { diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp index c4be409be..fbeabd74e 100644 --- a/linux/mapboxgl-app.gyp +++ b/linux/mapboxgl-app.gyp @@ -16,10 +16,14 @@ 'xcode_settings': { 'OTHER_CPLUSPLUSFLAGS':[ '<@(glfw3_cflags)', + '<@(png_cflags)', + '<@(jpeg_cflags)', ], }, 'cflags_cc': [ '<@(glfw3_cflags)', + '<@(png_cflags)', + '<@(jpeg_cflags)', '-I<(boost_root)/include', ], 'variables': { diff --git a/platform/default/image.cpp b/platform/default/image.cpp index bf119fee0..242367e88 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -8,6 +8,7 @@ #include #include +#include // Check png library version. const static bool png_version_check = []() { @@ -72,117 +73,28 @@ std::string compress_png(int width, int height, void *rgba) { return result; } - -struct Buffer { - Buffer(const std::string& data_) - : data(data_.data()), length(data_.size()) {} - const char *const data = 0; - const size_t length = 0; - size_t pos = 0; -}; - -void readCallback(png_structp png, png_bytep data, png_size_t length) { - Buffer *reader = static_cast(png_get_io_ptr(png)); - - // Read `length` bytes into `data`. - if (reader->pos + length > reader->length) { - png_error(png, "Read Error"); - } else { - std::memcpy(data, reader->data + reader->pos, length); - reader->pos += length; - } -} - -void errorHandler(png_structp, png_const_charp error_msg) { - throw std::runtime_error(error_msg); -} - -void warningHandler(png_structp, png_const_charp error_msg) { - fprintf(stderr, "PNG: %s\n", error_msg); -} - -Image::Image(const std::string &data) { - Buffer buffer(data); - - if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) { - fprintf(stderr, "image is not a valid PNG image\n"); - return; - } - - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, errorHandler, warningHandler); - assert(png); - - png_infop info = png_create_info_struct(png); - assert(info); - - int depth, color, interlace; - - try { - png_set_read_fn(png, (png_voidp)&buffer, readCallback); - png_read_info(png, info); - png_get_IHDR(png, info, (png_uint_32*)&width, (png_uint_32*)&height, &depth, &color, &interlace, nullptr, nullptr); - bool alpha = (color & PNG_COLOR_MASK_ALPHA) || png_get_valid(png, info, PNG_INFO_tRNS); - - // From http://trac.mapnik.org/browser/trunk/src/png_reader.cpp - if (color == PNG_COLOR_TYPE_PALETTE) - png_set_expand(png); - if (color == PNG_COLOR_TYPE_GRAY) - png_set_expand(png); - if (png_get_valid(png, info, PNG_INFO_tRNS)) - png_set_expand(png); - if (depth == 16) - png_set_strip_16(png); - if (depth < 8) - png_set_packing(png); - if (color == PNG_COLOR_TYPE_GRAY || - color == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png); - - if (interlace == PNG_INTERLACE_ADAM7) - png_set_interlace_handling(png); - - // Always add an alpha channel. - if (!alpha) { - png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER); - } - - double gamma; - 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); - +Image::Image(std::string const& data) +{ + try + { + auto reader = getImageReader(data.c_str(), data.size()); + width = reader->width(); + height = reader->height(); img = ::std::unique_ptr(new char[width * height * 4]()); + reader->read(0, 0, width, height, img.get()); + } + catch (ImageReaderException const& ex) + { + fprintf(stderr, "Image: %s\n", ex.what()); + img.reset(); + width = 0; + height = 0; - char *surface = img.get(); - assert(surface); - - struct ptrs { - ptrs(size_t count) : rows(new png_bytep[count]) {} - ~ptrs() { delete[] rows; } - png_bytep *rows = nullptr; - } pointers(height); - for (unsigned i = 0; i < height; ++i) { - pointers.rows[i] = (png_bytep)(surface + (i * rowbytes)); - } - - // Read image data - png_read_image(png, pointers.rows); - - png_read_end(png, nullptr); - - png_destroy_read_struct(&png, &info, nullptr); - } catch (std::exception& e) { - fprintf(stderr, "loading PNG failed: %s\n", e.what()); - png_destroy_read_struct(&png, &info, nullptr); - if (img) { - img.reset(); - } + } + catch (...) // catch the rest + { + fprintf(stderr, "Image: exception in constructor"); + img.reset(); width = 0; height = 0; } diff --git a/platform/default/image_reader.cpp b/platform/default/image_reader.cpp new file mode 100644 index 000000000..e80ccb681 --- /dev/null +++ b/platform/default/image_reader.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +#include +#include + +namespace mbgl { namespace util { + +inline boost::optional type_from_bytes(char const* data, size_t size) +{ + using result_type = boost::optional; + if (size >= 4) + { + unsigned int magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + if (magic == 0x89504E47U) + { + return result_type("png"); + } + else if (magic == 0x49492A00 || magic == 0x4D4D002A) + { + return result_type("tiff"); + } + } + if (size>=2) + { + unsigned int magic = ((data[0] << 8) | data[1]) & 0xffff; + if (magic == 0xffd8) + { + return result_type("jpeg"); + } + } + + if (size>=12) + { + if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' && + data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P') + { + return result_type("webp"); + } + } + return result_type(); +} + +std::unique_ptr getImageReader(char const* data, size_t size) +{ + boost::optional type = type_from_bytes(data,size); + if (type) + { + if (*type == "png") + { + return std::make_unique>(data, size); + } + else if (*type == "jpeg") + { + return std::make_unique>(data, size); + } + } + throw ImageReaderException("ImageReader: can't determine type from input data"); +} + +}} diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp new file mode 100644 index 000000000..f89e76687 --- /dev/null +++ b/platform/default/jpeg_reader.cpp @@ -0,0 +1,203 @@ +#include + +// boost +#include +#include + + +// std +#include +#include + +namespace mbgl { namespace util { + +// ctor +template +JpegReader::JpegReader(char const* data, size_t size) + : source_(data, size), + stream_(source_), + width_(0), + height_(0) +{ + if (!stream_) throw ImageReaderException("cannot open image stream"); + init(); +} + +// dtor +template +JpegReader::~JpegReader() {} + +// jpeg stream wrapper +template +void JpegReader::init_source (j_decompress_ptr cinfo) +{ + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + wrap->stream->seekg(0,std::ios_base::beg); +} + +template +boolean JpegReader::fill_input_buffer (j_decompress_ptr cinfo) +{ + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + wrap->stream->read(reinterpret_cast(&wrap->buffer[0]),BUF_SIZE); + std::streamsize size = wrap->stream->gcount(); + wrap->manager.next_input_byte = wrap->buffer; + wrap->manager.bytes_in_buffer = BUF_SIZE; + return (size > 0) ? TRUE : FALSE; +} + +template +void JpegReader::skip(j_decompress_ptr cinfo, long count) +{ + if (count <= 0) return; //A zero or negative skip count should be treated as a no-op. + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + + if (wrap->manager.bytes_in_buffer > 0 && count < static_cast(wrap->manager.bytes_in_buffer)) + { + wrap->manager.bytes_in_buffer -= count; + wrap->manager.next_input_byte = &wrap->buffer[BUF_SIZE - wrap->manager.bytes_in_buffer]; + } + else + { + wrap->stream->seekg(count - wrap->manager.bytes_in_buffer, std::ios_base::cur); + // trigger buffer fill + wrap->manager.next_input_byte = 0; + wrap->manager.bytes_in_buffer = 0; //bytes_in_buffer may be zero on return. + } +} + +template +void JpegReader::term (j_decompress_ptr /*cinfo*/) +{ +// no-op +} + +template +void JpegReader::attach_stream (j_decompress_ptr cinfo, input_stream* in) +{ + if (cinfo->src == 0) + { + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper)); + } + JpegReader::jpeg_stream_wrapper * src = reinterpret_cast (cinfo->src); + src->manager.init_source = init_source; + src->manager.fill_input_buffer = fill_input_buffer; + src->manager.skip_input_data = skip; + src->manager.resync_to_restart = jpeg_resync_to_restart; + src->manager.term_source = term; + src->manager.bytes_in_buffer = 0; + src->manager.next_input_byte = 0; + src->stream = in; +} + +template +void JpegReader::on_error(j_common_ptr /*cinfo*/) +{ +} + +template +void JpegReader::on_error_message(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + throw ImageReaderException(std::string("JPEG Reader: libjpeg could not read image: ") + buffer); +} + +template +void JpegReader::init() +{ + jpeg_decompress_struct cinfo; + jpeg_info_guard iguard(&cinfo); + jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = on_error; + jerr.output_message = on_error_message; + jpeg_create_decompress(&cinfo); + attach_stream(&cinfo, &stream_); + int ret = jpeg_read_header(&cinfo, TRUE); + if (ret != JPEG_HEADER_OK) + throw ImageReaderException("JPEG Reader: failed to read header"); + jpeg_start_decompress(&cinfo); + width_ = cinfo.output_width; + height_ = cinfo.output_height; + + if (cinfo.out_color_space == JCS_UNKNOWN) + { + throw ImageReaderException("JPEG Reader: failed to read unknown color space"); + } + if (cinfo.output_width == 0 || cinfo.output_height == 0) + { + throw ImageReaderException("JPEG Reader: failed to read image size of"); + } +} + +template +unsigned JpegReader::width() const +{ + return width_; +} + +template +unsigned JpegReader::height() const +{ + return height_; +} + +template +void JpegReader::read(unsigned x0, unsigned y0, unsigned w, unsigned h, char* image) +{ + stream_.clear(); + stream_.seekg(0, std::ios_base::beg); + + jpeg_decompress_struct cinfo; + jpeg_info_guard iguard(&cinfo); + jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = on_error; + jerr.output_message = on_error_message; + jpeg_create_decompress(&cinfo); + attach_stream(&cinfo, &stream_); + int ret = jpeg_read_header(&cinfo, TRUE); + if (ret != JPEG_HEADER_OK) throw ImageReaderException("JPEG Reader read(): failed to read header"); + jpeg_start_decompress(&cinfo); + JSAMPARRAY buffer; + int row_stride; + unsigned char r,g,b; + row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + w = std::min(w,width_ - x0); + h = std::min(h,height_ - y0); + + const std::unique_ptr out_row(new unsigned int[w]); + unsigned row = 0; + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, buffer, 1); + if (row >= y0 && row < y0 + h) + { + for (unsigned int x = 0; x < w; ++x) + { + unsigned col = x + x0; + r = buffer[0][cinfo.output_components * col]; + if (cinfo.output_components > 2) + { + g = buffer[0][cinfo.output_components * col + 1]; + b = buffer[0][cinfo.output_components * col + 2]; + } else { + g = r; + b = r; + } + out_row[x] = (0xff << 24) | (b << 16) | (g << 8) | r; + } + std::copy((char*)out_row.get(), (char*)out_row.get() + w*4, image + (row - y0)*width_*4); + } + ++row; + } + jpeg_finish_decompress(&cinfo); +} + +template class JpegReader; + +}} diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp new file mode 100644 index 000000000..cf96ca236 --- /dev/null +++ b/platform/default/png_reader.cpp @@ -0,0 +1,200 @@ +#include +#include +extern "C" +{ +#include +} + +// boost +#include +#include +#include + +// stl +#include +#include + +namespace mbgl { namespace util { + +void user_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg) +{ + throw ImageReaderException(std::string("failed to read invalid png: '") + error_msg + "'"); +} + +void user_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg) +{ + fprintf(stderr, "ImageReader (PNG): %s\n", warning_msg); + +} + +template +void PngReader::png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + input_stream * fin = reinterpret_cast(png_get_io_ptr(png_ptr)); + fin->read(reinterpret_cast(data), length); + std::streamsize read_count = fin->gcount(); + if (read_count < 0 || static_cast(read_count) != length) + { + png_error(png_ptr, "Read Error"); + } +} + +template +PngReader::PngReader(char const* data, std::size_t size) + : source_(data,size), + stream_(source_), + width_(0), + height_(0), + bit_depth_(0), + color_type_(0), + has_alpha_(false) +{ + + if (!stream_) throw ImageReaderException("PNG reader: cannot open image stream"); + init(); +} + + +template +PngReader::~PngReader() {} + + +template +void PngReader::init() +{ + png_byte header[8]; + std::memset(header,0,8); + stream_.read(reinterpret_cast(header),8); + if ( stream_.gcount() != 8) + { + throw ImageReaderException("PNG reader: Could not read image"); + } + int is_png=!png_sig_cmp(header,0,8); + if (!is_png) + { + throw ImageReaderException("File or stream is not a png"); + } + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING,0,0,0); + + if (!png_ptr) + { + throw ImageReaderException("failed to allocate png_ptr"); + } + + // catch errors in a custom way to avoid the need for setjmp + png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); + + png_infop info_ptr; + png_struct_guard sguard(&png_ptr,&info_ptr); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) throw ImageReaderException("failed to create info_ptr"); + + png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); + + png_set_sig_bytes(png_ptr,8); + png_read_info(png_ptr, info_ptr); + + png_uint_32 w, h; + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth_, &color_type_,0,0,0); + has_alpha_ = (color_type_ & PNG_COLOR_MASK_ALPHA) || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); + width_=w; + height_=h; +} + +template +unsigned PngReader::width() const +{ + return width_; +} + +template +unsigned PngReader::height() const +{ + return height_; +} + +template +void PngReader::read(unsigned x0, unsigned y0, unsigned w, unsigned h, char * image) +{ + stream_.clear(); + stream_.seekg(0, std::ios_base::beg); + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING,0,0,0); + + if (!png_ptr) + { + throw ImageReaderException("failed to allocate png_ptr"); + } + + // catch errors in a custom way to avoid the need for setjmp + png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); + + png_infop info_ptr; + png_struct_guard sguard(&png_ptr,&info_ptr); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) throw ImageReaderException("failed to create info_ptr"); + + png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); + png_read_info(png_ptr, info_ptr); + + if (color_type_ == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (bit_depth_ == 16) + png_set_strip_16(png_ptr); + if (color_type_ == PNG_COLOR_TYPE_GRAY || + color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + // quick hack -- only work in >=libpng 1.2.7 + png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba + + double gamma; + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, 2.2, gamma); + png_set_alpha_mode(png_ptr, PNG_ALPHA_PREMULTIPLIED, 2.2); + + if (x0 == 0 && y0 == 0 && w >= width_ && h >= height_) + { + if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) + { + png_set_interlace_handling(png_ptr); // FIXME: libpng bug? + // according to docs png_read_image + // "..automatically handles interlacing, + // so you don't need to call png_set_interlace_handling()" + } + png_read_update_info(png_ptr, info_ptr); + // we can read whole image at once + // alloc row pointers + const std::unique_ptr rows(new png_bytep[height_]); + for (unsigned row = 0; row < height_; ++row) + rows[row] = (png_bytep)image + row * width_ * 4 ; + png_read_image(png_ptr, rows.get()); + } + else + { + png_read_update_info(png_ptr, info_ptr); + w=std::min(w, width_ - x0); + h=std::min(h, height_ - y0); + unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); + const std::unique_ptr row(new png_byte[rowbytes]); + for (unsigned i = 0; i < height_; ++i) + { + png_read_row(png_ptr,row.get(),0); + if (i >= y0 && i < (y0 + h)) + { + std::copy(&row[x0 * 4], &row[x0 * 4] + w * 4, image + i * width_* 4); + } + } + } + png_read_end(png_ptr,0); +} + +template class PngReader; + +}} diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp index 9ff694bf3..0a59bdc62 100644 --- a/src/map/tile_data.cpp +++ b/src/map/tile_data.cpp @@ -37,6 +37,12 @@ void TileData::request(FileSource& fileSource) { if (token == "z") return std::to_string(id.z); if (token == "x") return std::to_string(id.x); if (token == "y") return std::to_string(id.y); + if (token == "prefix") { + std::string prefix { 2 }; + prefix[0] = "0123456789abcdef"[id.x % 16]; + prefix[1] = "0123456789abcdef"[id.y % 16]; + return prefix; + } if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : ""); return ""; }); diff --git a/src/storage/http_request.cpp b/src/storage/http_request.cpp index 46b1d5e19..c87a92e12 100644 --- a/src/storage/http_request.cpp +++ b/src/storage/http_request.cpp @@ -36,12 +36,12 @@ void HTTPRequest::startCacheRequest() { cache_baton->request = this; cache_baton->path = path; cache_baton->store = store; - store->get(path, [](std::unique_ptr &&response, void *ptr) { + store->get(path, [](std::unique_ptr &&response_, void *ptr) { // Wrap in a unique_ptr, so it'll always get auto-destructed. std::unique_ptr baton((CacheRequestBaton *)ptr); if (baton->request) { baton->request->cache_baton = nullptr; - baton->request->handleCacheResponse(std::move(response)); + baton->request->handleCacheResponse(std::move(response_)); } }, cache_baton); } diff --git a/test/test.gyp b/test/test.gyp index cd2bb0ce0..86dbcc08a 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -170,12 +170,12 @@ ['OS == "mac"', { 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': ['<@(uv_cflags)'], + 'OTHER_CPLUSPLUSFLAGS': ['<@(uv_cflags)','<@(png_cflags)'], 'OTHER_LDFLAGS': ['<@(glfw3_ldflags)', '<@(ldflags)'], }, }, { - 'cflags': ['<@(uv_cflags)'], + 'cflags': ['<@(uv_cflags)','<@(png_cflags)'], 'libraries': ['<@(glfw3_ldflags)', '<@(ldflags)'], }], ],