/****************************************************************************** * $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" #ifdef USE_PIXMAN #include #endif #ifdef AGG_ALIASED_ENABLED #include "renderers/agg/include/agg_renderer_primitives.h" #include "renderers/agg/include/agg_rasterizer_outline.h" #endif 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 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::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; #ifdef AGG_ALIASED_ENABLED typedef mapserver::renderer_primitives renderer_primitives; typedef mapserver::rasterizer_outline rasterizer_outline; #endif static 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: AGG2Renderer() #ifdef AGG_ALIASED_ENABLED : m_renderer_primitives(m_renderer_base), m_rasterizer_primitives(m_renderer_primitives) #endif { stroke = NULL; dash = NULL; stroke_dash = NULL; } ~AGG2Renderer() { if(stroke) { delete stroke; } if(dash) { delete dash; } if(stroke_dash) { delete stroke_dash; } } band_type* 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; #ifdef AGG_ALIASED_ENABLED renderer_primitives m_renderer_primitives; rasterizer_outline m_rasterizer_primitives; #endif 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; mapserver::conv_stroke *stroke; mapserver::conv_dash *dash; mapserver::conv_stroke > *stroke_dash; double default_gamma; 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); #ifdef AGG_ALIASED_ENABLED r->m_rasterizer_primitives.reset(); r->m_renderer_primitives.line_color(aggColor(style->color)); r->m_rasterizer_primitives.add_path(lines); return MS_SUCCESS; #endif r->m_rasterizer_aa.reset(); r->m_rasterizer_aa.filling_rule(mapserver::fill_non_zero); r->m_renderer_scanline.color(aggColor(style->color)); if (style->patternlength <= 0) { if(!r->stroke) { r->stroke = 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 = 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 = 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); } 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 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) { FT_Vector v_last; FT_Vector v_control; FT_Vector v_start; double x1, y1, x2, y2, x3, y3; FT_Vector* point; FT_Vector* limit; char* tags; int n; // index of contour in outline int first; // index of first point in contour char tag; // current point's state first = 0; for(n = 0; n < outline.n_contours; n++) { int last; // index of last point in contour last = outline.contours[n]; limit = outline.points + last; v_start = outline.points[first]; v_last = outline.points[last]; v_control = v_start; point = outline.points + first; 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) { // 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; v_last = v_start; } 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 <= limit) { FT_Vector vec; vec.x = 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, textPathObj *tp, colorObj *c, colorObj *oc, int ow) { 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; inumglyphs; 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) r->m_renderer_base.blend_from(pf,0,MS_NINT(x-pixmap->width/2.),MS_NINT(y-pixmap->height/2.)); } 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; 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, 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; } 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 */ AGG_INT64U bufSize64 = (AGG_INT64U)width * height * 4 * sizeof(band_type); size_t bufSize = (size_t)bufSize64; if( (AGG_INT64U)bufSize != bufSize64 ) { 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->buffer = (band_type*)malloc(bufSize); if (r->buffer == NULL) { 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, 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->default_gamma = atof(msGetOutputFormatOption( format, "GAMMA", "0.75" )); if(r->default_gamma <= 0.0 || r->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; 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); char *sgamma = msLayerGetProcessingKey( layer, "GAMMA" ); double gamma; if(sgamma) { gamma = atof(sgamma); if(gamma <= 0 || 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); free(r->buffer); 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=0&&y<=sy) { pt[2*inter]=x; pt[2*inter+1]=y; inter++; } x=sx; y=(r-sx*ct)*invst;// test for intersection with right of image if(y>=0&&y<=sy) { pt[2*inter]=x; pt[2*inter+1]=y; inter++; } if(inter<2) { y=0; x=r*invct;// test for intersection with top of image if(x>=0&&x<=sx) { pt[2*inter]=x; pt[2*inter+1]=y; inter++; } } if(inter<2) { y=sy; x=(r-sy*st)*invct;// test for intersection with bottom of image if(x>=0&&x<=sx) { pt[2*inter]=x; pt[2*inter+1]=y; inter++; } } if(inter==2 && (pt[0]!=pt[2] || pt[1]!=pt[3])) { //the line intersects with two sides of the image, it should therefore be drawn if(angle<90) { path.move_to(pt[0],pt[1]); path.line_to(pt[2],pt[3]); } else { path.move_to(pt[0],sy-pt[1]); path.line_to(pt[2],sy-pt[3]); } } } return path; } template int renderPolygonHatches(imageObj *img,VertexSource &clipper, colorObj *color) { if(img->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(UNLIKELY(MS_FAILURE == MS_IMAGE_RENDERER(img)->renderPolygon(img,&shape,color))) { free(shape.line[0].point); return MS_FAILURE; } } break; default: assert(0); //WTF? } } free(shape.line[0].point); } 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()<=0) return MS_SUCCESS; //translate the hatch so it overlaps the current shape hatch.transform(mapserver::trans_affine_translation(ox,oy)); polygon_adaptor polygons(poly); if(patternlength>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 > > 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_INVERT: case MS_COMPOP_INVERT_RGB: case MS_COMPOP_MINUS: case MS_COMPOP_CONTRAST: default: return PIXMAN_OP_OVER; } } #else 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; default: return mapserver::comp_op_src_over; } } #endif int aggCompositeRasterBuffer(imageObj *dest, rasterBufferObj *overlay, CompositingOperation comp, int opacity) { assert(overlay->type == MS_BUFFER_BYTE_RGBA); AGG2Renderer *r = AGG_RENDERER(dest); #ifdef USE_PIXMAN 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, (uint32_t*)r->buffer,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)(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; #else 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(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(opacity * 2.55)); } return MS_SUCCESS; #endif } 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 = &aggCompositeRasterBuffer; 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; renderer->cleanup = agg2Cleanup; return MS_SUCCESS; }