diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index 5d674be72..54c57cd5d 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -44,6 +44,7 @@ // cairo #if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO) #include +#include #include #include #endif @@ -217,7 +218,8 @@ void composite(image_32 & dst, image_32 & src, mapnik::composite_mode_e mode, fl std::shared_ptr from_cairo(PycairoSurface* py_surface) { mapnik::cairo_surface_ptr surface(cairo_surface_reference(py_surface->surface), mapnik::cairo_surface_closer()); - std::shared_ptr image_ptr = std::make_shared(surface); + std::shared_ptr image_ptr = std::make_shared(cairo_image_surface_get_width(&*surface), cairo_image_surface_get_height(&*surface)); + cairo_image_to_rgba8(image_ptr->data(), image_surface); return image_ptr; } #endif diff --git a/demo/c++/rundemo.cpp b/demo/c++/rundemo.cpp index 25c9233a2..e21c53251 100644 --- a/demo/c++/rundemo.cpp +++ b/demo/c++/rundemo.cpp @@ -40,6 +40,7 @@ #if defined(HAVE_CAIRO) #include +#include #endif #include @@ -352,8 +353,9 @@ int main ( int, char** ) cairo_surface_write_to_png(&*image_surface, "cairo-demo.png"); // but we can also benefit from quantization by converting // to a mapnik image object and then saving that - image_32 im(image_surface); - save_to_file(im, "cairo-demo256.png","png8"); + mapnik::image_data_rgba8 im_data(cairo_image_surface_get_width(&*image_surface), cairo_image_surface_get_height(&*image_surface)); + cairo_image_to_rgba8(im_data, image_surface); + save_to_file(im_data, "cairo-demo256.png","png8"); cairo_surface_finish(&*image_surface); std::cout << "Three maps have been rendered using Cairo in the current directory:\n" diff --git a/include/mapnik/cairo/cairo_image_util.hpp b/include/mapnik/cairo/cairo_image_util.hpp new file mode 100644 index 000000000..5fe764bcf --- /dev/null +++ b/include/mapnik/cairo/cairo_image_util.hpp @@ -0,0 +1,84 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2015 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + + +#ifndef MAPNIK_CAIRO_IMAGE_UTIL_HPP +#define MAPNIK_CAIRO_IMAGE_UTIL_HPP + +// mapnik +#include +#include // for cairo_surface_ptr + +// stl +#include + +namespace mapnik { + +static inline void cairo_image_to_rgba8(mapnik::image_data_rgba8 & data, + cairo_surface_ptr const& surface) +{ + if (cairo_image_surface_get_format(&*surface) != CAIRO_FORMAT_ARGB32) + { + throw std::runtime_error("Unable to convert this Cairo format to rgba8 image"); + } + + if (cairo_image_surface_get_width(&*surface) != data.width() || + cairo_image_surface_get_height(&*surface) != data.height()) + { + throw std::runtime_error("Mismatch in dimensions: size of image must match side of cairo surface"); + } + + int stride = cairo_image_surface_get_stride(&*surface) / 4; + + const std::unique_ptr out_row(new unsigned int[data.width()]); + const unsigned int *in_row = (const unsigned int *)cairo_image_surface_get_data(&*surface); + + for (unsigned int row = 0; row < data.height(); row++, in_row += stride) + { + for (unsigned int column = 0; column < data.width(); column++) + { + unsigned int in = in_row[column]; + unsigned int a = (in >> 24) & 0xff; + unsigned int r = (in >> 16) & 0xff; + unsigned int g = (in >> 8) & 0xff; + unsigned int b = (in >> 0) & 0xff; + +#define DE_ALPHA(x) do { \ + if (a == 0) x = 0; \ + else x = x * 255 / a; \ + if (x > 255) x = 255; \ + } while(0) + + DE_ALPHA(r); + DE_ALPHA(g); + DE_ALPHA(b); + + out_row[column] = color(r, g, b, a).rgba(); + } + data.setRow(row, out_row.get(), data.width()); + } +} + +} + + +#endif // MAPNIK_CAIRO_IMAGE_UTIL_HPP diff --git a/include/mapnik/graphics.hpp b/include/mapnik/graphics.hpp index 0e753fb44..d721cdfbb 100644 --- a/include/mapnik/graphics.hpp +++ b/include/mapnik/graphics.hpp @@ -39,29 +39,19 @@ // boost #include -struct _cairo_surface; -typedef struct _cairo_surface cairo_surface_t; - namespace mapnik { -using cairo_surface_ptr = std::shared_ptr; - class MAPNIK_DECL image_32 { private: - unsigned width_; - unsigned height_; - boost::optional background_; image_data_rgba8 data_; + boost::optional background_; bool painted_; bool premultiplied_; public: image_32(int width,int height); image_32(image_32 const& rhs); -#ifdef HAVE_CAIRO - explicit image_32(cairo_surface_ptr const& surface); -#endif ~image_32(); void painted(bool painted) @@ -127,7 +117,7 @@ private: inline bool checkBounds(int x, int y) const { - return (x >= 0 && x < static_cast(width_) && y >= 0 && y < static_cast(height_)); + return (x >= 0 && x < static_cast(data_.width()) && y >= 0 && y < static_cast(data_.height())); } public: @@ -143,17 +133,17 @@ public: inline unsigned width() const { - return width_; + return data_.width(); } inline unsigned height() const { - return height_; + return data_.height(); } inline void set_rectangle(int x0,int y0,image_data_rgba8 const& data) { - box2d ext0(0,0,width_,height_); + box2d ext0(0,0,data_.width(),data_.height()); box2d ext1(x0,y0,x0+data.width(),y0+data.height()); if (ext0.intersects(ext1)) @@ -177,7 +167,7 @@ public: inline void set_rectangle_alpha(int x0,int y0,const image_data_rgba8& data) { - box2d ext0(0,0,width_,height_); + box2d ext0(0,0,data_.width(),data_.height()); box2d ext1(x0,y0,x0 + data.width(),y0 + data.height()); if (ext0.intersects(ext1)) @@ -221,7 +211,7 @@ public: inline void set_rectangle_alpha2(image_data_rgba8 const& data, unsigned x0, unsigned y0, float opacity) { - box2d ext0(0,0,width_,height_); + box2d ext0(0,0,data_.width(),data_.height()); box2d ext1(x0,y0,x0 + data.width(),y0 + data.height()); if (ext0.intersects(ext1)) @@ -269,7 +259,7 @@ public: template inline void merge_rectangle(image_data_rgba8 const& data, unsigned x0, unsigned y0, float opacity) { - box2d ext0(0,0,width_,height_); + box2d ext0(0,0,data_.width(),data_.height()); box2d ext1(x0,y0,x0 + data.width(),y0 + data.height()); if (ext0.intersects(ext1)) diff --git a/src/graphics.cpp b/src/graphics.cpp index 3b053a559..901b5fa50 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -39,74 +39,24 @@ namespace mapnik { image_32::image_32(int width,int height) - :width_(width), - height_(height), - data_(width,height), + : data_(width,height), painted_(false), premultiplied_(false) {} image_32::image_32(image_32 const& rhs) - :width_(rhs.width_), - height_(rhs.height_), - data_(rhs.data_), + : data_(rhs.data_), painted_(rhs.painted_), premultiplied_(rhs.premultiplied_) {} -#ifdef HAVE_CAIRO -image_32::image_32(cairo_surface_ptr const& surface) - :width_(cairo_image_surface_get_width(&*surface)), - height_(cairo_image_surface_get_height(&*surface)), - data_(width_, height_), - premultiplied_(false) -{ - painted_ = true; - if ( cairo_image_surface_get_format(&*surface) != CAIRO_FORMAT_ARGB32) - { - MAPNIK_LOG_WARN(graphics) << "Unable to convert this Cairo format"; - throw; - } - - int stride = cairo_image_surface_get_stride(&*surface) / 4; - - const std::unique_ptr out_row(new unsigned int[width_]); - const unsigned int *in_row = (const unsigned int *)cairo_image_surface_get_data(&*surface); - - for (unsigned int row = 0; row < height_; row++, in_row += stride) - { - for (unsigned int column = 0; column < width_; column++) - { - unsigned int in = in_row[column]; - unsigned int a = (in >> 24) & 0xff; - unsigned int r = (in >> 16) & 0xff; - unsigned int g = (in >> 8) & 0xff; - unsigned int b = (in >> 0) & 0xff; - -#define DE_ALPHA(x) do { \ - if (a == 0) x = 0; \ - else x = x * 255 / a; \ - if (x > 255) x = 255; \ - } while(0) - - DE_ALPHA(r); - DE_ALPHA(g); - DE_ALPHA(b); - - out_row[column] = color(r, g, b, a).rgba(); - } - data_.setRow(row, out_row.get(), width_); - } -} -#endif - image_32::~image_32() {} void image_32::set_grayscale_to_alpha() { - for (unsigned int y = 0; y < height_; ++y) + for (unsigned int y = 0; y < data_.height(); ++y) { unsigned int* row_from = data_.getRow(y); - for (unsigned int x = 0; x < width_; ++x) + for (unsigned int x = 0; x < data_.width(); ++x) { unsigned rgba = row_from[x]; unsigned r = rgba & 0xff; @@ -123,10 +73,10 @@ void image_32::set_grayscale_to_alpha() void image_32::set_color_to_alpha(const color& c) { - for (unsigned y = 0; y < height_; ++y) + for (unsigned y = 0; y < data_.height(); ++y) { unsigned int* row_from = data_.getRow(y); - for (unsigned x = 0; x < width_; ++x) + for (unsigned x = 0; x < data_.width(); ++x) { unsigned rgba = row_from[x]; unsigned r = rgba & 0xff; @@ -142,10 +92,10 @@ void image_32::set_color_to_alpha(const color& c) void image_32::set_alpha(float opacity) { - for (unsigned int y = 0; y < height_; ++y) + for (unsigned int y = 0; y < data_.height(); ++y) { unsigned int* row_to = data_.getRow(y); - for (unsigned int x = 0; x < width_; ++x) + for (unsigned int x = 0; x < data_.width(); ++x) { unsigned rgba = row_to[x]; unsigned a0 = (rgba >> 24) & 0xff; @@ -175,7 +125,7 @@ boost::optional const& image_32::get_background() const void image_32::premultiply() { - agg::rendering_buffer buffer(data_.getBytes(),width_,height_,width_ * 4); + agg::rendering_buffer buffer(data_.getBytes(),data_.width(),data_.height(),data_.width() * 4); agg::pixfmt_rgba32 pixf(buffer); pixf.premultiply(); premultiplied_ = true; @@ -183,7 +133,7 @@ void image_32::premultiply() void image_32::demultiply() { - agg::rendering_buffer buffer(data_.getBytes(),width_,height_,width_ * 4); + agg::rendering_buffer buffer(data_.getBytes(),data_.width(),data_.height(),data_.width() * 4); agg::pixfmt_rgba32_pre pixf(buffer); pixf.demultiply(); premultiplied_ = false; diff --git a/tests/cpp_tests/image_io_test.cpp b/tests/cpp_tests/image_io_test.cpp index 1e7199c98..f7cb158f6 100644 --- a/tests/cpp_tests/image_io_test.cpp +++ b/tests/cpp_tests/image_io_test.cpp @@ -7,6 +7,10 @@ #include #include #include +#if defined(HAVE_CAIRO) +#include +#include +#endif #include "utils.hpp" @@ -60,6 +64,18 @@ int main(int argc, char** argv) BOOST_TEST( true ); // should hit bad alloc here } +#if defined(HAVE_CAIRO) + mapnik::cairo_surface_ptr image_surface( + cairo_image_surface_create(CAIRO_FORMAT_ARGB32,256,257), + mapnik::cairo_surface_closer()); + mapnik::image_data_rgba8 im_data(cairo_image_surface_get_width(&*image_surface), cairo_image_surface_get_height(&*image_surface)); + im_data.set(1); + BOOST_TEST( (unsigned)im_data(0,0) == unsigned(1) ); + // Should set back to fully transparent + mapnik::cairo_image_to_rgba8(im_data, image_surface); + BOOST_TEST( (unsigned)im_data(0,0) == unsigned(0) ); +#endif + #if defined(HAVE_PNG) should_throw = "./tests/cpp_tests/data/blank.png"; BOOST_TEST( mapnik::util::exists( should_throw ) );