Code

Fix behavior when loading a document
[inkscape.git] / src / libnr / nr-gradient.cpp
index 9de84d707a1819b17936ab901de6697c0cb09bc2..e6eb9b79c93a88802362c4987e583fe595dc5e25 100644 (file)
@@ -6,7 +6,9 @@
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   MenTaLguY <mental@rydia.net>
+ *...Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
  *
+ * Copyright (C) 2009 Jasper van de Gronde
  * Copyright (C) 2007 MenTaLguY 
  * Copyright (C) 2001-2002 Lauris Kaplinski
  * Copyright (C) 2001-2002 Ximian, Inc.
  * Derived in part from public domain code by Lauris Kaplinski
  */
 
+#include <cstring>
 #include <libnr/nr-pixops.h>
 #include <libnr/nr-pixblock-pixel.h>
 #include <libnr/nr-blit.h>
 #include <libnr/nr-gradient.h>
+#include <libnr/nr-matrix-ops.h>
 #include <glib/gtypes.h>
 #include <stdio.h>
 
@@ -41,35 +45,72 @@ template <NRGradientSpread spread> struct Spread;
 
 template <>
 struct Spread<NR_GRADIENT_SPREAD_PAD> {
+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<NR_GRADIENT_SPREAD_REPEAT> {
+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<NR_GRADIENT_SPREAD_REFLECT> {
+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);
 }
 };
 
-template <NR_PIXBLOCK_MODE mode, bool empty=false>
+template <NR_PIXBLOCK_MODE mode> struct ModeTraits;
+
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8N> {
+static const unsigned bpp=4;
+};
+
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8P> {
+static const unsigned bpp=4;
+};
+
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8> {
+static const unsigned bpp=3;
+};
+
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_A8> {
+static const unsigned bpp=1;
+};
+
+template <NR_PIXBLOCK_MODE mode, bool empty>
 struct Compose {
+static const unsigned bpp=ModeTraits<mode>::bpp;
 static void compose(NRPixBlock *pb, unsigned char *dest,
                     NRPixBlock *spb, unsigned char const *src)
 {
@@ -79,20 +120,54 @@ static void compose(NRPixBlock *pb, unsigned char *dest,
 
 template <>
 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
-static void compose(NRPixBlock *pb, unsigned char *dest,
-                    NRPixBlock *spb, unsigned char const *src)
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
+{
+    std::memcpy(dest, src, 4);
+}
+};
+
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> {
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
-    dest[0] = src[0];
-    dest[1] = src[1];
-    dest[2] = src[2];
+    dest[0] = NR_PREMUL_111(src[0], src[3]);
+    dest[1] = NR_PREMUL_111(src[1], src[3]);
+    dest[2] = NR_PREMUL_111(src[2], src[3]);
     dest[3] = src[3];
 }
 };
 
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8, true> {
+static const unsigned bpp=3;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
+{
+    dest[0] = NR_COMPOSEN11_1111(src[0], src[3], 255);
+    dest[1] = NR_COMPOSEN11_1111(src[1], src[3], 255);
+    dest[2] = NR_COMPOSEN11_1111(src[2], src[3], 255);
+}
+};
+
+template <>
+struct Compose<NR_PIXBLOCK_MODE_A8, true> {
+static const unsigned bpp=1;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
+{
+    dest[0] = src[3];
+}
+};
+
 template <>
 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
-static void compose(NRPixBlock *pb, unsigned char *dest,
-                    NRPixBlock *spb, unsigned char const *src)
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
     unsigned int ca;
     ca = NR_COMPOSEA_112(src[3], dest[3]);
@@ -105,8 +180,9 @@ static void compose(NRPixBlock *pb, unsigned char *dest,
 
 template <>
 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
-static void compose(NRPixBlock *pb, unsigned char *dest,
-                    NRPixBlock *spb, unsigned char const *src)
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
     dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
     dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
@@ -117,8 +193,9 @@ static void compose(NRPixBlock *pb, unsigned char *dest,
 
 template <>
 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
-static void compose(NRPixBlock *pb, unsigned char *dest,
-                    NRPixBlock *spb, unsigned char const *src)
+static const unsigned bpp=3;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
     dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
     dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
@@ -134,37 +211,37 @@ render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
     case NR_PIXBLOCK_MODE_R8G8B8A8N:
         if (pb->empty) {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
-            Subtype::template render<compose, spread, 4>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         } else {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
-            Subtype::template render<compose, spread, 4>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         }
         break;
     case NR_PIXBLOCK_MODE_R8G8B8A8P:
         if (pb->empty) {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> compose;
-            Subtype::template render<compose, spread, 4>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         } else {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
-            Subtype::template render<compose, spread, 4>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         }
         break;
     case NR_PIXBLOCK_MODE_R8G8B8:
         if (pb->empty) {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, true> compose;
-            Subtype::template render<compose, spread, 3>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         } else {
             typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
-            Subtype::template render<compose, spread, 3>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         }
         break;
     case NR_PIXBLOCK_MODE_A8:
         if (pb->empty) {
             typedef Compose<NR_PIXBLOCK_MODE_A8, true> compose;
-            Subtype::template render<compose, spread, 1>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         } else {
             typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
-            Subtype::template render<compose, spread, 1>(gr, pb);
+            Subtype::template render<compose, spread>(gr, pb);
         }
         break;
     }
@@ -172,11 +249,9 @@ render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
 
 template <typename Subtype>
 static void
-render(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+render(NRRenderer *r, NRPixBlock *pb, NRPixBlock */*m*/)
 {
-    NRGradientRenderer *gr;
-
-    gr = static_cast<NRGradientRenderer *>(r);
+    NRGradientRenderer *gr = static_cast<NRGradientRenderer *>(r);
 
     switch (gr->spread) {
     case NR_GRADIENT_SPREAD_REPEAT:
@@ -197,7 +272,7 @@ render(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
 namespace {
 
 struct Linear {
-template <typename compose, typename spread, unsigned bpp>
+template <typename compose, typename spread>
 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
     NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
 
@@ -224,7 +299,7 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
         for (x = 0; x < width; x++) {
             unsigned char const *s=spread::color_at(pos, lgr->vector);
             compose::compose(pb, d, &spb, s);
-            d += bpp;
+            d += compose::bpp;
             pos += lgr->dx;
         }
     }
@@ -239,31 +314,31 @@ NRRenderer *
 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
                             const unsigned char *cv, 
                             unsigned int spread, 
-                            const NRMatrix *gs2px,
+                            const NR::Matrix *gs2px,
                             float x0, float y0,
                             float x1, float y1)
 {
-       NRMatrix n2gs, n2px, px2n;
+       NR::Matrix n2gs, n2px, px2n;
 
        lgr->render = &render<Linear>;
 
        lgr->vector = cv;
        lgr->spread = spread;
 
-       n2gs.c[0] = x1 - x0;
-       n2gs.c[1] = y1 - y0;
-       n2gs.c[2] = y1 - y0;
-       n2gs.c[3] = x0 - x1;
-       n2gs.c[4] = x0;
-       n2gs.c[5] = y0;
+       n2gs[0] = x1 - x0;
+       n2gs[1] = y1 - y0;
+       n2gs[2] = y1 - y0;
+       n2gs[3] = x0 - x1;
+       n2gs[4] = x0;
+       n2gs[5] = y0;
 
-       nr_matrix_multiply (&n2px, &n2gs, gs2px);
-       nr_matrix_invert (&px2n, &n2px);
+       n2px = n2gs * (*gs2px);
+       px2n = n2px.inverse();
 
-       lgr->x0 = n2px.c[4] - 0.5;
-       lgr->y0 = n2px.c[5] - 0.5;
-       lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
-       lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
+       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-1);
+       lgr->dy = px2n[2] * (NR_GRADIENT_VECTOR_LENGTH-1);
 
        return (NRRenderer *) lgr;
 }
@@ -297,13 +372,13 @@ nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
 namespace {
 
 struct SymmetricRadial {
-template <typename compose, typename spread, unsigned bpp>
+template <typename compose, typename spread>
 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 {
     NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
 
-    NR::Coord const dx = rgr->px2gs.c[0];
-    NR::Coord const dy = rgr->px2gs.c[1];
+    NR::Coord const dx = rgr->px2gs[0];
+    NR::Coord const dy = rgr->px2gs[1];
 
     NRPixBlock spb;
     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
@@ -314,13 +389,13 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 
     for (int y = pb->area.y0; y < pb->area.y1; y++) {
         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
-        NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
-        NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
+        NR::Coord gx = rgr->px2gs[0] * pb->area.x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
+        NR::Coord gy = rgr->px2gs[1] * pb->area.x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
         for (int x = pb->area.x0; x < pb->area.x1; x++) {
             NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
             unsigned char const *s=spread::color_at(pos, rgr->vector);
             compose::compose(pb, d, &spb, s);
-            d += bpp;
+            d += compose::bpp;
             gx += dx;
             gy += dy;
         }
@@ -331,7 +406,7 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 };
 
 struct Radial {
-template <typename compose, typename spread, unsigned bpp>
+template <typename compose, typename spread>
 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 {
     NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
@@ -350,10 +425,10 @@ static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 
     for (int y = y0; y < y1; y++) {
         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
-        NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
-        NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
-        NR::Coord const dx = rgr->px2gs.c[0];
-        NR::Coord const dy = rgr->px2gs.c[1];
+        NR::Coord gx = rgr->px2gs[0] * x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
+        NR::Coord gy = rgr->px2gs[1] * x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
+        NR::Coord const dx = rgr->px2gs[0];
+        NR::Coord const dy = rgr->px2gs[1];
         for (int x = x0; x < x1; x++) {
             NR::Coord const gx2 = gx * gx;
             NR::Coord const gxy2 = gx2 + gy * gy;
@@ -363,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)) {
@@ -371,10 +446,9 @@ 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 += bpp;
+            compose::compose(pb, d, &spb, s);
+            d += compose::bpp;
 
             gx += dx;
             gy += dy;
@@ -393,7 +467,7 @@ NRRenderer *
 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
                             unsigned char const *cv,
                             unsigned spread,
-                            NRMatrix const *gs2px,
+                            NR::Matrix const *gs2px,
                             float cx, float cy,
                             float fx, float fy,
                             float r)
@@ -407,15 +481,17 @@ nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
                NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
         rgr->render = render<SymmetricRadial>;
 
-        nr_matrix_invert(&rgr->px2gs, gs2px);
-        rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
-        rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
-        rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
-        rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
-        rgr->px2gs.c[4] -= cx;
-        rgr->px2gs.c[5] -= cy;
-        rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
-        rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+        rgr->px2gs = gs2px->inverse();
+        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-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;
@@ -431,17 +507,19 @@ nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
             fy = cy + (fy - cy ) * r / (float) df;
         }
 
-        NRMatrix n2gs;
-        n2gs.c[0] = cx - fx;
-        n2gs.c[1] = cy - fy;
-        n2gs.c[2] = cy - fy;
-        n2gs.c[3] = fx - cx;
-        n2gs.c[4] = fx;
-        n2gs.c[5] = fy;
-
-        NRMatrix n2px;
-        nr_matrix_multiply(&n2px, &n2gs, gs2px);
-        nr_matrix_invert(&rgr->px2gs, &n2px);
+        NR::Matrix n2gs;
+        n2gs[0] = cx - fx;
+        n2gs[1] = cy - fy;
+        n2gs[2] = cy - fy;
+        n2gs[3] = fx - cx;
+        n2gs[4] = fx;
+        n2gs[5] = fy;
+
+        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;