diff options
Diffstat (limited to 'src/sp-gradient.cpp')
| -rw-r--r-- | src/sp-gradient.cpp | 646 |
1 files changed, 115 insertions, 531 deletions
diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 830e12f53..0acdac76e 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -23,16 +23,12 @@ #include <cstring> #include <string> -#include <libnr/nr-matrix-fns.h> -#include <libnr/nr-matrix-ops.h> -#include <libnr/nr-matrix-scale-ops.h> #include <2geom/transforms.h> #include <sigc++/functors/ptr_fun.h> #include <sigc++/adaptors/bind.h> -#include "libnr/nr-gradient.h" -#include "libnr/nr-pixops.h" +#include "display/cairo-utils.h" #include "svg/svg.h" #include "svg/svg-color.h" #include "svg/css-ostringstream.h" @@ -399,8 +395,6 @@ void SPGradientImpl::init(SPGradient *gr) gr->vector.built = false; gr->vector.stops.clear(); - gr->color = NULL; - new (&gr->modified_connection) sigc::connection(); } @@ -460,11 +454,6 @@ void SPGradientImpl::release(SPObject *object) gradient->ref = NULL; } - if (gradient->color) { - g_free(gradient->color); - gradient->color = NULL; - } - gradient->modified_connection.~connection(); if (((SPObjectClass *) gradient_parent_class)->release) @@ -662,7 +651,7 @@ void SPGradientImpl::modified(SPObject *object, guint flags) } if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { - gr->ensureColors(); + gr->ensureVector(); } if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; @@ -1006,12 +995,6 @@ bool SPGradient::invalidateVector() { bool ret = false; - if (color != NULL) { - g_free(color); - color = NULL; - ret = true; - } - if (vector.built) { vector.built = false; vector.stops.clear(); @@ -1119,303 +1102,6 @@ void SPGradient::rebuildVector() vector.built = true; } -/** - * The gradient's color array is newly created and set up from vector. - * Also, the gradient's color_grayscale is created. - */ -void SPGradient::ensureColors() -{ - if (!vector.built) { - rebuildVector(); - } - g_return_if_fail(!vector.stops.empty()); - - /// \todo Where is the memory freed? - if (!color) { - color = g_new(guchar, 4 * NCOLORS); - } - if (!color_grayscale) { - color_grayscale = g_new(guchar, 4 * NCOLORS); - } - - // This assumes that vector is a zero-order B-spline (box function) approximation of the "true" gradient. - // This means that the "true" gradient must be prefiltered using a zero order B-spline and then sampled. - // Furthermore, the first element corresponds to offset="0" and the last element to offset="1". - - double remainder[4] = {0,0,0,0}; - double remainder_for_end[4] = {0,0,0,0}; // Used at the end - switch(spread) { - case SP_GRADIENT_SPREAD_PAD: - remainder[0] = 0.5*vector.stops[0].color.v.c[0]; // Half of the first cell uses the color of the first stop - remainder[1] = 0.5*vector.stops[0].color.v.c[1]; - remainder[2] = 0.5*vector.stops[0].color.v.c[2]; - remainder[3] = 0.5*vector.stops[0].opacity; - remainder_for_end[0] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[0]; // Half of the first cell uses the color of the last stop - remainder_for_end[1] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[1]; - remainder_for_end[2] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[2]; - remainder_for_end[3] = 0.5*vector.stops[vector.stops.size() - 1].opacity; - break; - case SP_GRADIENT_SPREAD_REFLECT: - case SP_GRADIENT_SPREAD_REPEAT: - // These two are handled differently, see below. - break; - default: - g_error("Spread type not supported!"); - }; - for (unsigned int i = 0; i < vector.stops.size() - 1; i++) { - double r0 = vector.stops[i].color.v.c[0]; - double g0 = vector.stops[i].color.v.c[1]; - double b0 = vector.stops[i].color.v.c[2]; - double a0 = vector.stops[i].opacity; - double r1 = vector.stops[i+1].color.v.c[0]; - double g1 = vector.stops[i+1].color.v.c[1]; - double b1 = vector.stops[i+1].color.v.c[2]; - double a1 = vector.stops[i+1].opacity; - double o0 = vector.stops[i].offset * (NCOLORS-1); - double o1 = vector.stops[i + 1].offset * (NCOLORS-1); - unsigned int ob = (unsigned int) floor(o0+.5); // These are the first and last element that might be affected by this interval. - unsigned int oe = (unsigned int) floor(o1+.5); // These need to be computed the same to ensure that ob will be covered by the next interval if oe==ob - - if (oe == ob) { - // Simple case, this interval starts and stops within one cell - // The contribution of this interval is: - // (o1-o0)*(c(o0)+c(o1))/2 - // = (o1-o0)*(c0+c1)/2 - double dt = 0.5*(o1-o0); - remainder[0] += dt*(r0 + r1); - remainder[1] += dt*(g0 + g1); - remainder[2] += dt*(b0 + b1); - remainder[3] += dt*(a0 + a1); - } else { - // First compute colors for the cells which are fully covered by the current interval. - // The prefiltered values are equal to the midpoint of each cell here. - // f = (j-o0)/(o1-o0) - // = j*(1/(o1-o0)) - o0/(o1-o0) - double f = (ob-o0) / (o1-o0); - double df = 1. / (o1-o0); - for (unsigned int j = ob+1; j < oe; j++) { - f += df; - color[4 * j + 0] = (unsigned char) floor(255*(r0 + f*(r1-r0)) + .5); - color[4 * j + 1] = (unsigned char) floor(255*(g0 + f*(g1-g0)) + .5); - color[4 * j + 2] = (unsigned char) floor(255*(b0 + f*(b1-b0)) + .5); - color[4 * j + 3] = (unsigned char) floor(255*(a0 + f*(a1-a0)) + .5); - } - - // Now handle the beginning - // The contribution of the last point is already in remainder. - // The contribution of this point is: - // (ob+.5-o0)*(c(o0)+c(ob+.5))/2 - // = (ob+.5-o0)*c((o0+ob+.5)/2) - // = (ob+.5-o0)*(c0+((o0+ob+.5)/2-o0)*df*(c1-c0)) - // = (ob+.5-o0)*(c0+(ob+.5-o0)*df*(c1-c0)/2) - double dt = ob+.5-o0; - f = 0.5*dt*df; - if (ob==0 && spread==SP_GRADIENT_SPREAD_REFLECT) { - // The first half of the first cell is just a mirror image of the second half, so simply multiply it by 2. - color[4 * ob + 0] = (unsigned char) floor(2*255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); - color[4 * ob + 1] = (unsigned char) floor(2*255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); - color[4 * ob + 2] = (unsigned char) floor(2*255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); - color[4 * ob + 3] = (unsigned char) floor(2*255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); - } else if (ob==0 && spread==SP_GRADIENT_SPREAD_REPEAT) { - // The first cell is the same as the last cell, so save whatever is in the second half here and deal with the rest later. - remainder_for_end[0] = remainder[0] + dt*(r0 + f*(r1-r0)); - remainder_for_end[1] = remainder[1] + dt*(g0 + f*(g1-g0)); - remainder_for_end[2] = remainder[2] + dt*(b0 + f*(b1-b0)); - remainder_for_end[3] = remainder[3] + dt*(a0 + f*(a1-a0)); - } else { - // The first half of the cell was already in remainder. - color[4 * ob + 0] = (unsigned char) floor(255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); - color[4 * ob + 1] = (unsigned char) floor(255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); - color[4 * ob + 2] = (unsigned char) floor(255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); - color[4 * ob + 3] = (unsigned char) floor(255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); - } - - // Now handle the end, which should end up in remainder - // The contribution of this point is: - // (o1-oe+.5)*(c(o1)+c(oe-.5))/2 - // = (o1-oe+.5)*c((o1+oe-.5)/2) - // = (o1-oe+.5)*(c0+((o1+oe-.5)/2-o0)*df*(c1-c0)) - dt = o1-oe+.5; - f = (0.5*(o1+oe-.5)-o0)*df; - remainder[0] = dt*(r0 + f*(r1-r0)); - remainder[1] = dt*(g0 + f*(g1-g0)); - remainder[2] = dt*(b0 + f*(b1-b0)); - remainder[3] = dt*(a0 + f*(a1-a0)); - } - } - switch(spread) { - case SP_GRADIENT_SPREAD_PAD: - color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); - color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); - color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); - color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); - break; - case SP_GRADIENT_SPREAD_REFLECT: - // The second half is the same as the first half, so multiply by 2. - color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(2*255*remainder[0] + .5); - color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(2*255*remainder[1] + .5); - color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(2*255*remainder[2] + .5); - color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(2*255*remainder[3] + .5); - break; - case SP_GRADIENT_SPREAD_REPEAT: - // The second half is the same as the second half of the first cell (which was saved in remainder_for_end). - color[0] = color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); - color[1] = color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); - color[2] = color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); - color[3] = color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); - break; - } - - // Fill color_grayscale array - for (unsigned j = 0; j < NCOLORS; j++) { - guint32 rgba = Grayscale::process(color[4 * j + 0], color[4 * j + 1], color[4 * j + 2], color[4 * j + 3]); - color_grayscale[4 * j + 0] = SP_RGBA32_R_U(rgba); - color_grayscale[4 * j + 1] = SP_RGBA32_G_U(rgba); - color_grayscale[4 * j + 2] = SP_RGBA32_B_U(rgba); - color_grayscale[4 * j + 3] = SP_RGBA32_A_U(rgba); - } -} - -/** - * Renders gradient vector to buffer as line. - * - * RGB buffer background should be set up beforehand. - * - * @param len,width,height,rowstride Buffer parameters (1 or 2 dimensional). - * @param span Full integer width of requested gradient. - * @param pos Buffer starting position in span. - */ -static void -sp_gradient_render_vector_line_rgba(SPGradient *const gradient, guchar *buf, - gint const len, gint const pos, gint const span) -{ - g_return_if_fail(gradient != NULL); - g_return_if_fail(SP_IS_GRADIENT(gradient)); - g_return_if_fail(buf != NULL); - g_return_if_fail(len > 0); - g_return_if_fail(pos >= 0); - g_return_if_fail(pos + len <= span); - g_return_if_fail(span > 0); - - if (!gradient->color) { - gradient->ensureColors(); - } - - gint idx = (pos * 1024 << 8) / span; - gint didx = (1024 << 8) / span; - - bool grayscale = false; // this rendering is for UI items, like the gradient edit dialog - if (grayscale) { - for (gint x = 0; x < len; x++) { - guchar luminance = Grayscale::luminance( gradient->color[4 * (idx >> 8)], gradient->color[4 * (idx >> 8) + 1], gradient->color[4 * (idx >> 8) + 2] ); - *buf++ = luminance; - *buf++ = luminance; - *buf++ = luminance; - *buf++ = gradient->color[4 * (idx >> 8) + 3]; - idx += didx; - } - } else { - for (gint x = 0; x < len; x++) { - /// \todo Can this be done with 4 byte copies? - *buf++ = gradient->color[4 * (idx >> 8)]; - *buf++ = gradient->color[4 * (idx >> 8) + 1]; - *buf++ = gradient->color[4 * (idx >> 8) + 2]; - *buf++ = gradient->color[4 * (idx >> 8) + 3]; - idx += didx; - } - } -} - -/** - * Render rectangular RGBA area from gradient vector. - */ -void -sp_gradient_render_vector_block_rgba(SPGradient *const gradient, guchar *buf, - gint const width, gint const height, gint const rowstride, - gint const pos, gint const span, bool const horizontal) -{ - g_return_if_fail(gradient != NULL); - g_return_if_fail(SP_IS_GRADIENT(gradient)); - g_return_if_fail(buf != NULL); - g_return_if_fail(width > 0); - g_return_if_fail(height > 0); - g_return_if_fail(pos >= 0); - g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span))); - g_return_if_fail(span > 0); - - if (horizontal) { - sp_gradient_render_vector_line_rgba(gradient, buf, width, pos, span); - for (gint y = 1; y < height; y++) { - memcpy(buf + y * rowstride, buf, 4 * width); - } - } else { - guchar *tmp = (guchar *)alloca(4 * height); - sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span); - for (gint y = 0; y < height; y++) { - guchar *b = buf + y * rowstride; - for (gint x = 0; x < width; x++) { - *b++ = tmp[0]; - *b++ = tmp[1]; - *b++ = tmp[2]; - *b++ = tmp[3]; - } - tmp += 4; - } - } -} - -/** - * Render rectangular RGB area from gradient vector. - */ -void -sp_gradient_render_vector_block_rgb(SPGradient *gradient, guchar *buf, - gint const width, gint const height, gint const /*rowstride*/, - gint const pos, gint const span, bool const horizontal) -{ - g_return_if_fail(gradient != NULL); - g_return_if_fail(SP_IS_GRADIENT(gradient)); - g_return_if_fail(buf != NULL); - g_return_if_fail(width > 0); - g_return_if_fail(height > 0); - g_return_if_fail(pos >= 0); - g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span))); - g_return_if_fail(span > 0); - - if (horizontal) { - guchar *tmp = (guchar*)alloca(4 * width); - sp_gradient_render_vector_line_rgba(gradient, tmp, width, pos, span); - for (gint y = 0; y < height; y++) { - guchar *t = tmp; - for (gint x = 0; x < width; x++) { - gint a = t[3]; - gint fc = (t[0] - buf[0]) * a; - buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (t[1] - buf[1]) * a; - buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (t[2] - buf[2]) * a; - buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8); - buf += 3; - t += 4; - } - } - } else { - guchar *tmp = (guchar*)alloca(4 * height); - sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span); - for (gint y = 0; y < height; y++) { - guchar *t = tmp + 4 * y; - for (gint x = 0; x < width; x++) { - gint a = t[3]; - gint fc = (t[0] - buf[0]) * a; - buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (t[1] - buf[1]) * a; - buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (t[2] - buf[2]) * a; - buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8); - } - } - } -} - Geom::Affine sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Affine const &ctm, Geom::Rect const &bbox) { @@ -1460,21 +1146,6 @@ sp_gradient_set_gs2d_matrix(SPGradient *gr, Geom::Affine const &ctm, * Linear Gradient */ -class SPLGPainter; - -/// A context with linear gradient, painter, and gradient renderer. -struct SPLGPainter { - SPPainter painter; - SPLinearGradient *lg; - - NRLGradientRenderer lgr; - - static SPPainter * painter_new(SPPaintServer *ps, - Geom::Affine const &full_transform, - Geom::Affine const &parent_transform, - NRRect const *bbox); -}; - static void sp_lineargradient_class_init(SPLinearGradientClass *klass); static void sp_lineargradient_init(SPLinearGradient *lg); @@ -1484,10 +1155,7 @@ static void sp_lineargradient_build(SPObject *object, static void sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value); static Inkscape::XML::Node *sp_lineargradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); - -static void sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter); - -static void sp_lg_fill(SPPainter *painter, NRPixBlock *pb); +static cairo_pattern_t *sp_lineargradient_create_pattern(SPPaintServer *ps, cairo_t *ct, Geom::OptRect const &bbox, double opacity); static SPGradientClass *lg_parent_class; @@ -1528,8 +1196,7 @@ static void sp_lineargradient_class_init(SPLinearGradientClass *klass) sp_object_class->set = sp_lineargradient_set; sp_object_class->write = sp_lineargradient_write; - ps_class->painter_new = SPLGPainter::painter_new; - ps_class->painter_free = sp_lineargradient_painter_free; + ps_class->pattern_new = sp_lineargradient_create_pattern; } /** @@ -1619,86 +1286,6 @@ sp_lineargradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inks } /** - * Create linear gradient context. - * - * Basically we have to deal with transformations - * - * 1) color2norm - maps point in (0,NCOLORS) vector to (0,1) vector - * 2) norm2pos - maps (0,1) vector to x1,y1 - x2,y2 - * 2) gradientTransform - * 3) bbox2user - * 4) ctm == userspace to pixel grid - * - * See also (*) in sp-pattern about why we may need parent_transform. - * - * \todo (point 1 above) fixme: I do not know how to deal with start > 0 - * and end < 1. - */ -SPPainter * SPLGPainter::painter_new(SPPaintServer *ps, - Geom::Affine const &full_transform, - Geom::Affine const &/*parent_transform*/, - NRRect const *bbox) -{ - SPLinearGradient *lg = SP_LINEARGRADIENT(ps); - SPGradient *gr = SP_GRADIENT(ps); - - if (!gr->color) { - gr->ensureColors(); - } - - SPLGPainter *lgp = g_new(SPLGPainter, 1); - - lgp->painter.type = SP_PAINTER_IND; - lgp->painter.fill = sp_lg_fill; - - lgp->lg = lg; - - /** \todo - * Technically speaking, we map NCOLORS on line [start,end] onto line - * [0,1]. I almost think we should fill color array start and end in - * that case. The alternative would be to leave these just empty garbage - * or something similar. Originally I had 1023.9999 here - not sure - * whether we have really to cut out ceil int (Lauris). - */ - Geom::Affine color2norm(Geom::identity()); - Geom::Affine color2px; - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { - Geom::Affine norm2pos(Geom::identity()); - - /* BBox to user coordinate system */ - Geom::Affine bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0); - - Geom::Affine color2pos = color2norm * norm2pos; - Geom::Affine color2tpos = color2pos * gr->gradientTransform; - Geom::Affine color2user = color2tpos * bbox2user; - color2px = color2user * full_transform; - - } else { - /* Problem: What to do, if we have mixed lengths and percentages? */ - /* Currently we do ignore percentages at all, but that is not good (lauris) */ - - Geom::Affine norm2pos(Geom::identity()); - Geom::Affine color2pos = color2norm * norm2pos; - Geom::Affine color2tpos = color2pos * gr->gradientTransform; - color2px = color2tpos * full_transform; - - } - // TODO: remove color2px_nr after converting to 2geom - NR::Matrix color2px_nr = from_2geom(color2px); - nr_lgradient_renderer_setup(&lgp->lgr, gr->color, gr->fetchSpread(), &color2px_nr, - lg->x1.computed, lg->y1.computed, - lg->x2.computed, lg->y2.computed); - - return (SPPainter *) lgp; -} - -static void -sp_lineargradient_painter_free(SPPaintServer */*ps*/, SPPainter *painter) -{ - g_free(painter); -} - -/** * Directly set properties of linear gradient and request modified. */ void @@ -1718,45 +1305,10 @@ sp_lineargradient_set_position(SPLinearGradient *lg, lg->requestModified(SP_OBJECT_MODIFIED_FLAG); } -/** - * Callback when linear gradient object is rendered. - */ -static void -sp_lg_fill(SPPainter *painter, NRPixBlock *pb) -{ - SPLGPainter *lgp = (SPLGPainter *) painter; - - if (lgp->lg->color == NULL) { - lgp->lg->ensureColors(); - } - bool grayscale = Grayscale::activeDesktopIsGrayscale(); // TODO: find good way to access the current rendermode - if (grayscale) { - lgp->lgr.vector = lgp->lg->color_grayscale; - } else { - lgp->lgr.vector = lgp->lg->color; - } - - nr_render((NRRenderer *) &lgp->lgr, pb, NULL); -} - /* * Radial Gradient */ -class SPRGPainter; - -/// A context with radial gradient, painter, and gradient renderer. -struct SPRGPainter { - SPPainter painter; - SPRadialGradient *rg; - NRRGradientRenderer rgr; - - static SPPainter *painter_new(SPPaintServer *ps, - Geom::Affine const &full_transform, - Geom::Affine const &parent_transform, - NRRect const *bbox); -}; - static void sp_radialgradient_class_init(SPRadialGradientClass *klass); static void sp_radialgradient_init(SPRadialGradient *rg); @@ -1766,9 +1318,7 @@ static void sp_radialgradient_build(SPObject *object, static void sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value); static Inkscape::XML::Node *sp_radialgradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); -static void sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter); - -static void sp_rg_fill(SPPainter *painter, NRPixBlock *pb); +static cairo_pattern_t *sp_radialgradient_create_pattern(SPPaintServer *ps, cairo_t *ct, Geom::OptRect const &bbox, double opacity); static SPGradientClass *rg_parent_class; @@ -1809,8 +1359,7 @@ static void sp_radialgradient_class_init(SPRadialGradientClass *klass) sp_object_class->set = sp_radialgradient_set; sp_object_class->write = sp_radialgradient_write; - ps_class->painter_new = SPRGPainter::painter_new; - ps_class->painter_free = sp_radialgradient_painter_free; + ps_class->pattern_new = sp_radialgradient_create_pattern; } /** @@ -1921,106 +1470,141 @@ sp_radialgradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inks } /** - * Create radial gradient context. + * Directly set properties of radial gradient and request modified. */ -SPPainter *SPRGPainter::painter_new(SPPaintServer *ps, - Geom::Affine const &full_transform, - Geom::Affine const &/*parent_transform*/, - NRRect const *bbox) +void +sp_radialgradient_set_position(SPRadialGradient *rg, + gdouble cx, gdouble cy, gdouble fx, gdouble fy, gdouble r) { - SPRadialGradient *rg = SP_RADIALGRADIENT(ps); - SPGradient *gr = SP_GRADIENT(ps); + g_return_if_fail(rg != NULL); + g_return_if_fail(SP_IS_RADIALGRADIENT(rg)); - if (!gr->color) { - gr->ensureColors(); - } + /* fixme: units? (Lauris) */ + rg->cx.set(SVGLength::NONE, cx, cx); + rg->cy.set(SVGLength::NONE, cy, cy); + rg->fx.set(SVGLength::NONE, fx, fx); + rg->fy.set(SVGLength::NONE, fy, fy); + rg->r.set(SVGLength::NONE, r, r); - SPRGPainter *rgp = g_new(SPRGPainter, 1); + rg->requestModified(SP_OBJECT_MODIFIED_FLAG); +} - rgp->painter.type = SP_PAINTER_IND; - rgp->painter.fill = sp_rg_fill; +/* CAIRO RENDERING STUFF */ - rgp->rg = rg; +static void +sp_gradient_pattern_common_setup(cairo_pattern_t *cp, + SPGradient *gr, + Geom::OptRect const &bbox, + double opacity) +{ + // set spread type + switch (gr->getSpread()) { + case SP_GRADIENT_SPREAD_REFLECT: + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REFLECT); + break; + case SP_GRADIENT_SPREAD_REPEAT: + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); + break; + case SP_GRADIENT_SPREAD_PAD: + default: + cairo_pattern_set_extend(cp, CAIRO_EXTEND_PAD); + break; + } - Geom::Affine gs2px; + // add stops + for (std::vector<SPGradientStop>::iterator i = gr->vector.stops.begin(); + i != gr->vector.stops.end(); ++i) + { + // multiply stop opacity by paint opacity + cairo_pattern_add_color_stop_rgba(cp, i->offset, + i->color.v.c[0], i->color.v.c[1], i->color.v.c[2], i->opacity * opacity); + } - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { - /** \todo - * fixme: We may try to normalize here too, look at - * linearGradient (Lauris) - */ + // set pattern matrix + Geom::Affine gs2user = gr->gradientTransform; + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX && bbox) { + Geom::Affine bbox2user(bbox->width(), 0, 0, bbox->height(), bbox->left(), bbox->top()); + gs2user *= bbox2user; + } + ink_cairo_pattern_set_matrix(cp, gs2user.inverse()); +} - /* BBox to user coordinate system */ - Geom::Affine bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0); +static cairo_pattern_t * +sp_radialgradient_create_pattern(SPPaintServer *ps, + cairo_t *ct, + Geom::OptRect const &bbox, + double opacity) +{ + SPRadialGradient *rg = SP_RADIALGRADIENT(ps); + SPGradient *gr = SP_GRADIENT(ps); - Geom::Affine gs2user = gr->gradientTransform * bbox2user; + gr->ensureVector(); - gs2px = gs2user * full_transform; - } else { - /** \todo - * Problem: What to do, if we have mixed lengths and percentages? - * Currently we do ignore percentages at all, but that is not - * good (lauris) - */ + Geom::Point focus(rg->fx.computed, rg->fy.computed); + Geom::Point center(rg->cx.computed, rg->cy.computed); + double radius = rg->r.computed; + double scale = 1.0; + double tolerance = cairo_get_tolerance(ct); + + // code below suggested by Cairo devs to overcome tolerance problems + // more: https://bugs.freedesktop.org/show_bug.cgi?id=40918 + Geom::Point d = focus - center; + if (d.length() + tolerance > radius) { + scale = radius / d.length(); - gs2px = gr->gradientTransform * full_transform; + double dx = d.x(), dy = d.y(); + cairo_user_to_device_distance(ct, &dx, &dy); + if (!Geom::are_near(dx, 0, tolerance) || + !Geom::are_near(dy, 0, tolerance)) + { + scale *= 1.0 - 2.0 * tolerance / hypot(dx, dy); + } } - // TODO: remove gs2px_nr after converting to 2geom - NR::Matrix gs2px_nr = from_2geom(gs2px); - nr_rgradient_renderer_setup(&rgp->rgr, gr->color, gr->fetchSpread(), - &gs2px_nr, - rg->cx.computed, rg->cy.computed, - rg->fx.computed, rg->fy.computed, - rg->r.computed); - return (SPPainter *) rgp; -} + cairo_pattern_t *cp = cairo_pattern_create_radial( + scale * d.x() + center.x(), scale * d.y() + center.y(), 0, + center.x(), center.y(), radius); -static void -sp_radialgradient_painter_free(SPPaintServer */*ps*/, SPPainter *painter) -{ - g_free(painter); + sp_gradient_pattern_common_setup(cp, gr, bbox, opacity); + + return cp; } -/** - * Directly set properties of radial gradient and request modified. - */ -void -sp_radialgradient_set_position(SPRadialGradient *rg, - gdouble cx, gdouble cy, gdouble fx, gdouble fy, gdouble r) +static cairo_pattern_t * +sp_lineargradient_create_pattern(SPPaintServer *ps, + cairo_t */* ct */, + Geom::OptRect const &bbox, + double opacity) { - g_return_if_fail(rg != NULL); - g_return_if_fail(SP_IS_RADIALGRADIENT(rg)); + SPLinearGradient *lg = SP_LINEARGRADIENT(ps); + SPGradient *gr = SP_GRADIENT(ps); - /* fixme: units? (Lauris) */ - rg->cx.set(SVGLength::NONE, cx, cx); - rg->cy.set(SVGLength::NONE, cy, cy); - rg->fx.set(SVGLength::NONE, fx, fx); - rg->fy.set(SVGLength::NONE, fy, fy); - rg->r.set(SVGLength::NONE, r, r); + gr->ensureVector(); - rg->requestModified(SP_OBJECT_MODIFIED_FLAG); + cairo_pattern_t *cp = cairo_pattern_create_linear( + lg->x1.computed, lg->y1.computed, + lg->x2.computed, lg->y2.computed); + + sp_gradient_pattern_common_setup(cp, gr, bbox, opacity); + + return cp; } -/** - * Callback when radial gradient object is rendered. - */ -static void -sp_rg_fill(SPPainter *painter, NRPixBlock *pb) +cairo_pattern_t * +sp_gradient_create_preview_pattern(SPGradient *gr, double width) { - SPRGPainter *rgp = (SPRGPainter *) painter; + gr->ensureVector(); - if (rgp->rg->color == NULL) { - rgp->rg->ensureColors(); - } - bool grayscale = Grayscale::activeDesktopIsGrayscale(); // TODO: find good way to access the current rendermode - if (grayscale) { - rgp->rgr.vector = rgp->rg->color_grayscale; - } else { - rgp->rgr.vector = rgp->rg->color; + cairo_pattern_t *pat = cairo_pattern_create_linear(0, 0, width, 0); + + for (std::vector<SPGradientStop>::iterator i = gr->vector.stops.begin(); + i != gr->vector.stops.end(); ++i) + { + cairo_pattern_add_color_stop_rgba(pat, i->offset, + i->color.v.c[0], i->color.v.c[1], i->color.v.c[2], i->opacity); } - nr_render((NRRenderer *) &rgp->rgr, pb, NULL); + return pat; } /* |
