summaryrefslogtreecommitdiffstats
path: root/src/sp-gradient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp-gradient.cpp')
-rw-r--r--src/sp-gradient.cpp646
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;
}
/*