Merge pull request #2645 from mapnik/feature/set_alpha_move

Moved set_alpha to src/image_util.cpp and out of image_32. Added a boole...
This commit is contained in:
Blake Thompson 2015-01-15 21:06:06 -06:00
commit ff019cbe28
6 changed files with 282 additions and 104 deletions

View file

@ -36,6 +36,7 @@
// mapnik
#include <mapnik/graphics.hpp>
#include <mapnik/color.hpp>
#include <mapnik/palette.hpp>
#include <mapnik/image.hpp>
#include <mapnik/image_util.hpp>
@ -188,12 +189,19 @@ std::shared_ptr<image_32> frombuffer(PyObject * obj)
throw mapnik::image_reader_exception("Failed to load image from buffer" );
}
void blend (image_32 & im, unsigned x, unsigned y, image_32 & im2, float opacity)
void set_grayscale_to_alpha(image_32 & im)
{
mapnik::premultiply_alpha(im.data());
mapnik::premultiply_alpha(im2.data());
mapnik::composite(im.data(),im2.data(),mapnik::src_over,opacity,x,y);
mapnik::set_grayscale_to_alpha(im.data());
}
void set_color_to_alpha(image_32 & im, mapnik::color const& c)
{
mapnik::set_color_to_alpha(im.data(), c);
}
void set_alpha(image_32 & im, float opacity)
{
mapnik::set_alpha(im.data(), opacity);
}
bool premultiplied(image_32 &im)
@ -201,21 +209,29 @@ bool premultiplied(image_32 &im)
return im.data().get_premultiplied();
}
void premultiply(image_32 & im)
bool premultiply(image_32 & im)
{
mapnik::premultiply_alpha(im.data());
return mapnik::premultiply_alpha(im.data());
}
void demultiply(image_32 & im)
bool demultiply(image_32 & im)
{
mapnik::demultiply_alpha(im.data());
return mapnik::demultiply_alpha(im.data());
}
void composite(image_32 & dst, image_32 & src, mapnik::composite_mode_e mode, float opacity)
void composite(image_32 & dst, image_32 & src, mapnik::composite_mode_e mode, float opacity, int dx, int dy)
{
mapnik::premultiply_alpha(dst.data());
mapnik::premultiply_alpha(src.data());
mapnik::composite(dst.data(),src.data(),mode,opacity,0,0);
bool demultiply_dst = mapnik::premultiply_alpha(dst.data());
bool demultiply_src = mapnik::premultiply_alpha(src.data());
mapnik::composite(dst.data(),src.data(),mode,opacity,dx,dy);
if (demultiply_dst)
{
mapnik::demultiply_alpha(dst.data());
}
if (demultiply_src)
{
mapnik::demultiply_alpha(src.data());
}
}
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
@ -303,15 +319,16 @@ void export_image()
.add_property("background",make_function
(&image_32::get_background,return_value_policy<copy_const_reference>()),
&image_32::set_background, "The background color of the image.")
.def("set_grayscale_to_alpha",&image_32::set_grayscale_to_alpha, "Set the grayscale values to the alpha channel of the Image")
.def("set_color_to_alpha",&image_32::set_color_to_alpha, "Set a given color to the alpha channel of the Image")
.def("set_alpha",&image_32::set_alpha, "Set the overall alpha channel of the Image")
.def("blend",&blend)
.def("set_grayscale_to_alpha",&set_grayscale_to_alpha, "Set the grayscale values to the alpha channel of the Image")
.def("set_color_to_alpha",&set_color_to_alpha, "Set a given color to the alpha channel of the Image")
.def("set_alpha",&set_alpha, "Set the overall alpha channel of the Image")
.def("composite",&composite,
( arg("self"),
arg("image"),
arg("mode")=mapnik::src_over,
arg("opacity")=1.0f
arg("opacity")=1.0f,
arg("dx")=0,
arg("dy")=0
))
.def("premultiplied",&premultiplied)
.def("premultiply",&premultiply)

View file

@ -74,12 +74,6 @@ public:
void set_background(const color& c);
void set_grayscale_to_alpha();
void set_color_to_alpha(color const& c);
void set_alpha(float opacity);
inline const image_data_rgba8& data() const
{
return data_;

View file

@ -28,6 +28,7 @@
#include <mapnik/image_data.hpp>
#include <mapnik/image_view.hpp>
#include <mapnik/util/variant.hpp>
#include <mapnik/color.hpp>
// boost
#pragma GCC diagnostic push
@ -111,10 +112,10 @@ MAPNIK_DECL void save_to_stream
);
template <typename T>
MAPNIK_DECL void premultiply_alpha(T & image);
MAPNIK_DECL bool premultiply_alpha(T & image);
template <typename T>
MAPNIK_DECL void demultiply_alpha(T & image);
MAPNIK_DECL bool demultiply_alpha(T & image);
template <typename T>
MAPNIK_DECL void set_premultiplied_alpha(T & image, bool status);
@ -122,6 +123,15 @@ MAPNIK_DECL void set_premultiplied_alpha(T & image, bool status);
template <typename T>
MAPNIK_DECL bool is_solid (T const& image);
template <typename T>
MAPNIK_DECL void set_alpha (T & image, float opacity);
template <typename T>
MAPNIK_DECL void set_grayscale_to_alpha (T & image);
template <typename T>
MAPNIK_DECL void set_color_to_alpha (T & image, color const& c);
inline bool is_png(std::string const& filename)
{
return boost::algorithm::iends_with(filename,std::string(".png"));

View file

@ -53,67 +53,6 @@ image_32::image_32(image_data_rgba8 && data)
image_32::~image_32() {}
void image_32::set_grayscale_to_alpha()
{
for (unsigned int y = 0; y < data_.height(); ++y)
{
unsigned int* row_from = data_.getRow(y);
for (unsigned int x = 0; x < data_.width(); ++x)
{
unsigned rgba = row_from[x];
unsigned r = rgba & 0xff;
unsigned g = (rgba >> 8 ) & 0xff;
unsigned b = (rgba >> 16) & 0xff;
// magic numbers for grayscale
unsigned a = static_cast<unsigned>(std::ceil((r * .3) + (g * .59) + (b * .11)));
row_from[x] = (a << 24)| (255 << 16) | (255 << 8) | (255) ;
}
}
}
void image_32::set_color_to_alpha(const color& c)
{
for (unsigned y = 0; y < data_.height(); ++y)
{
unsigned int* row_from = data_.getRow(y);
for (unsigned x = 0; x < data_.width(); ++x)
{
unsigned rgba = row_from[x];
unsigned r = rgba & 0xff;
unsigned g = (rgba >> 8 ) & 0xff;
unsigned b = (rgba >> 16) & 0xff;
if (r == c.red() && g == c.green() && b == c.blue())
{
row_from[x] = 0;
}
}
}
}
void image_32::set_alpha(float opacity)
{
for (unsigned int y = 0; y < data_.height(); ++y)
{
unsigned int* row_to = data_.getRow(y);
for (unsigned int x = 0; x < data_.width(); ++x)
{
unsigned rgba = row_to[x];
unsigned a0 = (rgba >> 24) & 0xff;
unsigned a1 = int( ((rgba >> 24) & 0xff) * opacity );
//unsigned a1 = opacity;
if (a0 == a1) continue;
unsigned r = rgba & 0xff;
unsigned g = (rgba >> 8 ) & 0xff;
unsigned b = (rgba >> 16) & 0xff;
row_to[x] = (a1 << 24)| (b << 16) | (g << 8) | (r) ;
}
}
}
void image_32::set_background(const color& c)
{
background_=c;

View file

@ -33,6 +33,7 @@
#include <mapnik/image_view.hpp>
#include <mapnik/palette.hpp>
#include <mapnik/map.hpp>
#include <mapnik/color.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/util/variant.hpp>
@ -473,7 +474,7 @@ namespace detail {
struct premultiply_visitor
{
template <typename T>
void operator() (T & data)
bool operator() (T & data)
{
throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported");
}
@ -481,7 +482,7 @@ struct premultiply_visitor
};
template <>
void premultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
bool premultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
if (!data.get_premultiplied())
{
@ -489,13 +490,15 @@ void premultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
data.set_premultiplied(true);
return true;
}
return false;
}
struct demultiply_visitor
{
template <typename T>
void operator() (T & data)
bool operator() (T & data)
{
throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported");
}
@ -503,7 +506,7 @@ struct demultiply_visitor
};
template <>
void demultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
bool demultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
if (data.get_premultiplied())
{
@ -511,7 +514,9 @@ void demultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
agg::pixfmt_rgba32_pre pixf(buffer);
pixf.demultiply();
data.set_premultiplied(false);
return true;
}
return false;
}
struct set_premultiplied_visitor
@ -531,35 +536,35 @@ struct set_premultiplied_visitor
} // end detail ns
template <typename T>
MAPNIK_DECL void premultiply_alpha(T & image)
MAPNIK_DECL bool premultiply_alpha(T & image)
{
util::apply_visitor(detail::premultiply_visitor(), image);
return util::apply_visitor(detail::premultiply_visitor(), image);
}
template void premultiply_alpha<image_data_any> (image_data_any &);
template bool premultiply_alpha<image_data_any> (image_data_any &);
// Temporary, can be removed once image_view_any and image_data_any are the only ones passed
template <>
MAPNIK_DECL void premultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
MAPNIK_DECL bool premultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
{
detail::premultiply_visitor visit;
visit(image);
return visit(image);
}
template <typename T>
MAPNIK_DECL void demultiply_alpha(T & image)
MAPNIK_DECL bool demultiply_alpha(T & image)
{
util::apply_visitor(detail::demultiply_visitor(), image);
return util::apply_visitor(detail::demultiply_visitor(), image);
}
template void demultiply_alpha<image_data_any> (image_data_any &);
template bool demultiply_alpha<image_data_any> (image_data_any &);
// Temporary, can be removed once image_view_any and image_data_any are the only ones passed
template <>
MAPNIK_DECL void demultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
MAPNIK_DECL bool demultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
{
detail::demultiply_visitor visit;
visit(image);
return visit(image);
}
template <typename T>
@ -578,4 +583,202 @@ MAPNIK_DECL void set_premultiplied_alpha<image_data_rgba8>(image_data_rgba8 & im
visit(image);
}
namespace detail {
struct visitor_set_alpha
{
visitor_set_alpha(float opacity)
: opacity_(opacity) {}
template <typename T>
void operator() (T & data)
{
throw std::runtime_error("Error: set_alpha with " + std::string(typeid(data).name()) + " is not supported");
}
private:
float opacity_;
};
template <>
void visitor_set_alpha::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
using pixel_type = typename image_data_rgba8::pixel_type;
for (unsigned int y = 0; y < data.height(); ++y)
{
pixel_type* row_to = data.getRow(y);
for (unsigned int x = 0; x < data.width(); ++x)
{
pixel_type rgba = row_to[x];
pixel_type a0 = (rgba >> 24) & 0xff;
pixel_type a1 = pixel_type( ((rgba >> 24) & 0xff) * opacity_ );
//unsigned a1 = opacity;
if (a0 == a1) continue;
pixel_type r = rgba & 0xff;
pixel_type g = (rgba >> 8 ) & 0xff;
pixel_type b = (rgba >> 16) & 0xff;
row_to[x] = (a1 << 24)| (b << 16) | (g << 8) | (r) ;
}
}
}
} // end detail ns
template<>
MAPNIK_DECL void set_alpha<image_data_any> (image_data_any & data, float opacity)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
util::apply_visitor(detail::visitor_set_alpha(opacity), data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
// TEMPORARY can be removed once image_data_any is only way it is being passed.
template<>
MAPNIK_DECL void set_alpha<image_data_rgba8> (image_data_rgba8 & data, float opacity)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
detail::visitor_set_alpha visit(opacity);
visit(data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
namespace detail {
struct visitor_set_grayscale_to_alpha
{
template <typename T>
void operator() (T & data)
{
throw std::runtime_error("Error: set_grayscale_to_alpha with " + std::string(typeid(data).name()) + " is not supported");
}
};
template <>
void visitor_set_grayscale_to_alpha::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
using pixel_type = typename image_data_rgba8::pixel_type;
for (unsigned int y = 0; y < data.height(); ++y)
{
pixel_type* row_from = data.getRow(y);
for (unsigned int x = 0; x < data.width(); ++x)
{
pixel_type rgba = row_from[x];
pixel_type r = rgba & 0xff;
pixel_type g = (rgba >> 8 ) & 0xff;
pixel_type b = (rgba >> 16) & 0xff;
// magic numbers for grayscale
pixel_type a = static_cast<pixel_type>(std::ceil((r * .3) + (g * .59) + (b * .11)));
row_from[x] = (a << 24)| (255 << 16) | (255 << 8) | (255) ;
}
}
}
} // end detail ns
template<>
MAPNIK_DECL void set_grayscale_to_alpha<image_data_any> (image_data_any & data)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
util::apply_visitor(detail::visitor_set_grayscale_to_alpha(), data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
// TEMPORARY can be removed once image_data_any is only way it is being passed.
template<>
MAPNIK_DECL void set_grayscale_to_alpha<image_data_rgba8> (image_data_rgba8 & data)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
detail::visitor_set_grayscale_to_alpha visit;
visit(data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
namespace detail {
struct visitor_set_color_to_alpha
{
visitor_set_color_to_alpha(color const& c)
: c_(c) {}
template <typename T>
void operator() (T & data)
{
throw std::runtime_error("Error: set_color_to_alpha with " + std::string(typeid(data).name()) + " is not supported");
}
private:
color const& c_;
};
template <>
void visitor_set_color_to_alpha::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
using pixel_type = typename image_data_rgba8::pixel_type;
for (unsigned y = 0; y < data.height(); ++y)
{
pixel_type* row_from = data.getRow(y);
for (unsigned x = 0; x < data.width(); ++x)
{
pixel_type rgba = row_from[x];
pixel_type r = rgba & 0xff;
pixel_type g = (rgba >> 8 ) & 0xff;
pixel_type b = (rgba >> 16) & 0xff;
if (r == c_.red() && g == c_.green() && b == c_.blue())
{
row_from[x] = 0;
}
}
}
}
} // end detail ns
template<>
MAPNIK_DECL void set_color_to_alpha<image_data_any> (image_data_any & data, color const& c)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
util::apply_visitor(detail::visitor_set_color_to_alpha(c), data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
// TEMPORARY can be removed once image_data_any is only way it is being passed.
template<>
MAPNIK_DECL void set_color_to_alpha<image_data_rgba8> (image_data_rgba8 & data, color const& c)
{
// Prior to calling the data must not be premultiplied
bool remultiply = mapnik::demultiply_alpha(data);
detail::visitor_set_color_to_alpha visit(c);
visit(data);
if (remultiply)
{
mapnik::premultiply_alpha(data);
}
}
} // end ns

View file

@ -5,7 +5,7 @@ import sys
import os, mapnik
from timeit import Timer, time
from nose.tools import *
from utilities import execution_path, run_all
from utilities import execution_path, run_all, get_unique_colors
def setup():
# All of the paths used are relative, if we run the tests
@ -15,10 +15,25 @@ def setup():
def test_image_premultiply():
im = mapnik.Image(256,256)
eq_(im.premultiplied(),False)
im.premultiply()
# Premultiply should return true that it worked
eq_(im.premultiply(), True)
eq_(im.premultiplied(),True)
im.demultiply()
# Premultipling again should return false as nothing should happen
eq_(im.premultiply(), False)
eq_(im.premultiplied(),True)
# Demultiply should return true that it worked
eq_(im.demultiply(), True)
eq_(im.premultiplied(),False)
# Demultiply again should not work and return false as it did nothing
eq_(im.demultiply(), False)
eq_(im.premultiplied(),False)
def test_set_color_to_alpha():
im = mapnik.Image(256,256)
im.background = mapnik.Color('rgba(12,12,12,255)')
eq_(get_unique_colors(im), ['rgba(12,12,12,255)'])
im.set_color_to_alpha(mapnik.Color('rgba(12,12,12,0)'))
eq_(get_unique_colors(im), ['rgba(0,0,0,0)'])
@raises(RuntimeError)
def test_negative_image_dimensions():