/******************************************************************************
* $Id$
*
* Project: MapServer
* Purpose: AGG rendering and other AGG related functions.
* Author: Thomas Bonfort and the MapServer team.
*
******************************************************************************
* Copyright (c) 1996-2007 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include "mapserver.h"
#include "fontcache.h"
#include "mapagg.h"
#include
#include "renderers/agg/include/agg_color_rgba.h"
#include "renderers/agg/include/agg_pixfmt_rgba.h"
#include "renderers/agg/include/agg_renderer_base.h"
#include "renderers/agg/include/agg_renderer_scanline.h"
#include "renderers/agg/include/agg_math_stroke.h"
#include "renderers/agg/include/agg_scanline_p.h"
#include "renderers/agg/include/agg_scanline_u.h"
#include "renderers/agg/include/agg_rasterizer_scanline_aa.h"
#include "renderers/agg/include/agg_span_pattern_rgba.h"
#include "renderers/agg/include/agg_span_allocator.h"
#include "renderers/agg/include/agg_span_interpolator_linear.h"
#include "renderers/agg/include/agg_pattern_filters_rgba.h"
#include "renderers/agg/include/agg_image_accessors.h"
#include "renderers/agg/include/agg_conv_stroke.h"
#include "renderers/agg/include/agg_conv_dash.h"
#include "renderers/agg/include/agg_font_freetype.h"
#include "renderers/agg/include/agg_conv_contour.h"
#include "renderers/agg/include/agg_ellipse.h"
#include "renderers/agg/include/agg_gamma_functions.h"
#include "renderers/agg/include/agg_blur.h"
#include "renderers/agg/include/agg_rasterizer_outline_aa.h"
#include "renderers/agg/include/agg_renderer_outline_aa.h"
#include "renderers/agg/include/agg_renderer_outline_image.h"
#include "renderers/agg/include/agg_span_pattern_rgba.h"
#include "renderers/agg/include/agg_span_image_filter_rgba.h"
#include "renderers/agg/include/agg_glyph_raster_bin.h"
#include "renderers/agg/include/agg_renderer_raster_text.h"
#include "renderers/agg/include/agg_path_storage_integer.h"
#include "renderers/agg/include/agg_conv_clipper.h"
#include "cpl_conv.h" // CPLGetConfigOption
#include "cpl_string.h" // CPLTestBool
#ifdef USE_PIXMAN
#include
#endif
#include
#include
#include
#include
typedef mapserver::order_bgra band_order;
#define AGG_LINESPACE 1.33
typedef mapserver::int8u band_type;
typedef mapserver::rgba8 color_type;
typedef mapserver::pixel32_type pixel_type;
typedef mapserver::blender_rgba_pre blender_pre;
typedef mapserver::comp_op_adaptor_rgba_pre
compop_blender_pre;
typedef mapserver::pixfmt_alpha_blend_rgba<
blender_pre, mapserver::rendering_buffer, pixel_type>
pixel_format;
typedef mapserver::pixfmt_custom_blend_rgba
compop_pixel_format;
typedef mapserver::rendering_buffer rendering_buffer;
typedef mapserver::renderer_base renderer_base;
typedef mapserver::renderer_base compop_renderer_base;
typedef mapserver::renderer_scanline_aa_solid renderer_scanline;
typedef mapserver::renderer_scanline_bin_solid
renderer_scanline_aliased;
typedef mapserver::rasterizer_scanline_aa rasterizer_scanline;
typedef mapserver::font_engine_freetype_int16 font_engine_type;
typedef mapserver::font_cache_manager font_manager_type;
typedef mapserver::conv_curve
font_curve_type;
typedef mapserver::glyph_raster_bin glyph_gen;
static const color_type AGG_NO_COLOR = color_type(0, 0, 0, 0);
#define aggColor(c) \
mapserver::rgba8_pre((c)->red, (c)->green, (c)->blue, (c)->alpha)
class aggRendererCache {
public:
font_engine_type m_feng;
font_manager_type m_fman;
aggRendererCache() : m_fman(m_feng) {}
};
class AGG2Renderer {
public:
std::vector buffer{};
rendering_buffer m_rendering_buffer;
pixel_format m_pixel_format;
compop_pixel_format m_compop_pixel_format;
renderer_base m_renderer_base;
compop_renderer_base m_compop_renderer_base;
renderer_scanline m_renderer_scanline;
renderer_scanline_aliased m_renderer_scanline_aliased;
rasterizer_scanline m_rasterizer_aa;
rasterizer_scanline m_rasterizer_aa_gamma;
mapserver::scanline_p8 sl_poly; /*packed scanlines, works faster when the area
is larger than the perimeter, in number of pixels*/
mapserver::scanline_u8 sl_line; /*unpacked scanlines, works faster if the area
is roughly equal to the perimeter, in number of pixels*/
bool use_alpha = false;
std::unique_ptr stroke{};
std::unique_ptr dash{};
std::unique_ptr
stroke_dash{};
double default_gamma = 0.0;
mapserver::gamma_linear gamma_function;
};
#define AGG_RENDERER(image) ((AGG2Renderer *)(image)->img.plugin)
template
static void applyCJC(VertexSource &stroke, int caps, int joins) {
switch (joins) {
case MS_CJC_ROUND:
stroke.line_join(mapserver::round_join);
break;
case MS_CJC_MITER:
stroke.line_join(mapserver::miter_join);
break;
case MS_CJC_BEVEL:
case MS_CJC_NONE:
stroke.line_join(mapserver::bevel_join);
break;
}
switch (caps) {
case MS_CJC_BUTT:
case MS_CJC_NONE:
stroke.line_cap(mapserver::butt_cap);
break;
case MS_CJC_ROUND:
stroke.line_cap(mapserver::round_cap);
break;
case MS_CJC_SQUARE:
stroke.line_cap(mapserver::square_cap);
break;
}
}
int agg2RenderLine(imageObj *img, shapeObj *p, strokeStyleObj *style) {
AGG2Renderer *r = AGG_RENDERER(img);
line_adaptor lines = line_adaptor(p);
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
if (style->antialiased == MS_FALSE) {
r->m_renderer_scanline_aliased.color(aggColor(style->color));
} else {
r->m_renderer_scanline.color(aggColor(style->color));
}
if (style->patternlength stroke) {
r->stroke.reset(new mapserver::conv_stroke(lines));
} else {
r->stroke->attach(lines);
}
r->stroke->width(style->width);
if (style->width > 1) {
applyCJC(*r->stroke, style->linecap, style->linejoin);
} else {
r->stroke->inner_join(mapserver::inner_bevel);
r->stroke->line_join(mapserver::bevel_join);
}
r->m_rasterizer_aa.add_path(*r->stroke);
} else {
if (!r->dash) {
r->dash.reset(new mapserver::conv_dash(lines));
} else {
r->dash->remove_all_dashes();
r->dash->dash_start(0.0);
r->dash->attach(lines);
}
if (!r->stroke_dash) {
r->stroke_dash.reset(
new mapserver::conv_stroke(
*r->dash));
} else {
r->stroke_dash->attach(*r->dash);
}
int patt_length = 0;
for (int i = 0; i < style->patternlength; i += 2) {
if (i < style->patternlength - 1) {
r->dash->add_dash(MS_MAX(1, MS_NINT(style->pattern[i])),
MS_MAX(1, MS_NINT(style->pattern[i + 1])));
if (style->patternoffset) {
patt_length += MS_MAX(1, MS_NINT(style->pattern[i])) +
MS_MAX(1, MS_NINT(style->pattern[i + 1]));
}
}
}
if (style->patternoffset > 0) {
r->dash->dash_start(patt_length - style->patternoffset);
}
r->stroke_dash->width(style->width);
if (style->width > 1) {
applyCJC(*r->stroke_dash, style->linecap, style->linejoin);
} else {
r->stroke_dash->inner_join(mapserver::inner_bevel);
r->stroke_dash->line_join(mapserver::bevel_join);
}
r->m_rasterizer_aa.add_path(*r->stroke_dash);
}
if (style->antialiased == MS_FALSE)
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
r->m_renderer_scanline_aliased);
else
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
r->m_renderer_scanline);
return MS_SUCCESS;
}
int agg2RenderLineTiled(imageObj *img, shapeObj *p, imageObj *tile) {
mapserver::pattern_filter_bilinear_rgba8 fltr;
typedef mapserver::line_image_pattern<
mapserver::pattern_filter_bilinear_rgba8>
pattern_type;
typedef mapserver::renderer_outline_image
renderer_img_type;
typedef mapserver::rasterizer_outline_aa
rasterizer_img_type;
pattern_type patt(fltr);
AGG2Renderer *r = AGG_RENDERER(img);
AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
line_adaptor lines(p);
patt.create(tileRenderer->m_pixel_format);
renderer_img_type ren_img(r->m_renderer_base, patt);
rasterizer_img_type ras_img(ren_img);
ras_img.add_path(lines);
return MS_SUCCESS;
}
int agg2RenderPolygon(imageObj *img, shapeObj *p, colorObj *color) {
AGG2Renderer *r = AGG_RENDERER(img);
polygon_adaptor polygons(p);
r->m_rasterizer_aa_gamma.reset();
r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_even_odd);
r->m_rasterizer_aa_gamma.add_path(polygons);
r->m_renderer_scanline.color(aggColor(color));
mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly,
r->m_renderer_scanline);
return MS_SUCCESS;
}
static inline double int26p6_to_dbl(int p) { return double(p) / 64.0; }
template
bool decompose_ft_outline(const FT_Outline &outline, bool flip_y,
const mapserver::trans_affine &mtx,
PathStorage &path) {
double x1, y1, x2, y2, x3, y3;
FT_Vector *point;
FT_Vector *limit;
unsigned n; // index of contour in outline
unsigned first; // index of first point in contour
char tag; // current point's state
first = 0;
for (n = 0; n < (unsigned)outline.n_contours; n++) {
int last; // index of last point in contour
last = outline.contours[n];
limit = outline.points + last;
FT_Vector v_start = outline.points[first];
FT_Vector v_control = v_start;
point = outline.points + first;
auto tags = outline.tags + first;
tag = FT_CURVE_TAG(tags[0]);
// A contour cannot start with a cubic control point!
if (tag == FT_CURVE_TAG_CUBIC)
return false;
// check first point to determine origin
if (tag == FT_CURVE_TAG_CONIC) {
const FT_Vector v_last = outline.points[last];
// first point is conic control. Yes, this happens.
if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
// start at last point if it is on the curve
v_start = v_last;
limit--;
} else {
// if both first and last points are conic,
// start at their middle and record its position
// for closure
v_start.x = (v_start.x + v_last.x) / 2;
v_start.y = (v_start.y + v_last.y) / 2;
}
point--;
tags--;
}
x1 = int26p6_to_dbl(v_start.x);
y1 = int26p6_to_dbl(v_start.y);
if (flip_y)
y1 = -y1;
mtx.transform(&x1, &y1);
path.move_to(x1, y1);
while (point < limit) {
point++;
tags++;
tag = FT_CURVE_TAG(tags[0]);
switch (tag) {
case FT_CURVE_TAG_ON: // emit a single line_to
{
x1 = int26p6_to_dbl(point->x);
y1 = int26p6_to_dbl(point->y);
if (flip_y)
y1 = -y1;
mtx.transform(&x1, &y1);
path.line_to(x1, y1);
// path.line_to(conv(point->x), flip_y ? -conv(point->y) :
// conv(point->y));
continue;
}
case FT_CURVE_TAG_CONIC: // consume conic arcs
{
v_control.x = point->x;
v_control.y = point->y;
Do_Conic:
if (point < limit) {
FT_Vector vec;
FT_Vector v_middle;
point++;
tags++;
tag = FT_CURVE_TAG(tags[0]);
vec.x = point->x;
vec.y = point->y;
if (tag == FT_CURVE_TAG_ON) {
x1 = int26p6_to_dbl(v_control.x);
y1 = int26p6_to_dbl(v_control.y);
x2 = int26p6_to_dbl(vec.x);
y2 = int26p6_to_dbl(vec.y);
if (flip_y) {
y1 = -y1;
y2 = -y2;
}
mtx.transform(&x1, &y1);
mtx.transform(&x2, &y2);
path.curve3(x1, y1, x2, y2);
continue;
}
if (tag != FT_CURVE_TAG_CONIC)
return false;
v_middle.x = (v_control.x + vec.x) / 2;
v_middle.y = (v_control.y + vec.y) / 2;
x1 = int26p6_to_dbl(v_control.x);
y1 = int26p6_to_dbl(v_control.y);
x2 = int26p6_to_dbl(v_middle.x);
y2 = int26p6_to_dbl(v_middle.y);
if (flip_y) {
y1 = -y1;
y2 = -y2;
}
mtx.transform(&x1, &y1);
mtx.transform(&x2, &y2);
path.curve3(x1, y1, x2, y2);
// path.curve3(conv(v_control.x),
// flip_y ? -conv(v_control.y) : conv(v_control.y),
// conv(v_middle.x),
// flip_y ? -conv(v_middle.y) : conv(v_middle.y));
v_control = vec;
goto Do_Conic;
}
x1 = int26p6_to_dbl(v_control.x);
y1 = int26p6_to_dbl(v_control.y);
x2 = int26p6_to_dbl(v_start.x);
y2 = int26p6_to_dbl(v_start.y);
if (flip_y) {
y1 = -y1;
y2 = -y2;
}
mtx.transform(&x1, &y1);
mtx.transform(&x2, &y2);
path.curve3(x1, y1, x2, y2);
// path.curve3(conv(v_control.x),
// flip_y ? -conv(v_control.y) : conv(v_control.y),
// conv(v_start.x),
// flip_y ? -conv(v_start.y) : conv(v_start.y));
goto Close;
}
default: // FT_CURVE_TAG_CUBIC
{
FT_Vector vec1, vec2;
if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) {
return false;
}
vec1.x = point[0].x;
vec1.y = point[0].y;
vec2.x = point[1].x;
vec2.y = point[1].y;
point += 2;
tags += 2;
if (point x;
vec.y = point->y;
x1 = int26p6_to_dbl(vec1.x);
y1 = int26p6_to_dbl(vec1.y);
x2 = int26p6_to_dbl(vec2.x);
y2 = int26p6_to_dbl(vec2.y);
x3 = int26p6_to_dbl(vec.x);
y3 = int26p6_to_dbl(vec.y);
if (flip_y) {
y1 = -y1;
y2 = -y2;
y3 = -y3;
}
mtx.transform(&x1, &y1);
mtx.transform(&x2, &y2);
mtx.transform(&x3, &y3);
path.curve4(x1, y1, x2, y2, x3, y3);
// path.curve4(conv(vec1.x),
// flip_y ? -conv(vec1.y) : conv(vec1.y),
// conv(vec2.x),
// flip_y ? -conv(vec2.y) : conv(vec2.y),
// conv(vec.x),
// flip_y ? -conv(vec.y) : conv(vec.y));
continue;
}
x1 = int26p6_to_dbl(vec1.x);
y1 = int26p6_to_dbl(vec1.y);
x2 = int26p6_to_dbl(vec2.x);
y2 = int26p6_to_dbl(vec2.y);
x3 = int26p6_to_dbl(v_start.x);
y3 = int26p6_to_dbl(v_start.y);
if (flip_y) {
y1 = -y1;
y2 = -y2;
y3 = -y3;
}
mtx.transform(&x1, &y1);
mtx.transform(&x2, &y2);
mtx.transform(&x3, &y3);
path.curve4(x1, y1, x2, y2, x3, y3);
// path.curve4(conv(vec1.x),
// flip_y ? -conv(vec1.y) : conv(vec1.y),
// conv(vec2.x),
// flip_y ? -conv(vec2.y) : conv(vec2.y),
// conv(v_start.x),
// flip_y ? -conv(v_start.y) : conv(v_start.y));
goto Close;
}
}
}
path.close_polygon();
Close:
first = last + 1;
}
return true;
}
int agg2RenderPolygonTiled(imageObj *img, shapeObj *p, imageObj *tile) {
assert(img->format->renderer == tile->format->renderer);
AGG2Renderer *r = AGG_RENDERER(img);
AGG2Renderer *tileRenderer = AGG_RENDERER(tile);
polygon_adaptor polygons(p);
typedef mapserver::wrap_mode_repeat wrap_type;
typedef mapserver::image_accessor_wrap
img_source_type;
typedef mapserver::span_pattern_rgba span_gen_type;
mapserver::span_allocator sa;
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
img_source_type img_src(tileRenderer->m_pixel_format);
span_gen_type sg(img_src, 0, 0);
r->m_rasterizer_aa.add_path(polygons);
mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly,
r->m_renderer_base, sa, sg);
return MS_SUCCESS;
}
int agg2RenderGlyphsPath(imageObj *img, const textSymbolObj *ts, colorObj *c,
colorObj *oc, int ow, int /*isMarker*/) {
const textPathObj *tp = ts->textpath;
mapserver::path_storage glyphs;
mapserver::trans_affine trans;
AGG2Renderer *r = AGG_RENDERER(img);
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
for (int i = 0; i < tp->numglyphs; i++) {
glyphObj *gl = tp->glyphs + i;
trans.reset();
trans.rotate(-gl->rot);
trans.translate(gl->pnt.x, gl->pnt.y);
outline_element *ol = msGetGlyphOutline(gl->face, gl->glyph);
if (!ol) {
return MS_FAILURE;
}
decompose_ft_outline(ol->outline, true, trans, glyphs);
}
mapserver::conv_curve m_curves(glyphs);
if (oc) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
mapserver::conv_contour cc(
m_curves);
cc.width(ow + 1);
r->m_rasterizer_aa.add_path(cc);
r->m_renderer_scanline.color(aggColor(oc));
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
r->m_renderer_scanline);
}
if (c) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
r->m_rasterizer_aa.add_path(m_curves);
r->m_renderer_scanline.color(aggColor(c));
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
r->m_renderer_scanline);
}
return MS_SUCCESS;
}
mapserver::path_storage imageVectorSymbol(symbolObj *symbol) {
mapserver::path_storage path;
int is_new = 1;
for (int i = 0; i < symbol->numpoints; i++) {
if ((symbol->points[i].x == -99) && (symbol->points[i].y == -99))
is_new = 1;
else {
if (is_new) {
path.move_to(symbol->points[i].x, symbol->points[i].y);
is_new = 0;
} else {
path.line_to(symbol->points[i].x, symbol->points[i].y);
}
}
}
return path;
}
int agg2RenderVectorSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style) {
AGG2Renderer *r = AGG_RENDERER(img);
double ox = symbol->sizex * 0.5;
double oy = symbol->sizey * 0.5;
mapserver::path_storage path = imageVectorSymbol(symbol);
mapserver::trans_affine mtx;
mtx *= mapserver::trans_affine_translation(-ox, -oy);
mtx *= mapserver::trans_affine_scaling(style->scale);
mtx *= mapserver::trans_affine_rotation(-style->rotation);
mtx *= mapserver::trans_affine_translation(x, y);
path.transform(mtx);
if (style->color) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
r->m_rasterizer_aa.add_path(path);
r->m_renderer_scanline.color(aggColor(style->color));
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
r->m_renderer_scanline);
}
if (style->outlinecolor) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
r->m_renderer_scanline.color(aggColor(style->outlinecolor));
mapserver::conv_stroke stroke(path);
stroke.width(style->outlinewidth);
r->m_rasterizer_aa.add_path(stroke);
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
r->m_renderer_scanline);
}
return MS_SUCCESS;
}
int agg2RenderPixmapSymbol(imageObj *img, double x, double y, symbolObj *symbol,
symbolStyleObj *style) {
AGG2Renderer *r = AGG_RENDERER(img);
rasterBufferObj *pixmap = symbol->pixmap_buffer;
assert(pixmap->type == MS_BUFFER_BYTE_RGBA);
rendering_buffer b(pixmap->data.rgba.pixels, pixmap->width, pixmap->height,
pixmap->data.rgba.row_step);
pixel_format pf(b);
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
if ((style->rotation != 0 && style->rotation != MS_PI * 2.) ||
style->scale != 1) {
mapserver::trans_affine image_mtx;
image_mtx *= mapserver::trans_affine_translation(-(pf.width() / 2.),
-(pf.height() / 2.));
/*agg angles are antitrigonometric*/
image_mtx *= mapserver::trans_affine_rotation(-style->rotation);
image_mtx *= mapserver::trans_affine_scaling(style->scale);
image_mtx *= mapserver::trans_affine_translation(x, y);
image_mtx.invert();
typedef mapserver::span_interpolator_linear interpolator_type;
interpolator_type interpolator(image_mtx);
mapserver::span_allocator sa;
// "hardcoded" bilinear filter
//------------------------------------------
typedef mapserver::span_image_filter_rgba_bilinear_clip
span_gen_type;
span_gen_type sg(pf, mapserver::rgba(0, 0, 0, 0), interpolator);
mapserver::path_storage pixmap_bbox;
int ims_2 =
MS_NINT(MS_MAX(pixmap->width, pixmap->height) * style->scale * 1.415) /
2 +
1;
pixmap_bbox.move_to(x - ims_2, y - ims_2);
pixmap_bbox.line_to(x + ims_2, y - ims_2);
pixmap_bbox.line_to(x + ims_2, y + ims_2);
pixmap_bbox.line_to(x - ims_2, y + ims_2);
r->m_rasterizer_aa.add_path(pixmap_bbox);
mapserver::render_scanlines_aa(r->m_rasterizer_aa, r->sl_poly,
r->m_renderer_base, sa, sg);
} else {
// just copy the image at the correct location (we place the pixmap on
// the nearest integer pixel to avoid blurring)
unsigned alpha = mapserver::cover_full;
if (style && style->color) {
alpha = style->color->alpha;
}
r->m_renderer_base.blend_from(pf, 0, MS_NINT(x - pixmap->width / 2.),
MS_NINT(y - pixmap->height / 2.), alpha);
}
return MS_SUCCESS;
}
int agg2RenderEllipseSymbol(imageObj *image, double x, double y,
symbolObj *symbol, symbolStyleObj *style) {
AGG2Renderer *r = AGG_RENDERER(image);
mapserver::path_storage path;
mapserver::ellipse ellipse(x, y, symbol->sizex * style->scale / 2,
symbol->sizey * style->scale / 2);
path.concat_path(ellipse);
if (style->rotation != 0) {
mapserver::trans_affine mtx;
mtx *= mapserver::trans_affine_translation(-x, -y);
/*agg angles are antitrigonometric*/
mtx *= mapserver::trans_affine_rotation(-style->rotation);
mtx *= mapserver::trans_affine_translation(x, y);
path.transform(mtx);
}
if (style->color) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_even_odd);
r->m_rasterizer_aa.add_path(path);
r->m_renderer_scanline.color(aggColor(style->color));
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_line,
r->m_renderer_scanline);
}
if (style->outlinewidth) {
r->m_rasterizer_aa.reset();
r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero);
mapserver::conv_stroke stroke(path);
stroke.width(style->outlinewidth);
r->m_rasterizer_aa.add_path(stroke);
r->m_renderer_scanline.color(aggColor(style->outlinecolor));
mapserver::render_scanlines(r->m_rasterizer_aa, r->sl_poly,
r->m_renderer_scanline);
}
return MS_SUCCESS;
}
int agg2RenderTile(imageObj * /*img*/, imageObj * /*tile*/, double /*x*/,
double /*y*/) {
/*
AGG2Renderer *imgRenderer = agg2GetRenderer(img);
AGG2Renderer *tileRenderer = agg2GetRenderer(tile);
*/
return MS_FAILURE;
}
int aggInitializeRasterBuffer(rasterBufferObj *rb, int width, int height,
int mode) {
rb->type = MS_BUFFER_BYTE_RGBA;
rb->data.rgba.pixel_step = 4;
rb->data.rgba.row_step = rb->data.rgba.pixel_step * width;
rb->width = width;
rb->height = height;
int nBytes = rb->data.rgba.row_step * height;
rb->data.rgba.pixels = (band_type *)msSmallCalloc(nBytes, sizeof(band_type));
rb->data.rgba.r = &(rb->data.rgba.pixels[band_order::R]);
rb->data.rgba.g = &(rb->data.rgba.pixels[band_order::G]);
rb->data.rgba.b = &(rb->data.rgba.pixels[band_order::B]);
if (mode == MS_IMAGEMODE_RGBA) {
rb->data.rgba.a = &(rb->data.rgba.pixels[band_order::A]);
}
return MS_SUCCESS;
}
int aggGetRasterBufferHandle(imageObj *img, rasterBufferObj *rb) {
AGG2Renderer *r = AGG_RENDERER(img);
rb->type = MS_BUFFER_BYTE_RGBA;
rb->data.rgba.pixels = r->buffer.data();
rb->data.rgba.row_step = r->m_rendering_buffer.stride();
rb->data.rgba.pixel_step = 4;
rb->width = r->m_rendering_buffer.width();
rb->height = r->m_rendering_buffer.height();
rb->data.rgba.r = &(r->buffer[band_order::R]);
rb->data.rgba.g = &(r->buffer[band_order::G]);
rb->data.rgba.b = &(r->buffer[band_order::B]);
if (r->use_alpha)
rb->data.rgba.a = &(r->buffer[band_order::A]);
else
rb->data.rgba.a = NULL;
return MS_SUCCESS;
}
int aggGetRasterBufferCopy(imageObj *img, rasterBufferObj *rb) {
AGG2Renderer *r = AGG_RENDERER(img);
aggInitializeRasterBuffer(rb, img->width, img->height, MS_IMAGEMODE_RGBA);
int nBytes = r->m_rendering_buffer.stride() * r->m_rendering_buffer.height();
memcpy(rb->data.rgba.pixels, r->buffer.data(), nBytes);
return MS_SUCCESS;
}
int agg2MergeRasterBuffer(imageObj *dest, rasterBufferObj *overlay,
double opacity, int srcX, int srcY, int dstX,
int dstY, int width, int height) {
assert(overlay->type == MS_BUFFER_BYTE_RGBA);
rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height,
overlay->data.rgba.row_step);
pixel_format pf(b);
AGG2Renderer *r = AGG_RENDERER(dest);
mapserver::rect_base src_rect(srcX, srcY, srcX + width, srcY + height);
r->m_renderer_base.blend_from(pf, &src_rect, dstX - srcX, dstY - srcY,
unsigned(opacity * 255));
return MS_SUCCESS;
}
/* image i/o */
imageObj *agg2CreateImage(int width, int height, outputFormatObj *format,
colorObj *bg) {
imageObj *image = NULL;
if (format->imagemode != MS_IMAGEMODE_RGB &&
format->imagemode != MS_IMAGEMODE_RGBA) {
msSetError(MS_MISCERR,
"AGG2 driver only supports RGB or RGBA pixel models.",
"agg2CreateImage()");
return image;
}
if (width > 0 && height > 0) {
image = (imageObj *)calloc(1, sizeof(imageObj));
MS_CHECK_ALLOC(image, sizeof(imageObj), NULL);
AGG2Renderer *r = new AGG2Renderer();
/* Compute size on 64bit and check that it is compatible of the platform
* size_t */
const AGG_INT64U bufSize64 =
(AGG_INT64U)width * height * 4 * sizeof(band_type);
if (bufSize64 > std::numeric_limits::max() / 2) {
msSetError(MS_MEMERR,
"%s: %d: Out of memory allocating " AGG_INT64U_FRMT
" bytes.\n",
"agg2CreateImage()", __FILE__, __LINE__, bufSize64);
free(image);
delete r;
return NULL;
}
const size_t bufSize = (size_t)bufSize64;
try {
r->buffer.resize(bufSize / sizeof(band_type));
} catch (const std::bad_alloc &) {
msSetError(MS_MEMERR,
"%s: %d: Out of memory allocating " AGG_INT64U_FRMT
" bytes.\n",
"agg2CreateImage()", __FILE__, __LINE__, bufSize64);
free(image);
delete r;
return NULL;
}
r->m_rendering_buffer.attach(r->buffer.data(), width, height, width * 4);
r->m_pixel_format.attach(r->m_rendering_buffer);
r->m_compop_pixel_format.attach(r->m_rendering_buffer);
r->m_renderer_base.attach(r->m_pixel_format);
r->m_compop_renderer_base.attach(r->m_compop_pixel_format);
r->m_renderer_scanline.attach(r->m_renderer_base);
r->m_renderer_scanline_aliased.attach(r->m_renderer_base);
r->default_gamma = atof(msGetOutputFormatOption(format, "GAMMA", "0.75"));
if (r->default_gamma default_gamma >= 1.0) {
r->default_gamma = 0.75;
}
r->gamma_function.set(0, r->default_gamma);
r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
if (bg && !format->transparent)
r->m_renderer_base.clear(aggColor(bg));
else
r->m_renderer_base.clear(AGG_NO_COLOR);
if (!bg || format->transparent || format->imagemode == MS_IMAGEMODE_RGBA) {
r->use_alpha = true;
} else {
r->use_alpha = false;
}
image->img.plugin = (void *)r;
} else {
msSetError(MS_RENDERERERR, "Cannot create cairo image of size %dx%d.",
"msImageCreateCairo()", width, height);
}
return image;
}
int agg2SaveImage(imageObj * /*img*/, mapObj * /*map*/, FILE * /*fp*/,
outputFormatObj * /*format*/) {
return MS_FAILURE;
}
int agg2StartNewLayer(imageObj *img, mapObj * /*map*/, layerObj *layer) {
AGG2Renderer *r = AGG_RENDERER(img);
const char *sgamma = msLayerGetProcessingKey(layer, "GAMMA");
double gamma;
if (sgamma) {
gamma = atof(sgamma);
if (gamma = 1)
gamma = 0.75;
} else {
gamma = r->default_gamma;
}
if (r->gamma_function.end() != gamma) {
r->gamma_function.end(gamma);
r->m_rasterizer_aa_gamma.gamma(r->gamma_function);
}
return MS_SUCCESS;
}
int agg2CloseNewLayer(imageObj * /*img*/, mapObj * /*map*/,
layerObj * /*layer*/) {
return MS_SUCCESS;
}
int agg2FreeImage(imageObj *image) {
AGG2Renderer *r = AGG_RENDERER(image);
delete r;
image->img.plugin = NULL;
return MS_SUCCESS;
}
int agg2FreeSymbol(symbolObj * /*symbol*/) { return MS_SUCCESS; }
int agg2InitCache(void **vcache) {
aggRendererCache *cache = new aggRendererCache();
*vcache = (void *)cache;
return MS_SUCCESS;
}
int agg2Cleanup(void *vcache) {
aggRendererCache *cache = (aggRendererCache *)vcache;
delete cache;
return MS_SUCCESS;
}
// ------------------------------------------------------------------------
// Function to create a custom hatch symbol based on an arbitrary angle.
// ------------------------------------------------------------------------
static mapserver::path_storage createHatch(double ox, double oy, double rx,
double ry, int sx, int sy,
double angle, double step) {
mapserver::path_storage path;
// restrict the angle to [0 180[, i.e ]-pi/2,pi/2] in radians
angle = fmod(angle, 360.0);
if (angle < 0)
angle += 360;
if (angle >= 180)
angle -= 180;
// treat 2 easy cases which would cause divide by 0 in generic case
if (angle == 0) {
double y0 = step - fmod(oy - ry, step);
if ((oy - ry) < 0) {
y0 -= step;
}
for (double y = y0; y < sy; y += step) {
path.move_to(0, y);
path.line_to(sx, y);
}
return path;
}
if (angle == 90) {
double x0 = step - fmod(ox - rx, step);
if ((ox - rx) < 0) {
x0 -= step;
}
for (double x = x0; x < sx; x += step) {
path.move_to(x, 0);
path.line_to(x, sy);
}
return path;
}
double theta = (90 - angle) * MS_DEG_TO_RAD; /* theta in ]-pi/2 , pi/2] */
double ct = cos(theta);
double st = sin(theta);
double invct = 1.0 / ct;
double invst = 1.0 / st;
double
r0; /* distance from first hatch line to the top-left (if angle in 0,pi/2)
or bottom-left (if angle in -pi/2,0) corner of the hatch bbox */
double rmax = sqrt((
double)(sx * sx +
sy *
sy)); /* distance to the furthest hatch we will have to create
TODO: this could be optimized for bounding boxes where width is very different
than height for certain hatch angles */
double rref =
rx * ct + ry * st; /* distance to the line passing through the refpoint,
origin is (0,0) of the imageObj */
double
rcorner; /* distance to the line passing through the topleft or bottomleft
corner of the hatch bbox (origin is (0,0) of imageObj) */
/* calculate the distance from the refpoint to the top right of the path */
if (angle < 90) {
rcorner = ox * ct + oy * st;
r0 = step - fmod(rcorner - rref, step);
if (rcorner - rref < 0)
r0 -= step;
} else {
rcorner = ox * ct + (oy + sy) * st;
r0 = step - fmod(rcorner - rref, step);
if (rcorner - rref < 0)
r0 -= step;
st = -st;
invst = -invst;
}
// parametrize each line as r = x.cos(theta) + y.sin(theta)
for (double r = r0; r < rmax; r += step) {
int inter = 0;
double x, y;
double pt[4]; // array to store the coordinates of intersection of the line
// with the sides
// in the general case there will only be two intersections
// so pt[4] should be sufficient to store the coordinates of the
// intersection, but we allocate pt[8] to treat the special and
// rare/unfortunate case when the line is a perfect diagonal (and therefore
// intersects all four sides) note that the order for testing is important
// in this case so that the first two intersection points actually
// correspond to the diagonal and not a degenerate line
// test for intersection with each side
y = r * invst;
x = 0; // test for intersection with left of image
if (y >= 0 && y = 0 && y = 0 && x = 0 && x format->renderer == MS_RENDER_WITH_AGG) {
AGG2Renderer *r = AGG_RENDERER(img);
r->m_rasterizer_aa_gamma.reset();
r->m_rasterizer_aa_gamma.filling_rule(mapserver::fill_non_zero);
r->m_rasterizer_aa_gamma.add_path(clipper);
r->m_renderer_scanline.color(aggColor(color));
mapserver::render_scanlines(r->m_rasterizer_aa_gamma, r->sl_poly,
r->m_renderer_scanline);
} else {
shapeObj shape;
msInitShape(&shape);
int allocated = 20;
lineObj line;
shape.line = &line;
shape.numlines = 1;
shape.line[0].point =
(pointObj *)msSmallCalloc(allocated, sizeof(pointObj));
shape.line[0].numpoints = 0;
double x = 0, y = 0;
unsigned int cmd;
clipper.rewind(0);
while ((cmd = clipper.vertex(&x, &y)) != mapserver::path_cmd_stop) {
switch (cmd) {
case mapserver::path_cmd_line_to:
if (shape.line[0].numpoints == allocated) {
allocated *= 2;
shape.line[0].point = (pointObj *)msSmallRealloc(
shape.line[0].point, allocated * sizeof(pointObj));
}
shape.line[0].point[shape.line[0].numpoints].x = x;
shape.line[0].point[shape.line[0].numpoints].y = y;
shape.line[0].numpoints++;
break;
case mapserver::path_cmd_move_to:
shape.line[0].point[0].x = x;
shape.line[0].point[0].y = y;
shape.line[0].numpoints = 1;
break;
case mapserver::path_cmd_end_poly | mapserver::path_flags_close:
if (shape.line[0].numpoints > 2) {
if (MS_UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(img)->renderPolygon(
img, &shape, color))) {
free(shape.line[0].point);
shape.numlines = 0;
shape.line = nullptr;
return MS_FAILURE;
}
}
break;
default:
assert(0); // WTF?
}
}
free(shape.line[0].point);
shape.numlines = 0;
shape.line = nullptr;
}
return MS_SUCCESS;
}
int msHatchPolygon(imageObj *img, shapeObj *poly, double spacing, double width,
double *pattern, int patternlength, double angle,
colorObj *color) {
assert(MS_RENDERER_PLUGIN(img->format));
msComputeBounds(poly);
/* amount we should expand the bounding box by */
double exp = width * 0.7072;
/* width and height of the bounding box we will be creating the hatch in */
int pw = (int)(poly->bounds.maxx - poly->bounds.minx + exp * 2) + 1;
int ph = (int)(poly->bounds.maxy - poly->bounds.miny + exp * 2) + 1;
/* position of the top-left corner of the bounding box */
double ox = poly->bounds.minx - exp;
double oy = poly->bounds.miny - exp;
// create a rectangular hatch of size pw,ph starting at 0,0
// the created hatch is of the size of the shape's bounding box
mapserver::path_storage hatch =
createHatch(ox, oy, img->refpt.x, img->refpt.y, pw, ph, angle, spacing);
if (hatch.total_vertices() 1) {
// dash the color-hatch and render it clipped by the shape
mapserver::conv_dash dash(hatch);
mapserver::conv_stroke
stroke(dash);
for (int i = 0; i < patternlength; i += 2) {
if (i < patternlength - 1) {
dash.add_dash(pattern[i], pattern[i + 1]);
}
}
stroke.width(width);
stroke.line_cap(mapserver::butt_cap);
mapserver::conv_clipper<
polygon_adaptor,
mapserver::conv_stroke>
clipper(polygons, stroke, mapserver::clipper_and);
renderPolygonHatches(img, clipper, color);
} else {
// render the hatch clipped by the shape
mapserver::conv_stroke stroke(hatch);
stroke.width(width);
stroke.line_cap(mapserver::butt_cap);
mapserver::conv_clipper
clipper(polygons, stroke, mapserver::clipper_and);
renderPolygonHatches(img, clipper, color);
}
// assert(prevCmd == mapserver::path_cmd_line_to);
// delete lines;
return MS_SUCCESS;
}
#ifdef USE_PIXMAN
static pixman_op_t ms2pixman_compop(CompositingOperation c) {
switch (c) {
case MS_COMPOP_CLEAR:
return PIXMAN_OP_CLEAR;
case MS_COMPOP_SRC:
return PIXMAN_OP_SRC;
case MS_COMPOP_DST:
return PIXMAN_OP_DST;
case MS_COMPOP_SRC_OVER:
return PIXMAN_OP_OVER;
case MS_COMPOP_DST_OVER:
return PIXMAN_OP_OVER_REVERSE;
case MS_COMPOP_SRC_IN:
return PIXMAN_OP_IN;
case MS_COMPOP_DST_IN:
return PIXMAN_OP_IN_REVERSE;
case MS_COMPOP_SRC_OUT:
return PIXMAN_OP_OUT;
case MS_COMPOP_DST_OUT:
return PIXMAN_OP_OUT_REVERSE;
case MS_COMPOP_SRC_ATOP:
return PIXMAN_OP_ATOP;
case MS_COMPOP_DST_ATOP:
return PIXMAN_OP_ATOP_REVERSE;
case MS_COMPOP_XOR:
return PIXMAN_OP_XOR;
case MS_COMPOP_PLUS:
return PIXMAN_OP_ADD;
case MS_COMPOP_MULTIPLY:
return PIXMAN_OP_MULTIPLY;
case MS_COMPOP_SCREEN:
return PIXMAN_OP_SCREEN;
case MS_COMPOP_OVERLAY:
return PIXMAN_OP_OVERLAY;
case MS_COMPOP_DARKEN:
return PIXMAN_OP_DARKEN;
case MS_COMPOP_LIGHTEN:
return PIXMAN_OP_LIGHTEN;
case MS_COMPOP_COLOR_DODGE:
return PIXMAN_OP_COLOR_DODGE;
case MS_COMPOP_COLOR_BURN:
return PIXMAN_OP_COLOR_DODGE;
case MS_COMPOP_HARD_LIGHT:
return PIXMAN_OP_HARD_LIGHT;
case MS_COMPOP_SOFT_LIGHT:
return PIXMAN_OP_SOFT_LIGHT;
case MS_COMPOP_DIFFERENCE:
return PIXMAN_OP_DIFFERENCE;
case MS_COMPOP_EXCLUSION:
return PIXMAN_OP_EXCLUSION;
case MS_COMPOP_HSL_HUE:
return PIXMAN_OP_HSL_HUE;
case MS_COMPOP_HSL_LUMINOSITY:
return PIXMAN_OP_HSL_LUMINOSITY;
case MS_COMPOP_HSL_SATURATION:
return PIXMAN_OP_HSL_SATURATION;
case MS_COMPOP_HSL_COLOR:
return PIXMAN_OP_HSL_COLOR;
case MS_COMPOP_INVERT:
case MS_COMPOP_INVERT_RGB:
case MS_COMPOP_MINUS:
case MS_COMPOP_CONTRAST:
default:
return PIXMAN_OP_OVER;
}
}
#endif
static mapserver::comp_op_e ms2agg_compop(CompositingOperation c) {
switch (c) {
case MS_COMPOP_CLEAR:
return mapserver::comp_op_clear;
case MS_COMPOP_SRC:
return mapserver::comp_op_src;
case MS_COMPOP_DST:
return mapserver::comp_op_dst;
case MS_COMPOP_SRC_OVER:
return mapserver::comp_op_src_over;
case MS_COMPOP_DST_OVER:
return mapserver::comp_op_dst_over;
case MS_COMPOP_SRC_IN:
return mapserver::comp_op_src_in;
case MS_COMPOP_DST_IN:
return mapserver::comp_op_dst_in;
case MS_COMPOP_SRC_OUT:
return mapserver::comp_op_src_out;
case MS_COMPOP_DST_OUT:
return mapserver::comp_op_dst_out;
case MS_COMPOP_SRC_ATOP:
return mapserver::comp_op_src_atop;
case MS_COMPOP_DST_ATOP:
return mapserver::comp_op_dst_atop;
case MS_COMPOP_XOR:
return mapserver::comp_op_xor;
case MS_COMPOP_PLUS:
return mapserver::comp_op_plus;
case MS_COMPOP_MINUS:
return mapserver::comp_op_minus;
case MS_COMPOP_MULTIPLY:
return mapserver::comp_op_multiply;
case MS_COMPOP_SCREEN:
return mapserver::comp_op_screen;
case MS_COMPOP_OVERLAY:
return mapserver::comp_op_overlay;
case MS_COMPOP_DARKEN:
return mapserver::comp_op_darken;
case MS_COMPOP_LIGHTEN:
return mapserver::comp_op_lighten;
case MS_COMPOP_COLOR_DODGE:
return mapserver::comp_op_color_dodge;
case MS_COMPOP_COLOR_BURN:
return mapserver::comp_op_color_burn;
case MS_COMPOP_HARD_LIGHT:
return mapserver::comp_op_hard_light;
case MS_COMPOP_SOFT_LIGHT:
return mapserver::comp_op_soft_light;
case MS_COMPOP_DIFFERENCE:
return mapserver::comp_op_difference;
case MS_COMPOP_EXCLUSION:
return mapserver::comp_op_exclusion;
case MS_COMPOP_CONTRAST:
return mapserver::comp_op_contrast;
case MS_COMPOP_INVERT:
return mapserver::comp_op_invert;
case MS_COMPOP_INVERT_RGB:
return mapserver::comp_op_invert_rgb;
case MS_COMPOP_HSL_HUE:
return mapserver::comp_op_hsl_hue;
case MS_COMPOP_HSL_LUMINOSITY:
return mapserver::comp_op_hsl_luminosity;
case MS_COMPOP_HSL_SATURATION:
return mapserver::comp_op_hsl_saturation;
case MS_COMPOP_HSL_COLOR:
return mapserver::comp_op_hsl_color;
default:
return mapserver::comp_op_src_over;
}
}
#ifdef USE_PIXMAN
static int aggCompositeRasterBufferPixman(imageObj *dest,
rasterBufferObj *overlay,
CompositingOperation comp,
int opacity) {
assert(overlay->type == MS_BUFFER_BYTE_RGBA);
AGG2Renderer *r = AGG_RENDERER(dest);
pixman_image_t *si = pixman_image_create_bits(
PIXMAN_a8r8g8b8, overlay->width, overlay->height,
(uint32_t *)overlay->data.rgba.pixels, overlay->data.rgba.row_step);
pixman_image_t *bi = pixman_image_create_bits(
PIXMAN_a8r8g8b8, dest->width, dest->height,
reinterpret_cast(&(r->buffer[0])), dest->width * 4);
pixman_image_t *alpha_mask_i = NULL, *alpha_mask_i_ptr;
pixman_image_set_filter(si, PIXMAN_FILTER_NEAREST, NULL, 0);
unsigned char *alpha_mask = NULL;
if (opacity > 0) {
if (opacity == 100) {
alpha_mask_i_ptr = NULL;
} else {
unsigned char alpha = (unsigned char)(MS_NINT(opacity * 2.55));
if (!alpha_mask_i) {
alpha_mask = (unsigned char *)msSmallMalloc(dest->width * dest->height);
alpha_mask_i =
pixman_image_create_bits(PIXMAN_a8, dest->width, dest->height,
(uint32_t *)alpha_mask, dest->width);
}
memset(alpha_mask, alpha, dest->width * dest->height);
alpha_mask_i_ptr = alpha_mask_i;
}
pixman_image_composite(ms2pixman_compop(comp), si, alpha_mask_i_ptr, bi, 0,
0, 0, 0, 0, 0, dest->width, dest->height);
}
pixman_image_unref(si);
pixman_image_unref(bi);
if (alpha_mask_i) {
pixman_image_unref(alpha_mask_i);
msFree(alpha_mask);
}
return MS_SUCCESS;
}
#endif
static int aggCompositeRasterBufferNoPixman(imageObj *dest,
rasterBufferObj *overlay,
CompositingOperation comp,
int opacity) {
assert(overlay->type == MS_BUFFER_BYTE_RGBA);
AGG2Renderer *r = AGG_RENDERER(dest);
rendering_buffer b(overlay->data.rgba.pixels, overlay->width, overlay->height,
overlay->data.rgba.row_step);
pixel_format pf(b);
mapserver::comp_op_e comp_op = ms2agg_compop(comp);
if (comp_op == mapserver::comp_op_src_over) {
r->m_renderer_base.blend_from(pf, 0, 0, 0,
unsigned(MS_NINT(opacity * 2.55)));
} else {
compop_pixel_format pixf(r->m_rendering_buffer);
compop_renderer_base ren(pixf);
pixf.comp_op(comp_op);
ren.blend_from(pf, 0, 0, 0, unsigned(MS_NINT(opacity * 2.55)));
}
return MS_SUCCESS;
}
void msApplyBlurringCompositingFilter(rasterBufferObj *rb,
unsigned int radius) {
rendering_buffer b(rb->data.rgba.pixels, rb->width, rb->height,
rb->data.rgba.row_step);
pixel_format pf(b);
mapserver::stack_blur_rgba32(pf, radius, radius);
}
int msPopulateRendererVTableAGG(rendererVTableObj *renderer) {
renderer->compositeRasterBuffer = &aggCompositeRasterBufferNoPixman;
#ifdef USE_PIXMAN
const char *pszUsePixman = CPLGetConfigOption("MS_USE_PIXMAN", "YES");
if (CPLTestBool(pszUsePixman)) {
renderer->compositeRasterBuffer = &aggCompositeRasterBufferPixman;
}
#endif
renderer->supports_pixel_buffer = 1;
renderer->use_imagecache = 0;
renderer->supports_clipping = 0;
renderer->supports_svg = 0;
renderer->default_transform_mode = MS_TRANSFORM_SIMPLIFY;
agg2InitCache(&(MS_RENDERER_CACHE(renderer)));
renderer->cleanup = agg2Cleanup;
renderer->renderLine = &agg2RenderLine;
renderer->renderPolygon = &agg2RenderPolygon;
renderer->renderPolygonTiled = &agg2RenderPolygonTiled;
renderer->renderLineTiled = &agg2RenderLineTiled;
renderer->renderGlyphs = &agg2RenderGlyphsPath;
renderer->renderVectorSymbol = &agg2RenderVectorSymbol;
renderer->renderPixmapSymbol = &agg2RenderPixmapSymbol;
renderer->renderEllipseSymbol = &agg2RenderEllipseSymbol;
renderer->renderTile = &agg2RenderTile;
renderer->getRasterBufferHandle = &aggGetRasterBufferHandle;
renderer->getRasterBufferCopy = aggGetRasterBufferCopy;
renderer->initializeRasterBuffer = aggInitializeRasterBuffer;
renderer->mergeRasterBuffer = &agg2MergeRasterBuffer;
renderer->loadImageFromFile = msLoadMSRasterBufferFromFile;
renderer->createImage = &agg2CreateImage;
renderer->saveImage = &agg2SaveImage;
renderer->startLayer = &agg2StartNewLayer;
renderer->endLayer = &agg2CloseNewLayer;
renderer->freeImage = &agg2FreeImage;
renderer->freeSymbol = &agg2FreeSymbol;
return MS_SUCCESS;
}