From 833c08758cda30e603c640ef35ba2da894caef77 Mon Sep 17 00:00:00 2001 From: jaspervdg Date: Fri, 3 Apr 2009 15:16:18 +0000 Subject: [PATCH] And now gradients should be (almost) perfect... The gradient vector is set up to be a good approximation to the "true" gradient and it is used consistently throughout (I hope). Also some other, minor, tweaks and a small bugfix. --- src/color.h | 2 +- src/display/nr-3dutils.cpp | 11 +- src/display/nr-filter-component-transfer.cpp | 1 - src/display/nr-filter-diffuselighting.cpp | 13 +- src/display/nr-filter-displacement-map.cpp | 4 +- src/libnr/nr-gradient.cpp | 52 +++++-- src/sp-gradient.cpp | 148 ++++++++++++++++--- 7 files changed, 173 insertions(+), 58 deletions(-) diff --git a/src/color.h b/src/color.h index 8fcc94438..bebeaec60 100644 --- a/src/color.h +++ b/src/color.h @@ -25,7 +25,7 @@ #define SP_RGBA32_B_U(v) (((v) >> 8) & 0xff) #define SP_RGBA32_A_U(v) ((v) & 0xff) #define SP_COLOR_U_TO_F(v) ((v) / 255.0) -#define SP_COLOR_F_TO_U(v) ((unsigned int) ((v) * 255.9999)) +#define SP_COLOR_F_TO_U(v) ((unsigned int) ((v) * 255. + .5)) #define SP_RGBA32_R_F(v) SP_COLOR_U_TO_F (SP_RGBA32_R_U (v)) #define SP_RGBA32_G_F(v) SP_COLOR_U_TO_F (SP_RGBA32_G_U (v)) #define SP_RGBA32_B_F(v) SP_COLOR_U_TO_F (SP_RGBA32_B_U (v)) diff --git a/src/display/nr-3dutils.cpp b/src/display/nr-3dutils.cpp index dd1419f2b..89c21940a 100644 --- a/src/display/nr-3dutils.cpp +++ b/src/display/nr-3dutils.cpp @@ -125,13 +125,14 @@ void compute_surface_normal(Fvector &N, gdouble ss, NRPixBlock *in, int i, int j alpha_idx_y = alpha_idx + 4*(k-1)*dy*w; for (l = START(x_carac); l <= FINISH(x_carac); l++) { alpha = (data + alpha_idx_y + 4*dx*(l-1))[3]; - accu_x += K_X[y_carac][x_carac][k][l] * alpha / 255; - accu_y += K_X[x_carac][y_carac][l][k] * alpha / 255; + accu_x += K_X[y_carac][x_carac][k][l] * alpha; + accu_y += K_X[x_carac][y_carac][l][k] * alpha; } } - N[X_3D] = -1 * ss * FACTOR_X[y_carac][x_carac] * accu_x / dx; - N[Y_3D] = -1 * ss * FACTOR_X[x_carac][y_carac] * accu_y / dy; - N[Z_3D] = 1; + ss /= 255.0; // Correction for scale of pixel values + N[X_3D] = -ss * FACTOR_X[y_carac][x_carac] * accu_x / dx; + N[Y_3D] = -ss * FACTOR_X[x_carac][y_carac] * accu_y / dy; + N[Z_3D] = 1.0; normalize_vector(N); //std::cout << "(" << N[X_3D] << ", " << N[Y_3D] << ", " << N[Z_3D] << ")" << std::endl; } diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp index 6f9e2b712..bd52c071e 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -59,7 +59,6 @@ int FilterComponentTransfer::render(FilterSlot &slot, FilterUnits const &/*units free_in_on_exit = true; } bool premultiplied = in->mode == NR_PIXBLOCK_MODE_R8G8B8A8P; - g_message("Premultiplied=%s", premultiplied?"yes":"no"); NRPixBlock *out = new NRPixBlock; nr_pixblock_setup_fast(out, in->mode, x0, y0, x1, y1, true); diff --git a/src/display/nr-filter-diffuselighting.cpp b/src/display/nr-filter-diffuselighting.cpp index 9ae231d39..bf5b97fb1 100644 --- a/src/display/nr-filter-diffuselighting.cpp +++ b/src/display/nr-filter-diffuselighting.cpp @@ -43,13 +43,6 @@ FilterPrimitive * FilterDiffuseLighting::create() { FilterDiffuseLighting::~FilterDiffuseLighting() {} -#define COMPUTE_INTER(inter, N, L, kd) \ -do {\ - (inter) = (kd) * NR::scalar_product((N), (L)); \ - if ((inter) < 0) (inter) = 0; \ -}while(0) - - int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) { NRPixBlock *in = slot.get(_input); if (!in) { @@ -92,7 +85,7 @@ int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) { //finish the work for (i = 0, j = 0; i < w*h; i++) { NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - COMPUTE_INTER(inter, N, L, kd); + inter = kd * NR::scalar_product(N, L); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); @@ -118,7 +111,7 @@ int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) { i % w + x0, i / w + y0, ss * (double) data_i[4*i+3]/ 255); - COMPUTE_INTER(inter, N, L, kd); + inter = kd * NR::scalar_product(N, L); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); @@ -144,7 +137,7 @@ int FilterDiffuseLighting::render(FilterSlot &slot, FilterUnits const &units) { i / w + y0, ss * (double) data_i[4*i+3]/ 255); sl->light_components(LC, L); - COMPUTE_INTER(inter, N, L, kd); + inter = kd * NR::scalar_product(N, L); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp index 31d562f21..29815abbb 100644 --- a/src/display/nr-filter-displacement-map.cpp +++ b/src/display/nr-filter-displacement-map.cpp @@ -114,10 +114,10 @@ static void performDisplacement(NRPixBlock const* texture, NRPixBlock const* map pixel_t mapValue = pixelValue(map, xmap, ymap); double xtex = xout + (Xneedsdemul ? (mapValue[3]==0?0:(scalex * (mapValue[Xchannel] - mapValue[3]*0.5) / mapValue[3])) : - scalex * (mapValue[Xchannel] - 127.5)); + (scalex * (mapValue[Xchannel] - 127.5))); double ytex = yout + (Yneedsdemul ? (mapValue[3]==0?0:(scaley * (mapValue[Ychannel] - mapValue[3]*0.5) / mapValue[3])) : - scaley * (mapValue[Ychannel] - 127.5)); + (scaley * (mapValue[Ychannel] - 127.5))); out_data[(xout-out->area.x0) + (out->area.x1-out->area.x0)*(yout-out->area.y0)] = interpolatePixels(texture, xtex, ytex); } diff --git a/src/libnr/nr-gradient.cpp b/src/libnr/nr-gradient.cpp index 288722a16..e6eb9b79c 100644 --- a/src/libnr/nr-gradient.cpp +++ b/src/libnr/nr-gradient.cpp @@ -6,7 +6,9 @@ * Authors: * Lauris Kaplinski * MenTaLguY + *...Jasper van de Gronde * + * Copyright (C) 2009 Jasper van de Gronde * Copyright (C) 2007 MenTaLguY * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001-2002 Ximian, Inc. @@ -43,30 +45,44 @@ template struct Spread; template <> struct Spread { +static double index_at(NR::Coord r, double const unity = 1.0) { + return r<0.0?0.0:r>unity?unity:r; +} static unsigned char const *color_at(NR::Coord r, unsigned char const *vector) { - return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector); + return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector); + //return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector); } }; template <> struct Spread { +static double index_at(NR::Coord r, double const unity = 1.0) { + return r<0.0?(unity+fmod(r,unity)):fmod(r,unity); +} static unsigned char const *color_at(NR::Coord r, unsigned char const *vector) { - return vector_index((int)((long long)r & NRG_MASK), vector); + return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector); + //return vector_index((int)((long long)r & NRG_MASK), vector); } }; template <> struct Spread { +static double index_at(NR::Coord r, double const unity = 1.0) { + r = r<0.0?(2*unity+fmod(r,2*unity)):fmod(r,2*unity); + if (r>unity) r=2*unity-r; + return r; +} static unsigned char const *color_at(NR::Coord r, unsigned char const *vector) { - int idx = (int) ((long long)r & NRG_2MASK); - if (idx > NRG_MASK) idx = NRG_2MASK - idx; - return vector_index(idx, vector); + return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector); + //int idx = (int) ((long long)r & NRG_2MASK); + //if (idx > NRG_MASK) idx = NRG_2MASK - idx; + //return vector_index(idx, vector); } }; @@ -319,10 +335,10 @@ nr_lgradient_renderer_setup (NRLGradientRenderer *lgr, n2px = n2gs * (*gs2px); px2n = n2px.inverse(); - lgr->x0 = n2px[4] - 0.5; + lgr->x0 = n2px[4] - 0.5; // These -0.5 offsets make sure that the gradient is sampled in the MIDDLE of each pixel. lgr->y0 = n2px[5] - 0.5; - lgr->dx = px2n[0] * NR_GRADIENT_VECTOR_LENGTH; - lgr->dy = px2n[2] * NR_GRADIENT_VECTOR_LENGTH; + lgr->dx = px2n[0] * (NR_GRADIENT_VECTOR_LENGTH-1); + lgr->dy = px2n[2] * (NR_GRADIENT_VECTOR_LENGTH-1); return (NRRenderer *) lgr; } @@ -422,7 +438,7 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb) NR::Coord const pxgx = gx + sqrt(qgx2_4); /* We can safely divide by 0 here */ /* If we are sure pxgx cannot be -0 */ - NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH; + NR::Coord const pos = gxy2 / pxgx * (NR_GRADIENT_VECTOR_LENGTH-1); unsigned char const *s; if (pos < (1U << 31)) { @@ -430,7 +446,7 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb) } else { s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector); } - + compose::compose(pb, d, &spb, s); d += compose::bpp; @@ -466,14 +482,16 @@ nr_rgradient_renderer_setup(NRRGradientRenderer *rgr, rgr->render = render; rgr->px2gs = gs2px->inverse(); - rgr->px2gs[0] *= (NR_GRADIENT_VECTOR_LENGTH / r); - rgr->px2gs[1] *= (NR_GRADIENT_VECTOR_LENGTH / r); - rgr->px2gs[2] *= (NR_GRADIENT_VECTOR_LENGTH / r); - rgr->px2gs[3] *= (NR_GRADIENT_VECTOR_LENGTH / r); + rgr->px2gs[0] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; + rgr->px2gs[1] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; + rgr->px2gs[2] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; + rgr->px2gs[3] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; rgr->px2gs[4] -= cx; rgr->px2gs[5] -= cy; - rgr->px2gs[4] *= (NR_GRADIENT_VECTOR_LENGTH / r); - rgr->px2gs[5] *= (NR_GRADIENT_VECTOR_LENGTH / r); + rgr->px2gs[4] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; + rgr->px2gs[5] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r; + rgr->px2gs[4] += 0.5*(rgr->px2gs[0]+rgr->px2gs[2]); // These offsets make sure the gradient is sampled in the MIDDLE of each pixel + rgr->px2gs[5] += 0.5*(rgr->px2gs[1]+rgr->px2gs[3]); rgr->cx = 0.0; rgr->cy = 0.0; @@ -500,6 +518,8 @@ nr_rgradient_renderer_setup(NRRGradientRenderer *rgr, NR::Matrix n2px; n2px = n2gs * (*gs2px); rgr->px2gs = n2px.inverse(); + rgr->px2gs[4] += 0.5*(rgr->px2gs[0]+rgr->px2gs[2]); // These offsets make sure the gradient is sampled in the MIDDLE of each pixel + rgr->px2gs[5] += 0.5*(rgr->px2gs[1]+rgr->px2gs[3]); rgr->cx = 1.0; rgr->cy = 0.0; diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 09c2bd65f..1d41e2042 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -7,10 +7,12 @@ * Authors: * Lauris Kaplinski * bulia byak + * Jasper van de Gronde * * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. * Copyright (C) 2004 David Turner + * Copyright (C) 2009 Jasper van de Gronde * * Released under GNU GPL, read the file 'COPYING' for more information * @@ -1085,28 +1087,128 @@ sp_gradient_ensure_colors(SPGradient *gr) gr->color = g_new(guchar, 4 * NCOLORS); } - for (guint i = 0; i < gr->vector.stops.size() - 1; i++) { - guint32 color = gr->vector.stops[i].color.toRGBA32( gr->vector.stops[i].opacity ); - gint r0 = (color >> 24) & 0xff; - gint g0 = (color >> 16) & 0xff; - gint b0 = (color >> 8) & 0xff; - gint a0 = color & 0xff; - color = gr->vector.stops[i + 1].color.toRGBA32( gr->vector.stops[i + 1].opacity ); - gint r1 = (color >> 24) & 0xff; - gint g1 = (color >> 16) & 0xff; - gint b1 = (color >> 8) & 0xff; - gint a1 = color & 0xff; - gint o0 = (gint) floor(gr->vector.stops[i].offset * (NCOLORS - 0.001)); - gint o1 = (gint) floor(gr->vector.stops[i + 1].offset * (NCOLORS - 0.001)); - if (o1 > o0) { - for (int j = o0; j < o1 + 1; j++) { - gr->color[4 * j + 0] = r0 + ((j-o0)*(r1-r0) + (o1-o0)/2)/(o1-o0); - gr->color[4 * j + 1] = g0 + ((j-o0)*(g1-g0) + (o1-o0)/2)/(o1-o0); - gr->color[4 * j + 2] = b0 + ((j-o0)*(b1-b0) + (o1-o0)/2)/(o1-o0); - gr->color[4 * j + 3] = a0 + ((j-o0)*(a1-a0) + (o1-o0)/2)/(o1-o0); + // This assumes that gr->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(gr->spread) { + case SP_GRADIENT_SPREAD_PAD: + remainder[0] = 0.5*gr->vector.stops[0].color.v.c[0]; + remainder[1] = 0.5*gr->vector.stops[0].color.v.c[1]; + remainder[2] = 0.5*gr->vector.stops[0].color.v.c[2]; + remainder[3] = 0.5*gr->vector.stops[0].opacity; + remainder_for_end[0] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[0]; + remainder_for_end[1] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[1]; + remainder_for_end[2] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[2]; + remainder_for_end[3] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].opacity; + break; + case SP_GRADIENT_SPREAD_REFLECT: + break; + case SP_GRADIENT_SPREAD_REPEAT: + break; + default: + g_error("Spread type not supported!"); + }; + for (unsigned int i = 0; i < gr->vector.stops.size() - 1; i++) { + double r0 = gr->vector.stops[i].color.v.c[0]; + double g0 = gr->vector.stops[i].color.v.c[1]; + double b0 = gr->vector.stops[i].color.v.c[2]; + double a0 = gr->vector.stops[i].opacity; + double r1 = gr->vector.stops[i+1].color.v.c[0]; + double g1 = gr->vector.stops[i+1].color.v.c[1]; + double b1 = gr->vector.stops[i+1].color.v.c[2]; + double a1 = gr->vector.stops[i+1].opacity; + double o0 = gr->vector.stops[i].offset * (NCOLORS-1); + double o1 = gr->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; + gr->color[4 * j + 0] = (unsigned char) floor(255*(r0 + f*(r1-r0)) + .5); + gr->color[4 * j + 1] = (unsigned char) floor(255*(g0 + f*(g1-g0)) + .5); + gr->color[4 * j + 2] = (unsigned char) floor(255*(b0 + f*(b1-b0)) + .5); + gr->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 && gr->spread==SP_GRADIENT_SPREAD_REFLECT) { + gr->color[4 * ob + 0] = (unsigned char) floor(2*255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); + gr->color[4 * ob + 1] = (unsigned char) floor(2*255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); + gr->color[4 * ob + 2] = (unsigned char) floor(2*255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); + gr->color[4 * ob + 3] = (unsigned char) floor(2*255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); + } else if (ob==0 && gr->spread==SP_GRADIENT_SPREAD_REPEAT) { + 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 { + gr->color[4 * ob + 0] = (unsigned char) floor(255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); + gr->color[4 * ob + 1] = (unsigned char) floor(255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); + gr->color[4 * ob + 2] = (unsigned char) floor(255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); + gr->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(gr->spread) { + case SP_GRADIENT_SPREAD_PAD: + gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); + gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); + gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); + gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); + break; + case SP_GRADIENT_SPREAD_REFLECT: + gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(2*255*remainder[0] + .5); + gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(2*255*remainder[1] + .5); + gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(2*255*remainder[2] + .5); + gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(2*255*remainder[3] + .5); + break; + case SP_GRADIENT_SPREAD_REPEAT: + gr->color[0] = gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); + gr->color[1] = gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); + gr->color[2] = gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); + gr->color[3] = gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); + break; + } } /** @@ -1358,9 +1460,9 @@ static void sp_lineargradient_class_init(SPLinearGradientClass *klass) static void sp_lineargradient_init(SPLinearGradient *lg) { lg->x1.unset(SVGLength::PERCENT, 0.0, 0.0); - lg->y1.unset(SVGLength::PERCENT, 0.5, 0.5); + lg->y1.unset(SVGLength::PERCENT, 0.0, 0.0); lg->x2.unset(SVGLength::PERCENT, 1.0, 1.0); - lg->y2.unset(SVGLength::PERCENT, 0.5, 0.5); + lg->y2.unset(SVGLength::PERCENT, 0.0, 0.0); } /** @@ -1393,7 +1495,7 @@ sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value) object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_Y1: - lg->y1.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5); + lg->y1.readOrUnset(value, SVGLength::PERCENT, 0.0, 0.0); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_X2: @@ -1401,7 +1503,7 @@ sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value) object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_Y2: - lg->y2.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5); + lg->y2.readOrUnset(value, SVGLength::PERCENT, 0.0, 0.0); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; default: -- 2.30.2