[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/jbfrd/MapServer/main/src/mapagg.cpp [Back]  [Original]

/******************************************************************************
 * $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

#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 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 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;
        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;

            FT_Vector v_start = outline.points[first];

            FT_Vector 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)
            {
                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; 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.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; ysl_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);
              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()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; itype == 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,
                       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)(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;

  return MS_SUCCESS;
}

Web Proxy Viewer  |  New URL  |  Original Page