diff --git a/bindings/python/mapnik_color.cpp b/bindings/python/mapnik_color.cpp index fc642f449..7210730c2 100644 --- a/bindings/python/mapnik_color.cpp +++ b/bindings/python/mapnik_color.cpp @@ -57,11 +57,27 @@ void export_color () "and an alpha value.\n" "All values between 0 and 255.\n") ) + .def(init( + ( arg("r"), arg("g"), arg("b"), arg("a"), arg("premultiplied") ), + "Creates a new color from its RGB components\n" + "and an alpha value.\n" + "All values between 0 and 255.\n") + ) .def(init( ( arg("r"), arg("g"), arg("b") ), "Creates a new color from its RGB components.\n" "All values between 0 and 255.\n") ) + .def(init( + ( arg("val") ), + "Creates a new color from an unsigned integer.\n" + "All values between 0 and 2^32-1\n") + ) + .def(init( + ( arg("val"), arg("premultiplied") ), + "Creates a new color from an unsigned integer.\n" + "All values between 0 and 2^32-1\n") + ) .def(init( ( arg("color_string") ), "Creates a new color from its CSS string representation.\n" @@ -93,6 +109,12 @@ void export_color () .def_pickle(color_pickle_suite()) .def("__str__",&color::to_string) .def("packed",&color::rgba) + .def("premultiply",&color::premultiply) + .def("set_premultiplied",&color::set_premultiplied) + .def("get_premultiplied",&color::get_premultiplied) + .def("premultiply",&color::premultiply) + .def("demultiply",&color::demultiply) + .def("packed",&color::rgba) .def("to_hex_string",&color::to_hex_string, "Returns the hexadecimal representation of this color.\n" "\n" diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index 6263faf4d..a7142e699 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -131,9 +131,9 @@ void background(mapnik::image_any & im, mapnik::color const& c) mapnik::fill(im, c); } -uint32_t get_pixel(mapnik::image_any const& im, int x, int y) +uint32_t get_pixel(mapnik::image_any const& im, unsigned x, unsigned y) { - if (x < static_cast(im.width()) && y < static_cast(im.height())) + if (x < static_cast(im.width()) && y < static_cast(im.height())) { return mapnik::get_pixel(im, x, y); } @@ -142,8 +142,25 @@ uint32_t get_pixel(mapnik::image_any const& im, int x, int y) return 0; } +mapnik::color get_pixel_color(mapnik::image_any const& im, unsigned x, unsigned y) +{ + if (x < static_cast(im.width()) && y < static_cast(im.height())) + { + return mapnik::get_pixel(im, x, y); + } + PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions"); + boost::python::throw_error_already_set(); + return 0; +} + void set_pixel(mapnik::image_any & im, unsigned x, unsigned y, mapnik::color const& c) { + if (x >= static_cast(im.width()) && y >= static_cast(im.height())) + { + PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions"); + boost::python::throw_error_already_set(); + return; + } mapnik::set_pixel(im, x, y, c); } @@ -324,6 +341,7 @@ void export_image() .def("demultiply",&demultiply) .def("set_pixel",&set_pixel) .def("get_pixel",&get_pixel) + .def("get_pixel_color",&get_pixel_color) .def("clear",&clear) //TODO(haoyu) The method name 'tostring' might be confusing since they actually return bytes in Python 3 diff --git a/include/mapnik/color.hpp b/include/mapnik/color.hpp index f220ddd07..4d2eb846c 100644 --- a/include/mapnik/color.hpp +++ b/include/mapnik/color.hpp @@ -44,29 +44,39 @@ private: std::uint8_t green_; std::uint8_t blue_; std::uint8_t alpha_; - + bool premultiplied_; public: // default ctor color() : red_(0xff), green_(0xff), blue_(0xff), - alpha_(0xff) + alpha_(0xff), + premultiplied_(false) {} - color(std::uint8_t red, std::uint8_t green, std::uint8_t blue, std::uint8_t alpha = 0xff) + color(std::uint8_t red, std::uint8_t green, std::uint8_t blue, std::uint8_t alpha = 0xff, bool premultiplied = false) : red_(red), green_(green), blue_(blue), - alpha_(alpha) + alpha_(alpha), + premultiplied_(premultiplied) {} + color(std::uint32_t rgba, bool premultiplied = false) + : red_(rgba & 0xff), + green_((rgba >> 8) & 0xff), + blue_((rgba >> 16) & 0xff), + alpha_((rgba >> 24) & 0xff), + premultiplied_(premultiplied) {} + // copy ctor color(const color& rhs) : red_(rhs.red_), green_(rhs.green_), blue_(rhs.blue_), - alpha_(rhs.alpha_) + alpha_(rhs.alpha_), + premultiplied_(rhs.premultiplied_) {} // move ctor @@ -74,14 +84,15 @@ public: : red_(std::move(rhs.red_)), green_(std::move(rhs.green_)), blue_(std::move(rhs.blue_)), - alpha_(std::move(rhs.alpha_)) {} + alpha_(std::move(rhs.alpha_)), + premultiplied_(std::move(rhs.premultiplied_)) {} color( std::string const& str); std::string to_string() const; std::string to_hex_string() const; - void premultiply(); - void demultiply(); + bool premultiply(); + bool demultiply(); color& operator=(color rhs) { @@ -135,6 +146,14 @@ public: { alpha_ = alpha; } + inline bool get_premultiplied() const + { + return premultiplied_; + } + inline void set_premultiplied(bool status) + { + premultiplied_ = status; + } inline unsigned rgba() const { diff --git a/include/mapnik/image_view.hpp b/include/mapnik/image_view.hpp index 0c9a45e82..2256cfbca 100644 --- a/include/mapnik/image_view.hpp +++ b/include/mapnik/image_view.hpp @@ -84,6 +84,10 @@ public: { return height_; } + inline const pixel_type& operator() (std::size_t i, std::size_t j) const + { + return data_(i,j); + } inline unsigned getSize() const { @@ -110,6 +114,11 @@ public: return data_; } + inline bool get_premultiplied() const + { + return data_.get_premultiplied(); + } + private: unsigned x_; unsigned y_; diff --git a/include/mapnik/tiff_io.hpp b/include/mapnik/tiff_io.hpp index 97d2d6a32..ecf6faec1 100644 --- a/include/mapnik/tiff_io.hpp +++ b/include/mapnik/tiff_io.hpp @@ -39,8 +39,6 @@ extern "C" #define TIFF_WRITE_STRIPPED 1 #define TIFF_WRITE_TILED 2 -#include - namespace mapnik { static inline tsize_t tiff_write_proc(thandle_t fd, tdata_t buf, tsize_t size) @@ -200,14 +198,12 @@ struct tag_setter TIFFSetField(output_, TIFFTAG_SAMPLESPERPIXEL, 4); if (data.get_premultiplied()) { - //uint16 extras[] = { EXTRASAMPLE_ASSOCALPHA }; - uint16 extras[] = { EXTRASAMPLE_UNASSALPHA }; + uint16 extras[] = { EXTRASAMPLE_ASSOCALPHA }; TIFFSetField(output_, TIFFTAG_EXTRASAMPLES, 1, extras); } else { - //uint16 extras[] = { EXTRASAMPLE_UNASSALPHA }; - uint16 extras[] = { EXTRASAMPLE_ASSOCALPHA }; + uint16 extras[] = { EXTRASAMPLE_UNASSALPHA }; TIFFSetField(output_, TIFFTAG_EXTRASAMPLES, 1, extras); } if (config_.compression == COMPRESSION_DEFLATE @@ -267,7 +263,7 @@ struct tag_setter private: TIFF * output_; - tiff_config config_; + tiff_config & config_; }; inline void set_tiff_config(TIFF* output, tiff_config & config) @@ -348,10 +344,7 @@ void save_as_tiff(T1 & file, T2 const& image, tiff_config & config) TIFFSetField(output, TIFFTAG_ROWSPERSTRIP, rows_per_strip); std::size_t strip_size = width * rows_per_strip; std::unique_ptr strip_buffer(new pixel_type[strip_size]); - //int end_y=(height/rows_per_strip+1)*rows_per_strip; - int end_y=height; - - for (int y=0; y < end_y; y+=rows_per_strip) + for (int y=0; y < height; y+=rows_per_strip) { int ty1 = std::min(height, static_cast(y + rows_per_strip)) - y; int row = y; diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index 1563c3272..83aaed6bd 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -123,7 +123,9 @@ void agg_renderer::setup(Map const &m) } else { - mapnik::fill(pixmap_,*bg); + mapnik::color bg_color = *bg; + bg_color.set_premultiplied(true); + mapnik::fill(pixmap_,bg_color); } } diff --git a/src/color.cpp b/src/color.cpp index f1146e815..51fb19585 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -95,23 +95,28 @@ std::string color::to_hex_string() const return str; } -void color::premultiply() +bool color::premultiply() { + if (premultiplied_) return false; agg::rgba8 pre_c = agg::rgba8(red_,green_,blue_,alpha_); pre_c.premultiply(); red_ = pre_c.r; green_ = pre_c.g; blue_ = pre_c.b; + premultiplied_ = true; + return true; } -void color::demultiply() +bool color::demultiply() { - // note: this darkens too much: https://github.com/mapnik/mapnik/issues/1519 + if (!premultiplied_) return false; agg::rgba8 pre_c = agg::rgba8(red_,green_,blue_,alpha_); pre_c.demultiply(); red_ = pre_c.r; green_ = pre_c.g; blue_ = pre_c.b; + premultiplied_ = false; + return true; } } diff --git a/src/image_compositing.cpp b/src/image_compositing.cpp index ea4908d71..6667bce1c 100644 --- a/src/image_compositing.cpp +++ b/src/image_compositing.cpp @@ -169,7 +169,6 @@ MAPNIK_DECL void composite(image_rgba8 & dst, image_rgba8 const& src, composite_ #ifdef MAPNIK_DEBUG if (!src.get_premultiplied()) { - abort(); throw std::runtime_error("SOURCE MUST BE PREMULTIPLIED FOR COMPOSITING!"); } if (!dst.get_premultiplied()) diff --git a/src/image_util.cpp b/src/image_util.cpp index c5239e5af..02f5fdd32 100644 --- a/src/image_util.cpp +++ b/src/image_util.cpp @@ -783,19 +783,6 @@ struct visitor_fill visitor_fill(T1 const& val) : val_(val) {} - void operator() (image_rgba8 & data) - { - using pixel_type = typename image_rgba8::pixel_type; - bool was_demultiplied = mapnik::demultiply_alpha(data); - pixel_type val = static_cast(val_); - data.set(val); - if (was_demultiplied) - { - mapnik::premultiply_alpha(data); - } - - } - template void operator() (T2 & data) { @@ -817,14 +804,9 @@ struct visitor_fill void operator() (image_rgba8 & data) { using pixel_type = typename image_rgba8::pixel_type; - bool was_demultiplied = mapnik::demultiply_alpha(data); pixel_type val = static_cast(val_.rgba()); data.set(val); - if (was_demultiplied) - { - mapnik::premultiply_alpha(data); - } - + data.set_premultiplied(val_.get_premultiplied()); } template @@ -1069,7 +1051,23 @@ struct visitor_set_pixel void operator() (T2 & data) { using pixel_type = typename T2::pixel_type; - pixel_type val = static_cast(val_.rgba()); + pixel_type val; + if (data.get_premultiplied() && !val_.get_premultiplied()) + { + color tmp(val_); + tmp.premultiply(); + val = static_cast(tmp.rgba()); + } + else if (!data.get_premultiplied() && val_.get_premultiplied()) + { + color tmp(val_); + tmp.demultiply(); + val = static_cast(tmp.rgba()); + } + else + { + val = static_cast(val_.rgba()); + } if (check_bounds(data, x_, y_)) { data(x_, y_) = val; @@ -1163,11 +1161,7 @@ struct visitor_get_pixel { if (check_bounds(data, x_, y_)) { - uint32_t val = static_cast(data(x_, y_)); - return color(static_cast(val), - static_cast((val >>= 8)), - static_cast((val >>= 8)), - static_cast((val >>= 8))); + return color(static_cast(data(x_, y_)), data.get_premultiplied()); } else { diff --git a/src/tiff_reader.cpp b/src/tiff_reader.cpp index 9bbe6fd59..65f405c09 100644 --- a/src/tiff_reader.cpp +++ b/src/tiff_reader.cpp @@ -308,10 +308,10 @@ void tiff_reader::init() { has_alpha_ = true; premultiplied_alpha_ = true; - if (extrasamples == 1 && - sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA) + if (extrasamples > 0 && + sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED) { - premultiplied_alpha_ = false; + throw std::runtime_error("Unspecified provided for extra samples to tiff reader."); } } // Try extracting bounding box from geoTIFF tags @@ -641,15 +641,12 @@ void tiff_reader::read_stripped(unsigned x0,unsigned y0,image_rgba8& image) int height=image.height(); unsigned start_y=(y0/rows_per_strip_)*rows_per_strip_; - //unsigned end_y=((y0+height)/rows_per_strip_+1)*rows_per_strip_; unsigned end_y=std::min(y0+height, static_cast(height_)); - //bool laststrip=(static_cast(end_y) > height_)?true:false; - int row,tx0,tx1,ty0,ty1; + int tx0,tx1,ty0,ty1; - //image.set_premultiplied(true); tx0=x0; tx1=std::min(width+x0,static_cast(width_)); - + int row = 0; for (unsigned y=start_y; y < end_y; y+=rows_per_strip_) { ty0 = std::max(y0,y)-y; @@ -660,18 +657,11 @@ void tiff_reader::read_stripped(unsigned x0,unsigned y0,image_rgba8& image) std::clog << "TIFFReadRGBAStrip failed at " << y << " for " << width_ << "/" << height_ << "\n"; break; } - row=y+ty0; - - //int n0=laststrip ? 0:(rows_per_strip_-ty1); - //int n1=laststrip ? (ty1-ty0-1):(rows_per_strip_-ty0-1); - //std::cout << " n0: " << n0 << " n1: " << n1 << " y: " << y << " endy: " << end_y << std::endl; - //for (int n=n1;n>=n0;--n) - //for (int n=n0;n>=n1;++n) - // This is in reverse becauase the TIFFReadRGBAStrip reads inverted? - for (int ty = ty1-1; ty >= ty0; --ty, ++row) + // This is in reverse becauase the TIFFReadRGBAStrip reads inverted + for (unsigned ty = ty1; ty > ty0; --ty) { - //std::cout << "row: " << row <<" s: " << (tx0-x0) << " e: " << (tx1-x0) << " n: " << (n*width_+tx0) << std::endl; - image.setRow(row,tx0-x0,tx1-x0,&strip.getData()[ty*width_+tx0]); + image.setRow(row,tx0-x0,tx1-x0,&strip.getData()[(ty-1)*width_+tx0]); + ++row; } } } diff --git a/tests/cxx/tiff_io.cpp b/tests/cxx/tiff_io.cpp index da3b6c073..7652824ea 100644 --- a/tests/cxx/tiff_io.cpp +++ b/tests/cxx/tiff_io.cpp @@ -31,7 +31,7 @@ REQUIRE( reader->has_alpha() == true ); \ REQUIRE( tiff_reader2.has_alpha() == true ); \ REQUIRE( reader2->has_alpha() == true ); \ - REQUIRE( data.get_premultiplied() == false ); \ + REQUIRE( data.get_premultiplied() == true ); \ #define TIFF_ASSERT_NO_ALPHA( data ) \ REQUIRE( tiff_reader.has_alpha() == false ); \ diff --git a/tests/python_tests/compositing_test.py b/tests/python_tests/compositing_test.py index fc05525e6..bd4b75fe7 100644 --- a/tests/python_tests/compositing_test.py +++ b/tests/python_tests/compositing_test.py @@ -231,7 +231,7 @@ def test_background_image_with_alpha_and_background_color(): m.background_image = '../data/images/yellow_half_trans.png' im = mapnik.Image(m.width,m.height) mapnik.render(m,im) - eq_(get_unique_colors(im),['rgba(255,255,85,191)']) + eq_(get_unique_colors(im),['rgba(255,255,170,191)']) def test_background_image_with_alpha_and_background_color_against_composited_control(): m = mapnik.Map(10,10) diff --git a/tests/python_tests/image_test.py b/tests/python_tests/image_test.py index a4b944bb9..7b233a233 100644 --- a/tests/python_tests/image_test.py +++ b/tests/python_tests/image_test.py @@ -28,6 +28,137 @@ def test_image_premultiply(): eq_(im.demultiply(), False) eq_(im.premultiplied(),False) +def test_image_premultiply_values(): + im = mapnik.Image(256,256) + im.background(mapnik.Color(16, 33, 255, 128)) + im.premultiply() + c = im.get_pixel_color(0,0) + eq_(c.r, 8) + eq_(c.g, 17) + eq_(c.b, 128) + eq_(c.a, 128) + im.demultiply() + # Do to the nature of this operation the result will not be exactly the same + c = im.get_pixel_color(0,0) + eq_(c.r,15) + eq_(c.g,33) + eq_(c.b,255) + eq_(c.a,128) + +def test_background(): + im = mapnik.Image(256,256) + eq_(im.premultiplied(), False) + im.background(mapnik.Color(32,64,125,128)) + eq_(im.premultiplied(), False) + c = im.get_pixel_color(0,0) + eq_(c.get_premultiplied(), False) + eq_(c.r,32) + eq_(c.g,64) + eq_(c.b,125) + eq_(c.a,128) + # Now again with a premultiplied alpha + im.background(mapnik.Color(32,64,125,128,True)) + eq_(im.premultiplied(), True) + c = im.get_pixel_color(0,0) + eq_(c.get_premultiplied(), True) + eq_(c.r,32) + eq_(c.g,64) + eq_(c.b,125) + eq_(c.a,128) + +def test_set_and_get_pixel(): + # Create an image that is not premultiplied + im = mapnik.Image(256,256) + c0 = mapnik.Color(16,33,255,128) + c0_pre = mapnik.Color(16,33,255,128, True) + im.set_pixel(0,0,c0) + im.set_pixel(1,1,c0_pre) + # No differences for non premultiplied pixels + c1_int = mapnik.Color(im.get_pixel(0,0)) + eq_(c0.r, c1_int.r) + eq_(c0.g, c1_int.g) + eq_(c0.b, c1_int.b) + eq_(c0.a, c1_int.a) + c1 = im.get_pixel_color(0,0) + eq_(c0.r, c1.r) + eq_(c0.g, c1.g) + eq_(c0.b, c1.b) + eq_(c0.a, c1.a) + # The premultiplied Color should be demultiplied before being applied. + c0_pre.demultiply() + c1_int = mapnik.Color(im.get_pixel(1,1)) + eq_(c0_pre.r, c1_int.r) + eq_(c0_pre.g, c1_int.g) + eq_(c0_pre.b, c1_int.b) + eq_(c0_pre.a, c1_int.a) + c1 = im.get_pixel_color(1,1) + eq_(c0_pre.r, c1.r) + eq_(c0_pre.g, c1.g) + eq_(c0_pre.b, c1.b) + eq_(c0_pre.a, c1.a) + + # Now create a new image that is premultiplied + im = mapnik.Image(256,256, mapnik.ImageType.rgba8, True, True) + c0 = mapnik.Color(16,33,255,128) + c0_pre = mapnik.Color(16,33,255,128, True) + im.set_pixel(0,0,c0) + im.set_pixel(1,1,c0_pre) + # It should have put pixels that are the same as premultiplied so premultiply c0 + c0.premultiply() + c1_int = mapnik.Color(im.get_pixel(0,0)) + eq_(c0.r, c1_int.r) + eq_(c0.g, c1_int.g) + eq_(c0.b, c1_int.b) + eq_(c0.a, c1_int.a) + c1 = im.get_pixel_color(0,0) + eq_(c0.r, c1.r) + eq_(c0.g, c1.g) + eq_(c0.b, c1.b) + eq_(c0.a, c1.a) + # The premultiplied Color should be the same though + c1_int = mapnik.Color(im.get_pixel(1,1)) + eq_(c0_pre.r, c1_int.r) + eq_(c0_pre.g, c1_int.g) + eq_(c0_pre.b, c1_int.b) + eq_(c0_pre.a, c1_int.a) + c1 = im.get_pixel_color(1,1) + eq_(c0_pre.r, c1.r) + eq_(c0_pre.g, c1.g) + eq_(c0_pre.b, c1.b) + eq_(c0_pre.a, c1.a) + +@raises(IndexError) +def test_set_pixel_out_of_range_1(): + im = mapnik.Image(4,4) + c = mapnik.Color('blue') + im.set_pixel(5,5,c) + +@raises(OverflowError) +def test_set_pixel_out_of_range_2(): + im = mapnik.Image(4,4) + c = mapnik.Color('blue') + im.set_pixel(-1,1,c) + +@raises(IndexError) +def test_get_pixel_out_of_range_1(): + im = mapnik.Image(4,4) + c = im.get_pixel(5,5) + +@raises(OverflowError) +def test_get_pixel_out_of_range_2(): + im = mapnik.Image(4,4) + c = im.get_pixel(-1,1) + +@raises(IndexError) +def test_get_pixel_color_out_of_range_1(): + im = mapnik.Image(4,4) + c = im.get_pixel_color(5,5) + +@raises(OverflowError) +def test_get_pixel_color_out_of_range_2(): + im = mapnik.Image(4,4) + c = im.get_pixel_color(-1,1) + def test_set_color_to_alpha(): im = mapnik.Image(256,256) im.background(mapnik.Color('rgba(12,12,12,255)')) diff --git a/tests/python_tests/image_tiff_test.py b/tests/python_tests/image_tiff_test.py index 001d297a2..634379fbd 100644 --- a/tests/python_tests/image_tiff_test.py +++ b/tests/python_tests/image_tiff_test.py @@ -45,16 +45,19 @@ def test_tiff_round_trip_stripped(): eq_(im.height(),im3.height()) eq_(len(im.tostring()), org_str) eq_(len(im.tostring()),len(im2.tostring())) - eq_(len(im.tostring('png')),len(im2.tostring('png'))) + # Because one will end up with UNASSOC alpha tag which internally the TIFF will multiply, the first to string will not be the same due to the + # difference in tags. But size should still be the same eq_(len(im.tostring('tiff:method=stripped')),len(im2.tostring('tiff:method=stripped'))) - eq_(len(im.tostring()),len(im3.tostring())) - eq_(len(im.tostring('tiff:method=stripped')),len(im3.tostring('tiff:method=stripped'))) + eq_(len(im2.tostring()),len(im3.tostring())) + # Both of these started out premultiplied, so this round trip should be exactly the same! + eq_(len(im2.tostring('tiff:method=stripped')),len(im3.tostring('tiff:method=stripped'))) def test_tiff_round_trip_rows_stripped(): filepath = '/tmp/mapnik-tiff-io-rows_stripped.tiff' filepath2 = '/tmp/mapnik-tiff-io-rows_stripped2.tiff' im = mapnik.Image(255,267) im.background(mapnik.Color('rgba(12,255,128,.5)')) + #im.premultiply() org_str = len(im.tostring()) im.save(filepath,'tiff:method=stripped:rows_per_strip=8') im2 = mapnik.Image.open(filepath) @@ -66,31 +69,35 @@ def test_tiff_round_trip_rows_stripped(): eq_(im.height(),im3.height()) eq_(len(im.tostring()), org_str) eq_(len(im.tostring()),len(im2.tostring())) + # Because one will end up with UNASSOC alpha tag which internally the TIFF will multiply, the first to string will not be the same due to the + # difference in tags. But size should still be the same eq_(len(im.tostring('tiff:method=stripped:rows_per_strip=8')),len(im2.tostring('tiff:method=stripped:rows_per_strip=8'))) - eq_(len(im.tostring()),len(im3.tostring())) - eq_(len(im.tostring('tiff:method=stripped:rows_per_strip=8')),len(im3.tostring('tiff:method=stripped:rows_per_strip=8'))) + eq_(len(im2.tostring()),len(im3.tostring())) + # Both of these started out premultiplied, so this round trip should be exactly the same! + eq_(len(im2.tostring('tiff:method=stripped:rows_per_strip=8')),len(im3.tostring('tiff:method=stripped:rows_per_strip=8'))) def test_tiff_round_trip_buffered_tiled(): filepath = '/tmp/mapnik-tiff-io-buffered-tiled.tiff' filepath2 = '/tmp/mapnik-tiff-io-buffered-tiled2.tiff' + filepath3 = '/tmp/mapnik-tiff-io-buffered-tiled3.tiff' im = mapnik.Image(255,267) - #im = mapnik.Image(256,256) - im.background(mapnik.Color('rgba(1,255,128,.5)')) + im.background(mapnik.Color('rgba(33,255,128,.5)')) im.save(filepath,'tiff:method=tiled:tile_width=32:tile_height=32') im2 = mapnik.Image.open(filepath) im3 = mapnik.Image.fromstring(open(filepath,'r').read()) im2.save(filepath2, 'tiff:method=tiled:tile_width=32:tile_height=32') - im4 = mapnik.Image.open(filepath2) + im3.save(filepath3, 'tiff:method=tiled:tile_width=32:tile_height=32') eq_(im.width(),im2.width()) eq_(im.height(),im2.height()) eq_(im.width(),im3.width()) eq_(im.height(),im3.height()) - eq_(len(im2.tostring()),len(im4.tostring())) - eq_(len(im2.tostring('tiff:method=tiled:tile_width=32:tile_height=32')),len(im4.tostring('tiff:method=tiled:tile_width=32:tile_height=32'))) eq_(len(im.tostring()),len(im2.tostring())) + # Because one will end up with UNASSOC alpha tag which internally the TIFF will multiply, the first to string will not be the same due to the + # difference in tags. But size should still be the same eq_(len(im.tostring('tiff:method=tiled:tile_width=32:tile_height=32')),len(im2.tostring('tiff:method=tiled:tile_width=32:tile_height=32'))) - eq_(len(im.tostring()),len(im3.tostring())) - eq_(len(im.tostring('tiff:method=tiled:tile_width=32:tile_height=32')),len(im3.tostring('tiff:method=tiled:tile_width=32:tile_height=32'))) + eq_(len(im2.tostring()),len(im3.tostring())) + # Both of these started out premultiplied, so this round trip should be exactly the same! + eq_(len(im2.tostring('tiff:method=tiled:tile_width=32:tile_height=32')),len(im3.tostring('tiff:method=tiled:tile_width=32:tile_height=32'))) def test_tiff_round_trip_tiled(): filepath = '/tmp/mapnik-tiff-io-tiled.tiff' @@ -104,9 +111,12 @@ def test_tiff_round_trip_tiled(): eq_(im.width(),im3.width()) eq_(im.height(),im3.height()) eq_(len(im.tostring()),len(im2.tostring())) + # Because one will end up with UNASSOC alpha tag which internally the TIFF will multiply, the first to string will not be the same due to the + # difference in tags. HOWEVER for this type they have the same size? Perhaps is a bad test? eq_(len(im.tostring('tiff:method=tiled')),len(im2.tostring('tiff:method=tiled'))) - eq_(len(im.tostring()),len(im3.tostring())) - eq_(len(im.tostring('tiff:method=tiled')),len(im3.tostring('tiff:method=tiled'))) + eq_(len(im2.tostring()),len(im3.tostring())) + # Both of these started out premultiplied, so this round trip should be exactly the same! + eq_(len(im2.tostring('tiff:method=tiled')),len(im3.tostring('tiff:method=tiled'))) def test_tiff_rgb8_compare():