Moved premultiply and demultiply out of image_32 and other parts of the code. The image_data object is now responsible for keeping track of its own premultiplied_alpha status. Created a new utility method in image_util to preform premultiplication.

Added visitor pattern to several different methods as well to prepare for image_data_any including compositing.

Ref #2633
This commit is contained in:
Blake Thompson 2015-01-14 12:42:30 -06:00
parent f58f65ee4c
commit 4184f75011
24 changed files with 445 additions and 152 deletions

View file

@ -211,9 +211,24 @@ void blend (image_32 & im, unsigned x, unsigned y, image_32 const& im2, float op
im.set_rectangle_alpha2(im2.data(),x,y,opacity);
}
bool premultiplied(image_32 &im)
{
return im.data().get_premultiplied();
}
void premultiply(image_32 & im)
{
mapnik::premultiply_alpha(im.data());
}
void demultiply(image_32 & im)
{
mapnik::demultiply_alpha(im.data());
}
void composite(image_32 & dst, image_32 & src, mapnik::composite_mode_e mode, float opacity)
{
mapnik::composite(dst.data(),src.data(),mode,opacity,0,0,false);
mapnik::composite(dst.data(),src.data(),mode,opacity,0,0);
}
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
@ -311,9 +326,9 @@ void export_image()
arg("mode")=mapnik::src_over,
arg("opacity")=1.0f
))
.def("premultiplied",&image_32::premultiplied)
.def("premultiply",&image_32::premultiply)
.def("demultiply",&image_32::demultiply)
.def("premultiplied",&premultiplied)
.def("premultiply",&premultiply)
.def("demultiply",&demultiply)
.def("set_pixel",&set_pixel)
.def("get_pixel",&get_pixel)
.def("clear",&image_32::clear)

View file

@ -48,7 +48,6 @@ private:
image_data_rgba8 data_;
boost::optional<color> background_;
bool painted_;
bool premultiplied_;
public:
using pixel_type = typename image_data_rgba8::pixel_type;
image_32(int width,int height);
@ -65,11 +64,6 @@ public:
return painted_;
}
bool premultiplied() const
{
return premultiplied_;
}
inline void clear()
{
std::fill(data_.getData(), data_.getData() + data_.width() * data_.height(), 0);
@ -79,10 +73,6 @@ public:
void set_background(const color& c);
void premultiply();
void demultiply();
void set_grayscale_to_alpha();
void set_color_to_alpha(color const& c);

View file

@ -87,8 +87,7 @@ MAPNIK_DECL void composite(T & dst, T const& src,
composite_mode_e mode,
float opacity=1,
int dx=0,
int dy=0,
bool premultiply_src=false);
int dy=0);
}
#endif // MAPNIK_IMAGE_COMPOSITING_HPP

View file

@ -122,10 +122,11 @@ public:
using pixel_type = T;
static constexpr std::size_t pixel_size = sizeof(pixel_type);
image_data(int width, int height, bool initialize = true)
image_data(int width, int height, bool initialize = true, bool premultiplied = false)
: dimensions_(width, height),
buffer_(dimensions_.width() * dimensions_.height() * pixel_size),
pData_(reinterpret_cast<pixel_type*>(buffer_.data()))
pData_(reinterpret_cast<pixel_type*>(buffer_.data())),
premultiplied_alpha_(premultiplied)
{
if (pData_ && initialize) std::fill(pData_, pData_ + dimensions_.width() * dimensions_.height(), 0);
}
@ -133,13 +134,15 @@ public:
image_data(image_data<pixel_type> const& rhs)
: dimensions_(rhs.dimensions_),
buffer_(rhs.buffer_),
pData_(reinterpret_cast<pixel_type*>(buffer_.data()))
pData_(reinterpret_cast<pixel_type*>(buffer_.data())),
premultiplied_alpha_(rhs.premultiplied_alpha_)
{}
image_data(image_data<pixel_type> && rhs) noexcept
: dimensions_(std::move(rhs.dimensions_)),
buffer_(std::move(rhs.buffer_)),
pData_(reinterpret_cast<pixel_type*>(buffer_.data()))
buffer_(std::move(rhs.buffer_)),
pData_(reinterpret_cast<pixel_type*>(buffer_.data())),
premultiplied_alpha_(std::move(rhs.premultiplied_alpha_))
{
rhs.dimensions_ = { 0, 0 };
rhs.pData_ = nullptr;
@ -241,10 +244,21 @@ public:
std::copy(buf, buf + (x1 - x0), pData_ + row * dimensions_.width() + x0);
}
inline bool get_premultiplied() const
{
return premultiplied_alpha_;
}
inline void set_premultiplied(bool set)
{
premultiplied_alpha_ = set;
}
private:
detail::image_dimensions<max_size> dimensions_;
detail::buffer buffer_;
pixel_type *pData_;
bool premultiplied_alpha_;
};
using image_data_rgba8 = image_data<std::uint32_t>;

View file

@ -34,6 +34,7 @@ struct image_data_null
unsigned char* getBytes() { return nullptr;}
std::size_t width() const { return 0; }
std::size_t height() const { return 0; }
bool get_premultiplied() const { return false; }
};
using image_data_base = util::variant<image_data_null,
@ -80,6 +81,14 @@ struct get_height_visitor
}
};
struct get_premultiplied_visitor
{
template <typename T>
bool operator()(T const& data) const
{
return data.get_premultiplied();
}
};
} // namespace detail
struct image_data_any : image_data_base
@ -109,6 +118,11 @@ struct image_data_any : image_data_base
{
return util::apply_visitor(detail::get_height_visitor(),*this);
}
bool get_premultiplied() const
{
return util::apply_visitor(detail::get_premultiplied_visitor(),*this);
}
};
}

View file

@ -391,11 +391,11 @@ template <typename Src, typename Filter>
void apply_filter(Src & src, Filter const& filter)
{
{
src.demultiply();
demultiply_alpha(src.data());
double_buffer<Src> tb(src);
apply_convolution_3x3(tb.src_view, tb.dst_view, filter);
} // ensure ~double_buffer() is called before premultiplying
src.premultiply();
premultiply_alpha(src.data());
}
template <typename Src>

View file

@ -59,7 +59,6 @@ struct MAPNIK_DECL image_reader : private util::noncopyable
virtual unsigned width() const = 0;
virtual unsigned height() const = 0;
virtual bool has_alpha() const = 0;
virtual bool premultiplied_alpha() const = 0;
virtual boost::optional<box2d<double> > bounding_box() const = 0;
virtual void read(unsigned x,unsigned y,image_data_rgba8& image) = 0;
virtual image_data_any read(unsigned x, unsigned y, unsigned width, unsigned height) = 0;

View file

@ -109,6 +109,12 @@ MAPNIK_DECL void save_to_stream
std::string const& type
);
template <typename T>
MAPNIK_DECL void premultiply_alpha(T & image);
template <typename T>
MAPNIK_DECL void demultiply_alpha(T & image);
template <typename T>
void save_as_png(T const& image,
std::string const& filename,

View file

@ -39,18 +39,15 @@ public:
box2d<double> ext_;
image_data_any data_;
double filter_factor_;
bool premultiplied_alpha_;
boost::optional<double> nodata_;
template <typename ImageData>
raster(box2d<double> const& ext,
ImageData && data,
double filter_factor,
bool premultiplied_alpha = false)
double filter_factor)
: ext_(ext),
data_(std::move(data)),
filter_factor_(filter_factor),
premultiplied_alpha_(premultiplied_alpha) {}
filter_factor_(filter_factor) {}
void set_nodata(double nodata)
{

View file

@ -24,6 +24,7 @@
#define MAPNIK_RENDERER_COMMON_PROCESS_RASTER_SYMBOLIZER_HPP
// mapnik
#include <mapnik/image_util.hpp>
#include <mapnik/warp.hpp>
#include <mapnik/raster.hpp>
#include <mapnik/symbolizer.hpp>
@ -204,22 +205,7 @@ void render_raster_symbolizer(raster_symbolizer const& sym,
// only premultiply rgba8 images
if (source->data_.is<image_data_rgba8>())
{
bool premultiply_source = !source->premultiplied_alpha_;
auto is_premultiplied = get_optional<bool>(sym, keys::premultiplied, feature, common.vars_);
if (is_premultiplied)
{
if (*is_premultiplied) premultiply_source = false;
else premultiply_source = true;
}
if (premultiply_source)
{
agg::rendering_buffer buffer(source->data_.getBytes(),
source->data_.width(),
source->data_.height(),
source->data_.width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
}
mapnik::premultiply_alpha(source->data_);
}
if (!prj_trans.equal())

View file

@ -199,7 +199,7 @@ mapnik::raster_ptr read_data_band(mapnik::box2d<double> const& bbox,
data[off] = val;
}
}
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0, true);
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0);
if ( hasnodata ) raster->set_nodata(val);
return raster;
}
@ -271,7 +271,7 @@ mapnik::raster_ptr read_grayscale_band(mapnik::box2d<double> const& bbox,
uint16_t width, uint16_t height,
bool hasnodata, T reader)
{
mapnik::image_data_rgba8 image(width,height);
mapnik::image_data_rgba8 image(width,height, true, true);
// Start with plain white (ABGR or RGBA depending on endiannes)
// TODO: set to transparent instead?
image.set(0xffffffff);
@ -292,7 +292,7 @@ mapnik::raster_ptr read_grayscale_band(mapnik::box2d<double> const& bbox,
data[off+2] = val;
}
}
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0, true);
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0);
if ( hasnodata ) raster->set_nodata(val);
return raster;
}
@ -352,7 +352,7 @@ mapnik::raster_ptr pgraster_wkb_reader::read_grayscale(mapnik::box2d<double> con
mapnik::raster_ptr pgraster_wkb_reader::read_rgba(mapnik::box2d<double> const& bbox,
uint16_t width, uint16_t height)
{
mapnik::image_data_rgba8 image(width, height);
mapnik::image_data_rgba8 image(width, height, true, true);
// Start with plain white (ABGR or RGBA depending on endiannes)
image.set(0xffffffff);
@ -400,7 +400,7 @@ mapnik::raster_ptr pgraster_wkb_reader::read_rgba(mapnik::box2d<double> const& b
}
}
}
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0, true);
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(bbox, image, 1.0);
raster->set_nodata(0xffffffff);
return raster;
}

View file

@ -116,7 +116,6 @@ feature_ptr raster_featureset<LookupPolicy>::next()
intersect = t.backward(feature_raster_extent);
mapnik::image_data_any data = reader->read(x_off, y_off, width, height);
mapnik::raster_ptr raster = std::make_shared<mapnik::raster>(intersect, std::move(data), 1.0);
raster->premultiplied_alpha_ = reader->premultiplied_alpha();
feature->set_raster(raster);
}
}

View file

@ -145,7 +145,7 @@ void agg_renderer<T0,T1>::setup(Map const &m)
{
for (unsigned y=0;y<y_steps;++y)
{
composite(pixmap_.data(),*bg_image, m.background_image_comp_op(), m.background_image_opacity(), x*w, y*h, false);
composite(pixmap_.data(),*bg_image, m.background_image_comp_op(), m.background_image_opacity(), x*w, y*h);
}
}
}
@ -285,14 +285,14 @@ void agg_renderer<T0,T1>::end_style_processing(feature_type_style const& st)
composite(pixmap_.data(), current_buffer_->data(),
*st.comp_op(), st.get_opacity(),
-common_.t_.offset(),
-common_.t_.offset(), false);
-common_.t_.offset());
}
else if (blend_from || st.get_opacity() < 1.0)
{
composite(pixmap_.data(), current_buffer_->data(),
src_over, st.get_opacity(),
-common_.t_.offset(),
-common_.t_.offset(), false);
-common_.t_.offset());
}
}
// apply any 'direct' image filters
@ -376,8 +376,7 @@ void agg_renderer<T0,T1>::render_marker(pixel_position const& pos,
composite(current_buffer_->data(), **marker.get_bitmap_data(),
comp_op, opacity,
std::floor(pos.x - cx + .5),
std::floor(pos.y - cy + .5),
false);
std::floor(pos.y - cy + .5));
}
else
{

View file

@ -55,7 +55,7 @@ void agg_renderer<T0,T1>::process(raster_symbolizer const& sym,
[&](image_data_rgba8 & target, composite_mode_e comp_op, double opacity,
int start_x, int start_y) {
composite(current_buffer_->data(), target,
comp_op, opacity, start_x, start_y, false);
comp_op, opacity, start_x, start_y);
}
);
}

View file

@ -40,14 +40,12 @@ namespace mapnik
{
image_32::image_32(int width,int height)
: data_(width,height),
painted_(false),
premultiplied_(false) {}
painted_(false) {}
image_32::image_32(image_32 const& rhs)
: data_(rhs.data_),
painted_(rhs.painted_),
premultiplied_(rhs.premultiplied_) {}
painted_(rhs.painted_) {}
image_32::~image_32() {}
@ -123,22 +121,6 @@ boost::optional<color> const& image_32::get_background() const
return background_;
}
void image_32::premultiply()
{
agg::rendering_buffer buffer(data_.getBytes(),data_.width(),data_.height(),data_.width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
premultiplied_ = true;
}
void image_32::demultiply()
{
agg::rendering_buffer buffer(data_.getBytes(),data_.width(),data_.height(),data_.width() * 4);
agg::pixfmt_rgba32_pre pixf(buffer);
pixf.demultiply();
premultiplied_ = false;
}
void image_32::composite_pixel(unsigned op, int x,int y, unsigned c, unsigned cover, double opacity)
{
using color_type = agg::rgba8;

View file

@ -23,6 +23,7 @@
// mapnik
#include <mapnik/image_compositing.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/image_data_any.hpp>
// boost
#pragma GCC diagnostic push
@ -145,14 +146,13 @@ struct rendering_buffer
image_data_type const& data_;
};
}
} // end detail ns
template <>
MAPNIK_DECL void composite(image_data_rgba8 & dst, image_data_rgba8 const& src, composite_mode_e mode,
float opacity,
int dx,
int dy,
bool premultiply_src)
int dy)
{
using color = agg::rgba8;
using order = agg::order_rgba;
@ -166,7 +166,7 @@ MAPNIK_DECL void composite(image_data_rgba8 & dst, image_data_rgba8 const& src,
pixfmt_type pixf(dst_buffer);
pixf.comp_op(static_cast<agg::comp_op_e>(mode));
agg::pixfmt_alpha_blend_rgba<agg::blender_rgba32, const_rendering_buffer, agg::pixel32_type> pixf_mask(src_buffer);
if (premultiply_src) pixf_mask.premultiply();
if (!src.get_premultiplied()) pixf_mask.premultiply();
renderer_type ren(pixf);
ren.blend_from(pixf_mask,0,dx,dy,unsigned(255*opacity));
}
@ -175,8 +175,7 @@ template <>
MAPNIK_DECL void composite(image_data_gray32f & dst, image_data_gray32f const& src, composite_mode_e mode,
float opacity,
int dx,
int dy,
bool premultiply_src)
int dy)
{
using const_rendering_buffer = detail::rendering_buffer<image_data_gray32f>;
using src_pixfmt_type = agg::pixfmt_alpha_blend_gray<agg::blender_gray<agg::gray32>, const_rendering_buffer, 1, 0>;
@ -191,4 +190,59 @@ MAPNIK_DECL void composite(image_data_gray32f & dst, image_data_gray32f const& s
ren.copy_from(pixf_mask,0,dx,dy);
}
namespace detail {
struct composite_visitor
{
composite_visitor(image_data_any const& src,
composite_mode_e mode,
float opacity,
int dx,
int dy)
: src_(src),
mode_(mode),
opacity_(opacity),
dx_(dx),
dy_(dy) {}
template <typename T>
void operator() (T & dst);
private:
image_data_any const& src_;
composite_mode_e mode_;
float opacity_;
int dx_;
int dy_;
};
template <typename T>
void composite_visitor::operator() (T & dst)
{
throw std::runtime_error("Error: Composite with " + std::string(typeid(dst).name()) + " is not supported");
}
template <>
void composite_visitor::operator()<image_data_rgba8> (image_data_rgba8 & dst)
{
composite(dst, util::get<image_data_rgba8>(src_), mode_, opacity_, dx_, dy_);
}
template <>
void composite_visitor::operator()<image_data_gray32f> (image_data_gray32f & dst)
{
composite(dst, util::get<image_data_gray32f>(src_), mode_, opacity_, dx_, dy_);
}
} // end ns
template <>
MAPNIK_DECL void composite(image_data_any & dst, image_data_any const& src, composite_mode_e mode,
float opacity,
int dx,
int dy)
{
util::apply_visitor(detail::composite_visitor(src, mode, opacity, dx, dy), dst);
}
}

View file

@ -27,6 +27,7 @@
#include <mapnik/image_util_tiff.hpp>
#include <mapnik/image_util_webp.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/image_data_any.hpp>
#include <mapnik/memory.hpp>
#include <mapnik/image_view.hpp>
#include <mapnik/palette.hpp>
@ -37,6 +38,11 @@
// boost
#include <boost/tokenizer.hpp>
// agg
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgba.h"
#include "agg_color_rgba.h"
// stl
#include <string>
#include <iostream>
@ -75,7 +81,7 @@ void save_to_file(T const& image,
std::ofstream file (filename.c_str(), std::ios::out| std::ios::trunc|std::ios::binary);
if (file)
{
save_to_stream(image, file, type, palette);
save_to_stream<T>(image, file, type, palette);
}
else throw ImageWriterException("Could not write file to " + filename );
}
@ -88,7 +94,7 @@ void save_to_file(T const& image,
std::ofstream file (filename.c_str(), std::ios::out| std::ios::trunc|std::ios::binary);
if (file)
{
save_to_stream(image, file, type);
save_to_stream<T>(image, file, type);
}
else throw ImageWriterException("Could not write file to " + filename );
}
@ -98,6 +104,67 @@ void save_to_stream(T const& image,
std::ostream & stream,
std::string const& type,
rgba_palette const& palette)
{
if (stream && image.width() > 0 && image.height() > 0)
{
std::string t = type;
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
if (t == "png" || boost::algorithm::starts_with(t, "png"))
{
png_saver_pal visitor(stream, t, palette);
mapnik::util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "tif"))
{
throw ImageWriterException("palettes are not currently supported when writing to tiff format (yet)");
}
else if (boost::algorithm::starts_with(t, "jpeg"))
{
throw ImageWriterException("palettes are not currently supported when writing to jpeg format");
}
else throw ImageWriterException("unknown file type: " + type);
}
else throw ImageWriterException("Could not write to empty stream" );
}
// This can be removed once image_data_any and image_view_any are the only
// items using this template
template <>
void save_to_stream<image_data_rgba8>(image_data_rgba8 const& image,
std::ostream & stream,
std::string const& type,
rgba_palette const& palette)
{
if (stream && image.width() > 0 && image.height() > 0)
{
std::string t = type;
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
if (t == "png" || boost::algorithm::starts_with(t, "png"))
{
png_saver_pal visitor(stream, t, palette);
visitor(image);
//mapnik::util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "tif"))
{
throw ImageWriterException("palettes are not currently supported when writing to tiff format (yet)");
}
else if (boost::algorithm::starts_with(t, "jpeg"))
{
throw ImageWriterException("palettes are not currently supported when writing to jpeg format");
}
else throw ImageWriterException("unknown file type: " + type);
}
else throw ImageWriterException("Could not write to empty stream" );
}
// This can be removed once image_data_any and image_view_any are the only
// items using this template
template <>
void save_to_stream<image_view_rgba8>(image_view_rgba8 const& image,
std::ostream & stream,
std::string const& type,
rgba_palette const& palette)
{
if (stream && image.width() > 0 && image.height() > 0)
{
@ -126,6 +193,82 @@ template <typename T>
void save_to_stream(T const& image,
std::ostream & stream,
std::string const& type)
{
if (stream && image.width() > 0 && image.height() > 0)
{
std::string t = type;
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
if (t == "png" || boost::algorithm::starts_with(t, "png"))
{
png_saver visitor(stream, t);
util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "tif"))
{
tiff_saver visitor(stream, t);
util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "jpeg"))
{
jpeg_saver visitor(stream, t);
util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "webp"))
{
webp_saver visitor(stream, t);
util::apply_visitor(visitor, image);
}
else throw ImageWriterException("unknown file type: " + type);
}
else throw ImageWriterException("Could not write to empty stream" );
}
// This can be removed once image_data_any and image_view_any are the only
// items using this template
template <>
void save_to_stream<image_data_rgba8>(image_data_rgba8 const& image,
std::ostream & stream,
std::string const& type)
{
if (stream && image.width() > 0 && image.height() > 0)
{
std::string t = type;
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
if (t == "png" || boost::algorithm::starts_with(t, "png"))
{
png_saver visitor(stream, t);
visitor(image);
//util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "tif"))
{
tiff_saver visitor(stream, t);
visitor(image);
//util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "jpeg"))
{
jpeg_saver visitor(stream, t);
visitor(image);
//util::apply_visitor(visitor, image);
}
else if (boost::algorithm::starts_with(t, "webp"))
{
webp_saver visitor(stream, t);
visitor(image);
//util::apply_visitor(visitor, image);
}
else throw ImageWriterException("unknown file type: " + type);
}
else throw ImageWriterException("Could not write to empty stream" );
}
// This can be removed once image_data_any and image_view_any are the only
// items using this template
template <>
void save_to_stream<image_view_rgba8>(image_view_rgba8 const& image,
std::ostream & stream,
std::string const& type)
{
if (stream && image.width() > 0 && image.height() > 0)
{
@ -182,50 +325,157 @@ void save_to_file(T const& image, std::string const& filename, rgba_palette cons
else throw ImageWriterException("Could not write file to " + filename );
}
// image_data_rgba8
template void save_to_file<image_data_rgba8>(image_data_rgba8 const&,
std::string const&,
std::string const&);
std::string const&,
std::string const&);
template void save_to_file<image_data_rgba8>(image_data_rgba8 const&,
std::string const&,
std::string const&,
rgba_palette const& palette);
std::string const&,
std::string const&,
rgba_palette const& palette);
template void save_to_file<image_data_rgba8>(image_data_rgba8 const&,
std::string const&);
std::string const&);
template void save_to_file<image_data_rgba8>(image_data_rgba8 const&,
std::string const&,
rgba_palette const& palette);
std::string const&,
rgba_palette const& palette);
template std::string save_to_string<image_data_rgba8>(image_data_rgba8 const&,
std::string const&);
std::string const&);
template std::string save_to_string<image_data_rgba8>(image_data_rgba8 const&,
std::string const&,
rgba_palette const& palette);
std::string const&,
rgba_palette const& palette);
// image_view_rgba8
template void save_to_file<image_view_rgba8> (image_view_rgba8 const&,
std::string const&,
std::string const&);
template void save_to_file<image_view_rgba8> (image_view_rgba8 const&,
std::string const&,
std::string const&);
std::string const&,
std::string const&,
rgba_palette const& palette);
template void save_to_file<image_view_rgba8> (image_view_rgba8 const&,
std::string const&,
std::string const&,
rgba_palette const& palette);
std::string const&);
template void save_to_file<image_view_rgba8> (image_view_rgba8 const&,
std::string const&);
template void save_to_file<image_view_rgba8> (image_view_rgba8 const&,
std::string const&,
rgba_palette const& palette);
std::string const&,
rgba_palette const& palette);
template std::string save_to_string<image_view_rgba8> (image_view_rgba8 const&,
std::string const&);
std::string const&);
template std::string save_to_string<image_view_rgba8> (image_view_rgba8 const&,
std::string const&,
rgba_palette const& palette);
std::string const&,
rgba_palette const& palette);
// image_data_any
template void save_to_file<image_data_any>(image_data_any const&,
std::string const&,
std::string const&);
template void save_to_file<image_data_any>(image_data_any const&,
std::string const&,
std::string const&,
rgba_palette const& palette);
template void save_to_file<image_data_any>(image_data_any const&,
std::string const&);
template void save_to_file<image_data_any>(image_data_any const&,
std::string const&,
rgba_palette const& palette);
template std::string save_to_string<image_data_any>(image_data_any const&,
std::string const&);
template std::string save_to_string<image_data_any>(image_data_any const&,
std::string const&,
rgba_palette const& palette);
namespace detail {
struct premultiply_visitor
{
template <typename T>
void operator() (T & data)
{
throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported");
}
};
template <>
void premultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
if (!data.get_premultiplied())
{
agg::rendering_buffer buffer(data.getBytes(),data.width(),data.height(),data.width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
data.set_premultiplied(true);
}
}
struct demultiply_visitor
{
template <typename T>
void operator() (T & data)
{
throw std::runtime_error("Error: Premultiply with " + std::string(typeid(data).name()) + " is not supported");
}
};
template <>
void demultiply_visitor::operator()<image_data_rgba8> (image_data_rgba8 & data)
{
if (data.get_premultiplied())
{
agg::rendering_buffer buffer(data.getBytes(),data.width(),data.height(),data.width() * 4);
agg::pixfmt_rgba32_pre pixf(buffer);
pixf.demultiply();
data.set_premultiplied(false);
}
}
} // end detail ns
template <typename T>
void premultiply_alpha(T & image)
{
util::apply_visitor(detail::premultiply_visitor(), image);
}
template void 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 <>
void premultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
{
detail::premultiply_visitor visit;
visit(image);
}
template <typename T>
void demultiply_alpha(T & image)
{
util::apply_visitor(detail::demultiply_visitor(), image);
}
template void 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 <>
void demultiply_alpha<image_data_rgba8>(image_data_rgba8 & image)
{
detail::demultiply_visitor visit;
visit(image);
}
} // end ns

View file

@ -76,6 +76,12 @@ void jpeg_saver::operator()<image_view_rgba8> (image_view_rgba8 const& image) co
process_rgba8_jpeg(image, t_, stream_);
}
template<>
void jpeg_saver::operator()<image_data_null> (image_data_null const& image) const
{
throw ImageWriterException("Can not save a null image to jpeg");
}
template <typename T>
void jpeg_saver::operator() (T const& image) const
{

View file

@ -85,7 +85,6 @@ public:
unsigned height() const final;
boost::optional<box2d<double> > bounding_box() const final;
inline bool has_alpha() const final { return false; }
inline bool premultiplied_alpha() const final { return true; }
void read(unsigned x,unsigned y,image_data_rgba8& image) final;
image_data_any read(unsigned x, unsigned y, unsigned width, unsigned height) final;
private:
@ -323,7 +322,7 @@ void jpeg_reader<T>::read(unsigned x0, unsigned y0, image_data_rgba8& image)
template <typename T>
image_data_any jpeg_reader<T>::read(unsigned x, unsigned y, unsigned width, unsigned height)
{
image_data_rgba8 data(width,height);
image_data_rgba8 data(width,height, true, true);
read(x, y, data);
return image_data_any(std::move(data));
}

View file

@ -210,14 +210,13 @@ boost::optional<marker_ptr> marker_cache::find(std::string const& uri,
unsigned width = reader->width();
unsigned height = reader->height();
BOOST_ASSERT(width > 0 && height > 0);
mapnik::image_ptr image(std::make_shared<mapnik::image_data_rgba8>(width,height));
reader->read(0,0,*image);
if (!reader->premultiplied_alpha())
image_data_any im = reader->read(0,0,width,height);
if (!im.is<image_data_rgba8>())
{
agg::rendering_buffer buffer(image->getBytes(),image->width(),image->height(),image->width() * 4);
agg::pixfmt_rgba32 pixf(buffer);
pixf.premultiply();
throw std::runtime_error("Error: Only image_data_rgba8 types are supported currenctly by markers");
}
mapnik::premultiply_alpha(im);
mapnik::image_ptr image(std::make_shared<mapnik::image_data_rgba8>(std::move(util::get<image_data_rgba8>(im))));
marker_ptr mark(std::make_shared<marker>(image));
result.reset(mark);
if (update_cache)

View file

@ -80,7 +80,6 @@ public:
unsigned height() const final;
boost::optional<box2d<double> > bounding_box() const final;
inline bool has_alpha() const final { return has_alpha_; }
bool premultiplied_alpha() const final { return false; } //http://www.libpng.org/pub/png/spec/1.1/PNG-Rationale.html
void read(unsigned x,unsigned y,image_data_rgba8& image) final;
image_data_any read(unsigned x, unsigned y, unsigned width, unsigned height) final;
private:

View file

@ -153,7 +153,6 @@ public:
unsigned height() const final;
boost::optional<box2d<double> > bounding_box() const final;
inline bool has_alpha() const final { return has_alpha_; }
bool premultiplied_alpha() const final;
void read(unsigned x,unsigned y,image_data_rgba8& image) final;
image_data_any read(unsigned x, unsigned y, unsigned width, unsigned height) final;
// methods specific to tiff reader
@ -377,12 +376,6 @@ boost::optional<box2d<double> > tiff_reader<T>::bounding_box() const
return bbox_;
}
template <typename T>
bool tiff_reader<T>::premultiplied_alpha() const
{
return premultiplied_alpha_;
}
template <typename T>
void tiff_reader<T>::read(unsigned x,unsigned y,image_data_rgba8& image)
{
@ -525,7 +518,7 @@ image_data_any tiff_reader<T>::read(unsigned x0, unsigned y0, unsigned width, un
TIFF* tif = open(stream_);
if (tif)
{
image_data_rgba8 data(width, height);
image_data_rgba8 data(width, height, true, premultiplied_alpha_);
std::size_t element_size = sizeof(detail::rgb8);
std::size_t size_to_allocate = (TIFFScanlineSize(tif) + element_size - 1)/element_size;
const std::unique_ptr<detail::rgb8[]> scanline(new detail::rgb8[size_to_allocate]);
@ -550,13 +543,13 @@ image_data_any tiff_reader<T>::read(unsigned x0, unsigned y0, unsigned width, un
}
case 16:
{
image_data_rgba8 data(width,height);
image_data_rgba8 data(width,height,true,premultiplied_alpha_);
read(x0, y0, data);
return image_data_any(std::move(data));
}
case 32:
{
image_data_rgba8 data(width,height);
image_data_rgba8 data(width,height,true,premultiplied_alpha_);
read(x0, y0, data);
return image_data_any(std::move(data));
}
@ -574,7 +567,7 @@ image_data_any tiff_reader<T>::read(unsigned x0, unsigned y0, unsigned width, un
//PHOTOMETRIC_ITULAB = 10;
//PHOTOMETRIC_LOGL = 32844;
//PHOTOMETRIC_LOGLUV = 32845;
image_data_rgba8 data(width,height);
image_data_rgba8 data(width,height, true, premultiplied_alpha_);
read(x0, y0, data);
return image_data_any(std::move(data));
}

View file

@ -124,7 +124,6 @@ public:
unsigned height() const final;
boost::optional<box2d<double> > bounding_box() const final;
inline bool has_alpha() const final { return has_alpha_; }
bool premultiplied_alpha() const final { return false; }
void read(unsigned x,unsigned y,image_data_rgba8& image) final;
image_data_any read(unsigned x, unsigned y, unsigned width, unsigned height) final;
private:

View file

@ -26,25 +26,19 @@
REQUIRE( reader2->width() == 256 ); \
REQUIRE( reader2->height() == 256 ); \
#define TIFF_ASSERT_ALPHA \
#define TIFF_ASSERT_ALPHA( data ) \
REQUIRE( tiff_reader.has_alpha() == true ); \
REQUIRE( tiff_reader.premultiplied_alpha() == false ); \
REQUIRE( reader->has_alpha() == true ); \
REQUIRE( reader->premultiplied_alpha() == false ); \
REQUIRE( tiff_reader2.has_alpha() == true ); \
REQUIRE( tiff_reader2.premultiplied_alpha() == false ); \
REQUIRE( reader2->has_alpha() == true ); \
REQUIRE( reader2->premultiplied_alpha() == false ); \
REQUIRE( data.get_premultiplied() == false ); \
#define TIFF_ASSERT_NO_ALPHA \
#define TIFF_ASSERT_NO_ALPHA( data ) \
REQUIRE( tiff_reader.has_alpha() == false ); \
REQUIRE( tiff_reader.premultiplied_alpha() == false ); \
REQUIRE( reader->has_alpha() == false ); \
REQUIRE( reader->premultiplied_alpha() == false ); \
REQUIRE( tiff_reader2.has_alpha() == false ); \
REQUIRE( tiff_reader2.premultiplied_alpha() == false ); \
REQUIRE( reader2->has_alpha() == false ); \
REQUIRE( reader2->premultiplied_alpha() == false ); \
REQUIRE( data.get_premultiplied() == false ); \
#define TIFF_ASSERT_SIZE( data,reader ) \
REQUIRE( data.width() == reader->width() ); \
@ -83,7 +77,7 @@ SECTION("scan rgb8 striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -113,7 +107,7 @@ SECTION("scan rgb8 tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -129,7 +123,7 @@ SECTION("rgba8 striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_ALPHA
TIFF_ASSERT_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -145,7 +139,7 @@ SECTION("rgba8 tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_ALPHA
TIFF_ASSERT_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -161,7 +155,7 @@ SECTION("rgb8 striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -177,7 +171,7 @@ SECTION("rgb8 tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_rgba8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -193,7 +187,7 @@ SECTION("gray8 striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -209,7 +203,7 @@ SECTION("gray8 tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray8>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -225,7 +219,7 @@ SECTION("gray16 striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray16>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -241,7 +235,7 @@ SECTION("gray16 tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray16>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -257,7 +251,7 @@ SECTION("gray32f striped") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray32f>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}
@ -273,7 +267,7 @@ SECTION("gray32f tiled") {
mapnik::image_data_any data = reader->read(0, 0, reader->width(), reader->height());
REQUIRE( data.is<mapnik::image_data_gray32f>() == true );
TIFF_ASSERT_SIZE( data,reader );
TIFF_ASSERT_NO_ALPHA
TIFF_ASSERT_NO_ALPHA( data );
TIFF_READ_ONE_PIXEL
}