///////////////////////////////////////////////////////////////////////////// // Name: SVGCanvasAgg.cpp // Purpose: Agg render // Author: Alex Thuering // Created: 2005/05/04 // RCS-ID: $Id: SVGCanvasAgg.cpp,v 1.10 2006/01/10 12:41:12 ntalex Exp $ // Copyright: (c) 2005 Alex Thuering // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #include "SVGCanvasAgg.h" #include "SVGCanvasPathAgg.h" #ifdef __WXMSW__ #include "SVGCanvasTextAgg.h" #else #include "../freetype/SVGCanvasTextFreetype.h" #endif #include #include #include #include #include #include #include #include wxSVGCanvasAgg::wxSVGCanvasAgg(): m_rbuf(NULL), m_pixf(NULL), m_rendererBase(NULL), m_rendererSolid(NULL) { } wxSVGCanvasAgg::~wxSVGCanvasAgg() { delete m_rbuf; delete m_pixf; delete m_rendererBase; delete m_rendererSolid; } void wxSVGCanvasAgg::Init(int width, int height) { m_image.Create(width, height); m_rbuf = new RenderingBuffer(m_image.GetData(), m_image.GetWidth(), m_image.GetHeight(), m_image.GetWidth()*3); m_pixf = new PixFmt(*m_rbuf); m_rendererBase = new RendererBase(*m_pixf); m_rendererSolid = new RendererSolid(*m_rendererBase); } wxImage wxSVGCanvasAgg::GetImage() { return m_image; } void wxSVGCanvasAgg::Clear(wxRGBColor color) { if (!m_rendererBase) return; m_rendererBase->clear(agg::rgba( color.Red()/255, color.Green()/255, color.Blue()/255)); } wxSVGCanvasPath* wxSVGCanvasAgg::CreateCanvasPath() { return new wxSVGCanvasPathAgg(); } wxSVGCanvasItem* wxSVGCanvasAgg::CreateItem(wxSVGTextElement* element, const wxCSSStyleDeclaration* style) { #ifdef __WXMSW__ wxSVGCanvasText* canvasText = new wxSVGCanvasTextAgg(this); #else wxSVGCanvasText* canvasText = new wxSVGCanvasTextFreetype(this); #endif if (style == NULL) style = (wxCSSStyleDeclaration*) &element->GetStyle(); canvasText->Init(*element, *style); return canvasText; } void wxSVGCanvasAgg::DrawItem(wxSVGCanvasItem& item, wxSVGMatrix& matrix, const wxCSSStyleDeclaration& style, wxSVGSVGElement& svgElem) { switch (item.GetType()) { case wxSVGCanvasItem::wxSVG_CANVAS_ITEM_PATH: DrawCanvasPath((wxSVGCanvasPathAgg&) item, matrix, style, svgElem); break; case wxSVGCanvasItem::wxSVG_CANVAS_ITEM_TEXT: DrawCanvasText((wxSVGCanvasText&) item, matrix, style, svgElem); break; case wxSVGCanvasItem::wxSVG_CANVAS_ITEM_IMAGE: DrawCanvasImage((wxSVGCanvasImage&) item, matrix, style, svgElem); break; } } void wxSVGCanvasAgg::DrawCanvasPath(wxSVGCanvasPathAgg& canvasPath, wxSVGMatrix& matrix, const wxCSSStyleDeclaration& style, wxSVGSVGElement& svgElem) { if (!m_rendererBase) return; rasterizer_scanline_aa<> ras; const rect& cb = m_rendererBase->clip_box(); ras.clip_box(cb.x1, cb.y1, cb.x2+1, cb.y2+1); canvasPath.m_transform = trans_affine(matrix.GetA(), matrix.GetB(), matrix.GetC(), matrix.GetD(), matrix.GetE(), matrix.GetF()); if (style.GetFill().Ok()) { ras.reset(); ras.filling_rule( style.GetFillRule() == wxCSS_VALUE_EVENODD ? fill_even_odd : fill_non_zero); if (fabs(canvasPath.m_curved_trans_contour.width()) < 0.0001) ras.add_path(canvasPath.m_curved_trans); else { canvasPath.m_curved_trans_contour.miter_limit(style.GetStrokeMiterlimit()); ras.add_path(canvasPath.m_curved_trans_contour); } PaintRasterizer(ras, style.GetFill(), style.GetOpacity()*style.GetFillOpacity(), matrix, svgElem, canvasPath); } if (style.GetStroke().Ok() && style.GetStrokeWidth()>0) { canvasPath.SetStrokeStyle(style); ras.reset(); ras.filling_rule(fill_non_zero); ras.add_path(canvasPath.m_curved_stroked_trans); PaintRasterizer(ras, style.GetStroke(), style.GetOpacity()*style.GetStrokeOpacity(), matrix, svgElem, canvasPath); } } class GradientPolymorphicWrapperBase { public: virtual int calculate(int x, int y, int) const = 0; }; template class GradientPolymorphicWrapper : public GradientPolymorphicWrapperBase { public: GradientPolymorphicWrapper() {} virtual int calculate(int x, int y, int d) const { return m_gradient.calculate(x, y, d); } GradientF m_gradient; }; struct ColorFunctionProfile { ColorFunctionProfile() {} ColorFunctionProfile(const rgba8* colors): m_colors(colors) {} static unsigned size() { return 256; } const rgba8& operator [] (unsigned v) const { return m_colors[v]; } const rgba8* m_colors; }; void wxSVGCanvasAgg::AllocateGradientStops(unsigned int stop_count) { m_r = m_g = m_b = 0; m_a = 255; m_offset = -1; } void wxSVGCanvasAgg::SetStopValue(unsigned int index, float offset, float opacity, const wxRGBColor& rgbColor) { int r = rgbColor.Red(); int g = rgbColor.Green(); int b = rgbColor.Blue(); int a = int(floor(int(opacity * 255.0) + 0.5)); unsigned int start = m_offset == -1 ? 0 : int(m_offset * 255); int end = int(offset * 255); if (m_offset == -1) { m_r = r; m_g = g; m_b = b; m_a = a; } int diffr = r - m_r; int diffg = g - m_g; int diffb = b - m_b; int diffa = a - m_a; unsigned int nsteps = end - start; for (unsigned int i = 0; i <= nsteps; i++) { double diff = double(i) / double(nsteps); m_colorProfile[start + i].r = int(m_r + diff * diffr); m_colorProfile[start + i].g = int(m_g + diff * diffg); m_colorProfile[start + i].b = int(m_b + diff * diffb); m_colorProfile[start + i].a = int(m_a + diff * diffa); } m_r = r; m_g = g; m_b = b; m_a = a; m_offset = offset; } void wxSVGCanvasAgg::PaintRasterizer(rasterizer_scanline_aa<>& ras, const wxSVGPaint& paint, float opacity, wxSVGMatrix& matrix, const wxSVGSVGElement& svgElem, wxSVGCanvasPath& path) { scanline_p8 sl; if (paint.GetPaintType() >= wxSVG_PAINTTYPE_URI_NONE && paint.GetPaintType() <= wxSVG_PAINTTYPE_URI) { const wxSVGElement* refElem; int m_nstops = GetGradientStops(svgElem, paint.GetUri(), opacity, refElem); if (m_nstops) { for (unsigned int i = int(m_offset * 255); i <= 255; i++) { m_colorProfile[i].r = m_r; m_colorProfile[i].g = m_g; m_colorProfile[i].b = m_b; m_colorProfile[i].a = m_a; } GradientPolymorphicWrapper gr_x; GradientPolymorphicWrapper gr_circle; GradientPolymorphicWrapperBase* gr_ptr = &gr_x; wxSVGMatrix rgMatrix = matrix; double length = 0; switch (refElem->GetDtd()) { case wxSVG_LINEARGRADIENT_ELEMENT: { wxSVGLinearGradientElement* gradElem = (wxSVGLinearGradientElement*)refElem; wxSVGPoint p1, p2; GetLinearGradientVector(p1, p2, *gradElem, path); double dx = p2.GetX() - p1.GetX(); double dy = p2.GetY() - p1.GetY(); rgMatrix = rgMatrix.Translate(p1.GetX(), p1.GetY()); rgMatrix = rgMatrix.Rotate(atan2(dy, dx)*180/M_PI); length = sqrt(dx * dx + dy * dy); break; } case wxSVG_RADIALGRADIENT_ELEMENT: { gr_ptr = &gr_circle; wxSVGRadialGradientElement* gradElem = (wxSVGRadialGradientElement*)refElem; wxSVGPoint focus; GetRadialGradientTransform(focus, rgMatrix, *gradElem, path); length = gradElem->GetR().GetAnimVal(); break; } default: break; } rgMatrix = rgMatrix.Inverse(); trans_affine mtx_g1(rgMatrix.GetA(), rgMatrix.GetB(), rgMatrix.GetC(), rgMatrix.GetD(), rgMatrix.GetE(), rgMatrix.GetF()); typedef span_interpolator_linear<> InterpolatorType; typedef span_gradient GradientSpanGen; typedef span_allocator GradientSpanAlloc; typedef renderer_scanline_aa RendererGradient; GradientSpanAlloc spanAlloc; ColorFunctionProfile colors(m_colorProfile); InterpolatorType inter(mtx_g1); GradientSpanGen spanGen(spanAlloc, inter, *gr_ptr, colors, 0, length); RendererGradient r1(*m_rendererBase, spanGen); agg::render_scanlines(ras, sl, r1); return; } if (paint.GetPaintType() == wxSVG_PAINTTYPE_URI_NONE) return; } const wxRGBColor& c = paint.GetRGBColor(); rgba8 color(c.Red(), c.Green(), c.Blue()); color.opacity(opacity); m_rendererSolid->color(color); agg::render_scanlines(ras, sl, *m_rendererSolid); } class SpanConvAlpha { public: SpanConvAlpha::SpanConvAlpha(double alpha) { m_alpha = alpha; } void convert(rgba8* colors, int x, int y, unsigned len) const { while (len) { colors->a = (unsigned char) (m_alpha*colors->a); colors++; len--; } } protected: double m_alpha; }; void wxSVGCanvasAgg::DrawCanvasImage(wxSVGCanvasImage& canvasImage, wxSVGMatrix& matrix, const wxCSSStyleDeclaration& style, wxSVGSVGElement& svgElem) { if (!canvasImage.m_image.Ok() || canvasImage.m_image.GetWidth() == 0 || canvasImage.m_image.GetHeight() == 0) return; // source_buffer rendering_buffer source_buffer; source_buffer.attach(canvasImage.m_image.GetData(), canvasImage.m_image.GetWidth(), canvasImage.m_image.GetHeight(), canvasImage.m_image.GetWidth()*3); //transform wxSVGMatrix transMatrix = matrix.Translate(canvasImage.m_x, canvasImage.m_y); transMatrix = transMatrix.ScaleNonUniform( canvasImage.m_width/canvasImage.m_image.GetWidth(), canvasImage.m_height/canvasImage.m_image.GetHeight()); trans_affine transform(transMatrix.GetA(), transMatrix.GetB(), transMatrix.GetC(), transMatrix.GetD(), transMatrix.GetE(), transMatrix.GetF()); double x1 = 0; double y1 = 0; double x2 = canvasImage.m_image.GetWidth(); double y2 = canvasImage.m_image.GetHeight(); transform.transform(&x1, &y1); transform.transform(&x2, &y2); transform.invert(); scanline_u8 sl; // better as scanline_p8 because the number of spans rasterizer_scanline_aa<> ras; const rect& cb = m_rendererBase->clip_box(); ras.clip_box(cb.x1, cb.y1, cb.x2+1, cb.y2+1); path_storage imgRect; imgRect.move_to(x1, y1); imgRect.line_to(x1, y2); imgRect.line_to(x2, y2); imgRect.line_to(x2, y1); imgRect.close_polygon(); ras.add_path(imgRect); double opacity = style.GetOpacity(); if (opacity == 1) { typedef span_interpolator_linear<> InterpolatorType; typedef span_image_filter_rgb_bilinear SpanGenType; typedef renderer_scanline_aa RendererType; InterpolatorType interpolator(transform); span_allocator sa; SpanGenType sg(sa, source_buffer, rgba(1, 1, 1, 0), interpolator); RendererType ri(*m_rendererBase, sg); render_scanlines(ras, sl, ri); } else { typedef span_interpolator_linear<> InterpolatorType; typedef span_image_filter_rgb_bilinear SpanGenType; typedef span_converter SpanConv; typedef renderer_scanline_aa RendererType; InterpolatorType interpolator(transform); span_allocator sa; SpanGenType sg(sa, source_buffer, rgba(1, 1, 1, 0), interpolator); SpanConvAlpha spanConvAlpha(opacity); SpanConv sc(sg, spanConvAlpha); RendererType ri(*m_rendererBase, sc); render_scanlines(ras, sl, ri); } }