Merge branch 'fix-gray-scaling-2' of https://github.com/mapycz/mapnik into mapycz-fix-gray-scaling-2

This commit is contained in:
artemp 2015-11-17 15:03:37 +00:00
commit 6d599dff0a
9 changed files with 243 additions and 45 deletions

View file

@ -23,6 +23,7 @@ Released:
- `mapnik.Image` - fixed copy semantics implementation for internal buffer
- JSON parsing: unified error_handler across all grammars
- Improved unit test coverage
- Raster scaling: fixed nodata handling, acurracy when working with small floats and clipping floats by \[0; 255\] (https://github.com/mapnik/mapnik/pull/3147)
## 3.0.8

View file

@ -67,7 +67,8 @@ MAPNIK_DECL void scale_image_agg(T & target, T const& source,
double image_ratio_y,
double x_off_f,
double y_off_f,
double filter_factor);
double filter_factor,
boost::optional<double> const & nodata_value);
}
#endif // MAPNIK_IMAGE_SCALING_HPP

View file

@ -23,6 +23,9 @@
#ifndef MAPNIK_IMAGE_SCALING_TRAITS_HPP
#define MAPNIK_IMAGE_SCALING_TRAITS_HPP
// mapnik
#include <mapnik/span_image_filter.h>
// agg
#include "agg_image_accessors.h"
#include "agg_pixfmt_rgba.h"
@ -45,7 +48,7 @@ struct agg_scaling_traits<image_rgba8>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_rgba_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_rgba_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_rgba_affine<img_src_type>;
};
@ -57,7 +60,7 @@ struct agg_scaling_traits<image_gray8>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -68,7 +71,7 @@ struct agg_scaling_traits<image_gray8s>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -79,7 +82,7 @@ struct agg_scaling_traits<image_gray16>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -90,7 +93,7 @@ struct agg_scaling_traits<image_gray16s>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -101,7 +104,7 @@ struct agg_scaling_traits<image_gray32>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -112,7 +115,7 @@ struct agg_scaling_traits<image_gray32s>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -123,7 +126,7 @@ struct agg_scaling_traits<image_gray32f>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -134,7 +137,7 @@ struct agg_scaling_traits<image_gray64>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -145,7 +148,7 @@ struct agg_scaling_traits<image_gray64s>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <>
@ -156,7 +159,7 @@ struct agg_scaling_traits<image_gray64f>
using interpolator_type = agg::span_interpolator_linear<>;
using img_src_type = agg::image_accessor_clone<pixfmt_pre>;
using span_image_filter = agg::span_image_filter_gray_nn<img_src_type,interpolator_type>;
using span_image_resample_affine = agg::span_image_resample_gray_affine<img_src_type>;
using span_image_resample_affine = span_image_resample_gray_affine<img_src_type>;
};
template <typename Filter>

View file

@ -77,7 +77,7 @@ struct image_dispatcher
if (need_scaling_)
{
image_rgba8 data_out(width_, height_, true, true);
scale_image_agg(data_out, data_in, method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_);
scale_image_agg(data_out, data_in, method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_, nodata_);
composite_(data_out, comp_op_, opacity_, start_x_, start_y_);
}
else
@ -95,7 +95,7 @@ struct image_dispatcher
if (need_scaling_)
{
image_type data_out(width_, height_);
scale_image_agg(data_out, data_in, method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_);
scale_image_agg(data_out, data_in, method_, scale_x_, scale_y_, 0.0, 0.0, filter_factor_, nodata_);
if (colorizer) colorizer->colorize(dst, data_out, nodata_, feature_);
}
else
@ -157,7 +157,7 @@ struct image_warp_dispatcher
void operator() (image_rgba8 const& data_in) const
{
image_rgba8 data_out(width_, height_, true, true);
warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_);
composite_(data_out, comp_op_, opacity_, start_x_, start_y_);
}
@ -167,7 +167,7 @@ struct image_warp_dispatcher
using image_type = T;
image_type data_out(width_, height_);
if (nodata_) data_out.set(*nodata_);
warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
warp_image(data_out, data_in, prj_trans_, target_ext_, source_ext_, offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_);
image_rgba8 dst(width_, height_);
raster_colorizer_ptr colorizer = get<raster_colorizer_ptr>(sym_, keys::colorizer);
if (colorizer) colorizer->colorize(dst, data_out, nodata_, feature_);

View file

@ -0,0 +1,178 @@
/*****************************************************************************
*
* 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_SPAN_IMAGE_FILTER_INCLUDED
#define MAPNIK_SPAN_IMAGE_FILTER_INCLUDED
#include "agg_span_image_filter_gray.h"
#include "agg_span_image_filter_rgba.h"
#include <limits>
namespace mapnik
{
template<class Source>
class span_image_resample_gray_affine : public agg::span_image_resample_affine<Source>
{
public:
using source_type = Source;
using color_type = typename source_type::color_type;
using base_type = agg::span_image_resample_affine<source_type>;
using interpolator_type = typename base_type::interpolator_type;
using value_type = typename color_type::value_type;
using long_type = typename color_type::long_type;
enum base_scale_e
{
downscale_shift = agg::image_filter_shift,
base_mask = color_type::base_mask
};
span_image_resample_gray_affine(source_type & src,
interpolator_type & inter,
agg::image_filter_lut const & filter,
boost::optional<value_type> const & nodata_value) :
base_type(src, inter, filter),
nodata_value_(nodata_value)
{ }
void generate(color_type* span, int x, int y, unsigned len)
{
base_type::interpolator().begin(x + base_type::filter_dx_dbl(),
y + base_type::filter_dy_dbl(), len);
long_type fg;
int diameter = base_type::filter().diameter();
int filter_scale = diameter << agg::image_subpixel_shift;
int radius_x = (diameter * base_type::m_rx) >> 1;
int radius_y = (diameter * base_type::m_ry) >> 1;
int len_x_lr =
(diameter * base_type::m_rx + agg::image_subpixel_mask) >>
agg::image_subpixel_shift;
const agg::int16* weight_array = base_type::filter().weight_array();
do
{
base_type::interpolator().coordinates(&x, &y);
int src_x = x >> agg::image_subpixel_shift;
int src_y = y >> agg::image_subpixel_shift;
const value_type* pix = reinterpret_cast<const value_type*>(base_type::source().span(src_x, src_y, 0));
if (nodata_value_ && *nodata_value_ == *pix)
{
span->v = *nodata_value_;
span->a = base_mask;
++span;
++base_type::interpolator();
continue;
}
x += base_type::filter_dx_int() - radius_x;
y += base_type::filter_dy_int() - radius_y;
fg = 0;
int y_lr = y >> agg::image_subpixel_shift;
int y_hr = ((agg::image_subpixel_mask - (y & agg::image_subpixel_mask)) *
base_type::m_ry_inv) >>
agg::image_subpixel_shift;
int total_weight = 0;
int x_lr = x >> agg::image_subpixel_shift;
int x_hr = ((agg::image_subpixel_mask - (x & agg::image_subpixel_mask)) *
base_type::m_rx_inv) >>
agg::image_subpixel_shift;
int x_hr2 = x_hr;
const value_type* fg_ptr = reinterpret_cast<const value_type*>(base_type::source().span(x_lr, y_lr, len_x_lr));
for(;;)
{
int weight_y = weight_array[y_hr];
x_hr = x_hr2;
for(;;)
{
int weight = (weight_y * weight_array[x_hr] +
agg::image_filter_scale) >>
downscale_shift;
if (!nodata_value_ || *nodata_value_ != *fg_ptr)
{
fg += *fg_ptr * weight;
total_weight += weight;
}
x_hr += base_type::m_rx_inv;
if (x_hr >= filter_scale) break;
fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_x());
}
y_hr += base_type::m_ry_inv;
if (y_hr >= filter_scale) break;
fg_ptr = reinterpret_cast<const value_type*>(base_type::source().next_y());
}
fg /= total_weight;
if (fg < std::numeric_limits<value_type>::min())
{
span->v = std::numeric_limits<value_type>::min();
}
else if (fg > std::numeric_limits<value_type>::max())
{
span->v = std::numeric_limits<value_type>::max();
}
else
{
span->v = static_cast<value_type>(fg);
}
span->a = base_mask;
++span;
++base_type::interpolator();
} while(--len);
}
private:
boost::optional<value_type> nodata_value_;
};
template<class Source>
class span_image_resample_rgba_affine : public agg::span_image_resample_rgba_affine<Source>
{
public:
using source_type = Source;
using color_type = typename source_type::color_type;
using order_type = typename source_type::order_type;
using base_type = agg::span_image_resample_rgba_affine<source_type>;
using interpolator_type = typename base_type::interpolator_type;
using value_type = typename color_type::value_type;
using long_type = typename color_type::long_type;
span_image_resample_rgba_affine(source_type & src,
interpolator_type & inter,
agg::image_filter_lut const & filter,
boost::optional<value_type> const & nodata_value) :
agg::span_image_resample_rgba_affine<Source>(src, inter, filter)
{ }
};
}
#endif

View file

@ -44,7 +44,8 @@ MAPNIK_DECL void reproject_and_scale_raster(raster & target,
template <typename T>
MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const& prj_trans,
box2d<double> const& target_ext, box2d<double> const& source_ext,
double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor);
double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor,
boost::optional<double> const & nodata_value);
}
#endif // MAPNIK_WARP_HPP

View file

@ -197,12 +197,13 @@ struct grid_render_marker_visitor
else
{
image_rgba8 target(data.width(), data.height());
boost::optional<double> nodata;
mapnik::scale_image_agg(target,
data,
SCALING_NEAR,
1,
1,
0.0, 0.0, 1.0); // TODO: is 1.0 a valid default here, and do we even care in grid_renderer what the image looks like?
0.0, 0.0, 1.0, nodata); // TODO: is 1.0 a valid default here, and do we even care in grid_renderer what the image looks like?
pixmap_.set_rectangle(feature_.id(), target,
boost::math::iround(pos_.x - cx),
boost::math::iround(pos_.y - cy));

View file

@ -24,8 +24,6 @@
#include <mapnik/image.hpp>
#include <mapnik/image_scaling.hpp>
#include <mapnik/image_scaling_traits.hpp>
// does not handle alpha correctly
//#include <mapnik/span_image_filter.hpp>
// boost
#pragma GCC diagnostic push
@ -100,7 +98,7 @@ boost::optional<std::string> scaling_method_to_string(scaling_method_e scaling_m
template <typename T>
void scale_image_agg(T & target, T const& source, scaling_method_e scaling_method,
double image_ratio_x, double image_ratio_y, double x_off_f, double y_off_f,
double filter_factor)
double filter_factor, boost::optional<double> const & nodata_value)
{
// "the image filters should work namely in the premultiplied color space"
// http://old.nabble.com/Re:--AGG--Basic-image-transformations-p1110665.html
@ -158,42 +156,46 @@ void scale_image_agg(T & target, T const& source, scaling_method_e scaling_metho
using span_gen_type = typename detail::agg_scaling_traits<image_type>::span_image_resample_affine;
agg::image_filter_lut filter;
detail::set_scaling_method(filter, scaling_method, filter_factor);
span_gen_type sg(img_src, interpolator, filter);
boost::optional<typename span_gen_type::value_type> nodata;
if (nodata_value)
{
nodata = nodata_value;
}
span_gen_type sg(img_src, interpolator, filter, nodata);
agg::render_scanlines_aa(ras, sl, rb_dst_pre, sa, sg);
}
}
template MAPNIK_DECL void scale_image_agg(image_rgba8 &, image_rgba8 const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray8 &, image_gray8 const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray8s &, image_gray8s const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray16 &, image_gray16 const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray16s &, image_gray16s const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray32 &, image_gray32 const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray32s &, image_gray32s const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray32f &, image_gray32f const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray64 &, image_gray64 const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray64s &, image_gray64s const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
template MAPNIK_DECL void scale_image_agg(image_gray64f &, image_gray64f const&, scaling_method_e,
double, double , double, double , double);
double, double , double, double , double, boost::optional<double> const &);
}

View file

@ -51,7 +51,8 @@ namespace mapnik {
template <typename T>
MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const& prj_trans,
box2d<double> const& target_ext, box2d<double> const& source_ext,
double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor)
double offset_x, double offset_y, unsigned mesh_size, scaling_method_e scaling_method, double filter_factor,
boost::optional<double> const & nodata_value)
{
using image_type = T;
using pixel_type = typename image_type::pixel_type;
@ -147,7 +148,12 @@ MAPNIK_DECL void warp_image (T & target, T const& source, proj_transform const&
using span_gen_type = typename detail::agg_scaling_traits<image_type>::span_image_resample_affine;
agg::image_filter_lut filter;
detail::set_scaling_method(filter, scaling_method, filter_factor);
span_gen_type sg(ia, interpolator, filter);
boost::optional<typename span_gen_type::value_type> nodata;
if (nodata_value)
{
nodata = nodata_value;
}
span_gen_type sg(ia, interpolator, filter, nodata);
agg::render_scanlines_bin(rasterizer, scanline, rb, sa, sg);
}
}
@ -162,7 +168,8 @@ struct warp_image_visitor
{
warp_image_visitor (raster & target_raster, proj_transform const& prj_trans, box2d<double> const& source_ext,
double offset_x, double offset_y, unsigned mesh_size,
scaling_method_e scaling_method, double filter_factor)
scaling_method_e scaling_method, double filter_factor,
boost::optional<double> const & nodata_value)
: target_raster_(target_raster),
prj_trans_(prj_trans),
source_ext_(source_ext),
@ -170,7 +177,9 @@ struct warp_image_visitor
offset_y_(offset_y),
mesh_size_(mesh_size),
scaling_method_(scaling_method),
filter_factor_(filter_factor) {}
filter_factor_(filter_factor),
nodata_value_(nodata_value)
{}
void operator() (image_null const&) {}
@ -183,7 +192,7 @@ struct warp_image_visitor
{
image_type & target = util::get<image_type>(target_raster_.data_);
warp_image (target, source, prj_trans_, target_raster_.ext_, source_ext_,
offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_);
offset_x_, offset_y_, mesh_size_, scaling_method_, filter_factor_, nodata_value_);
}
}
@ -195,6 +204,7 @@ struct warp_image_visitor
unsigned mesh_size_;
scaling_method_e scaling_method_;
double filter_factor_;
boost::optional<double> const & nodata_value_;
};
}
@ -203,24 +213,25 @@ void reproject_and_scale_raster(raster & target, raster const& source,
proj_transform const& prj_trans,
double offset_x, double offset_y,
unsigned mesh_size,
scaling_method_e scaling_method)
scaling_method_e scaling_method,
boost::optional<double> const & nodata_value)
{
detail::warp_image_visitor warper(target, prj_trans, source.ext_, offset_x, offset_y, mesh_size,
scaling_method, source.get_filter_factor());
scaling_method, source.get_filter_factor(), nodata_value);
util::apply_visitor(warper, source.data_);
}
template MAPNIK_DECL void warp_image (image_rgba8&, image_rgba8 const&, proj_transform const&,
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
template MAPNIK_DECL void warp_image (image_gray8&, image_gray8 const&, proj_transform const&,
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
template MAPNIK_DECL void warp_image (image_gray16&, image_gray16 const&, proj_transform const&,
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
template MAPNIK_DECL void warp_image (image_gray32f&, image_gray32f const&, proj_transform const&,
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double);
box2d<double> const&, box2d<double> const&, double, double, unsigned, scaling_method_e, double, boost::optional<double> const &);
}// namespace mapnik