diff options
author | Justin R. Miller <incanus@codesorcery.net> | 2014-03-18 16:57:17 -0700 |
---|---|---|
committer | Justin R. Miller <incanus@codesorcery.net> | 2014-03-18 16:57:17 -0700 |
commit | 00a3245ce8c43f9d74d0627170a22b123734c7eb (patch) | |
tree | 8765c5595ae0eee89823532d949d8cbe61518fd3 /src/util/raster.cpp | |
parent | bdfa761c39218234d9ce7419b3b323e8f25a1476 (diff) | |
download | qtlocation-mapboxgl-00a3245ce8c43f9d74d0627170a22b123734c7eb.tar.gz |
fixes #70: raster tile rendering as toggleable mode from vector
Diffstat (limited to 'src/util/raster.cpp')
-rw-r--r-- | src/util/raster.cpp | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/util/raster.cpp b/src/util/raster.cpp new file mode 100644 index 0000000000..121fe53bca --- /dev/null +++ b/src/util/raster.cpp @@ -0,0 +1,195 @@ +#include <llmr/util/raster.hpp> + +#include <memory> +#include <cassert> + +#include <llmr/platform/gl.hpp> + +#include <png.h> + +using namespace llmr; + +Raster::~Raster() { + if (img && !textured) { + free(img); + } else if (textured) { + texturepool->removeTextureID(texture); + } +} + +Raster::operator bool() const { + std::lock_guard<std::mutex> lock(mtx); + return loaded; +} + +void Raster::load(const std::string& data) { + std::shared_ptr<Raster> raster = shared_from_this(); + + raster->loadImage(data); + + std::lock_guard<std::mutex> lock(raster->mtx); + if (raster->img) { + raster->loaded = true; + } +} + +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<Buffer *>(png_get_io_ptr(png)); + + // Read `length` bytes into `data`. + if (reader->pos + length > reader->length) { + png_error(png, "Read Error"); + } else { + memcpy(data, reader->data + reader->pos, length); + reader->pos += length; + } +} + +void errorHandler(png_structp png, png_const_charp error_msg) { + throw std::runtime_error(error_msg); +} + +void warningHandler(png_structp png, png_const_charp error_msg) { + fprintf(stderr, "PNG: %s\n", error_msg); +} + +void Raster::loadImage(const std::string& data) { + std::lock_guard<std::mutex> lock(mtx); + + 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_read_update_info(png, info); + + png_size_t rowbytes = png_get_rowbytes(png, info); + assert(width * 4 == rowbytes); + + img = (char *)malloc(width * height * 4); + char *surface = img; + assert(surface); + + png_bytep row_pointers[height]; + for (unsigned i = 0; i < height; ++i) { + row_pointers[i] = (png_bytep)(surface + (i * rowbytes)); + } + + // Read image data + png_read_image(png, row_pointers); + + 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) { + free(img); + img = nullptr; + } + width = 0; + height = 0; + } +} + +void Raster::setTexturepool(Texturepool* new_texturepool) { + texturepool = new_texturepool; +} + +void Raster::bind(bool linear) { + if (!width || !height) { + fprintf(stderr, "trying to bind texture without dimension\n"); + return; + } + + if (img && !textured) { + if (!texturepool) { + fprintf(stderr, "no available texture pool\n"); + return; + } + texture = texturepool->getTextureID(); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img); + free(img); + img = nullptr; + textured = true; + } else { + glBindTexture(GL_TEXTURE_2D, texture); + } + + GLuint filter = linear ? GL_LINEAR : GL_NEAREST; + if (filter != this->filter) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + this->filter = filter; + } +} + +void Raster::beginFadeInAnimation() { + fade_animation = std::make_shared<util::ease_animation>(opacity, 1.0, opacity, 0.25); +} + +bool Raster::needsAnimation() const { + return fade_animation != nullptr; +} + +void Raster::updateAnimations() { + if (fade_animation->update() == util::animation::complete) { + fade_animation = nullptr; + } +} + |