Code

Use subdirectories with icon sizes.
[inkscape.git] / src / libnr / nr-gradient.cpp
index 4379a322ceaa725ade73c2f2cae6efac5c95c6bc..cb03c048bd547995109312a60426b574440bfce4 100644 (file)
@@ -5,7 +5,11 @@
  *
  * 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>
 
 /* Common */
 
-#define noNR_USE_GENERIC_RENDERER
-
 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
 
-/* Linear */
+namespace {
+inline unsigned char const *vector_index(int idx,
+                                         unsigned char const *vector)
+{
+  return vector + 4 * idx;
+}
 
-static void nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
-static void nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
-static void nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
-static void nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
-static void nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb);
+template <NRGradientSpread spread> struct Spread;
 
-NRRenderer *
-nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
-                            const unsigned char *cv, 
-                            unsigned int spread, 
-                            const NRMatrix *gs2px,
-                            float x0, float y0,
-                            float x1, float y1)
+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)
 {
-       NRMatrix n2gs, n2px, px2n;
-
-       lgr->renderer.render = nr_lgradient_render_block;
+    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);
+}
+};
 
-       lgr->vector = cv;
-       lgr->spread = spread;
+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)(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)
+{
+    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> 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)
+{
+    nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
+}
+};
 
-       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;
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
+{
+    std::memcpy(dest, src, 4);
+}
+};
 
-       nr_matrix_multiply (&n2px, &n2gs, gs2px);
-       nr_matrix_invert (&px2n, &n2px);
+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] = 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];
+}
+};
 
-       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;
+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);
+}
+};
 
-       return (NRRenderer *) lgr;
+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];
 }
+};
 
-static void
-nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
-       NRLGradientRenderer *lgr;
-       int width, height;
-
-       lgr = (NRLGradientRenderer *) r;
-
-       width = pb->area.x1 - pb->area.x0;
-       height = pb->area.y1 - pb->area.y0;
-
-#ifdef NR_USE_GENERIC_RENDERER
-       nr_lgradient_render_generic (lgr, pb);
-#else
-       if (pb->empty) {
-               switch (pb->mode) {
-               case NR_PIXBLOCK_MODE_A8:
-                       nr_lgradient_render_generic (lgr, pb);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8:
-                       nr_lgradient_render_generic (lgr, pb);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8A8N:
-                       nr_lgradient_render_R8G8B8A8N_EMPTY (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8A8P:
-                       nr_lgradient_render_generic (lgr, pb);
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               switch (pb->mode) {
-               case NR_PIXBLOCK_MODE_A8:
-                       nr_lgradient_render_generic (lgr, pb);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8:
-                       nr_lgradient_render_R8G8B8 (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8A8N:
-                       nr_lgradient_render_R8G8B8A8N (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
-                       break;
-               case NR_PIXBLOCK_MODE_R8G8B8A8P:
-                       nr_lgradient_render_generic (lgr, pb);
-                       break;
-               default:
-                       break;
-               }
-       }
-#endif
+    unsigned int ca;
+    ca = NR_COMPOSEA_112(src[3], dest[3]);
+    dest[0] = NR_COMPOSENNN_111121(src[0], src[3], dest[0], dest[3], ca);
+    dest[1] = NR_COMPOSENNN_111121(src[1], src[3], dest[1], dest[3], ca);
+    dest[2] = NR_COMPOSENNN_111121(src[2], src[3], dest[2], dest[3], ca);
+    dest[3] = NR_NORMALIZE_21(ca);
 }
+};
 
-static void
-nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
+static const unsigned bpp=4;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
-       int x, y;
-       double pos;
-
-       for (y = 0; y < height; y++) {
-               const unsigned char *s;
-               unsigned char *d;
-               int idx;
-               d = px + y * rs;
-               pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
-               if (lgr->spread == NR_GRADIENT_SPREAD_PAD) {
-                       for (x = 0; x < width; x++) {
-                               idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
-                               s = lgr->vector + 4 * idx;
-                               d[0] = s[0];
-                               d[1] = s[1];
-                               d[2] = s[2];
-                               d[3] = s[3];
-                               d += 4;
-                               pos += lgr->dx;
-                       }
-               } else if (lgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
-                       for (x = 0; x < width; x++) {
-                               idx = (int) ((long long) pos & NRG_2MASK);
-                               if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                               s = lgr->vector + 4 * idx;
-                               d[0] = s[0];
-                               d[1] = s[1];
-                               d[2] = s[2];
-                               d[3] = s[3];
-                               d += 4;
-                               pos += lgr->dx;
-                       }
-               } else {
-                       for (x = 0; x < width; x++) {
-                               idx = (int) ((long long) pos & NRG_MASK);
-                               s = lgr->vector + 4 * idx;
-                               d[0] = s[0];
-                               d[1] = s[1];
-                               d[2] = s[2];
-                               d[3] = s[3];
-                               d += 4;
-                               pos += lgr->dx;
-                       }
-               }
-       }
+    dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
+    dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
+    dest[2] = NR_COMPOSENPP_1111(src[2], src[3], dest[2]);
+    dest[3] = NR_COMPOSEA_111(src[3], dest[3]);
 }
+};
 
-static void
-nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
+template <>
+struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
+static const unsigned bpp=3;
+static void compose(NRPixBlock */*pb*/, unsigned char *dest,
+                    NRPixBlock */*spb*/, unsigned char const *src)
 {
-       int x, y;
-       unsigned char *d;
-       double pos;
-
-       for (y = 0; y < height; y++) {
-               d = px + y * rs;
-               pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
-               for (x = 0; x < width; x++) {
-                       int idx;
-                       unsigned int ca;
-                       const unsigned char *s;
-                       switch (lgr->spread) {
-                       case NR_GRADIENT_SPREAD_PAD:
-                               idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
-                               break;
-                       case NR_GRADIENT_SPREAD_REFLECT:
-                               idx = (int) ((long long) pos & NRG_2MASK);
-                               if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                               break;
-                       case NR_GRADIENT_SPREAD_REPEAT:
-                               idx = (int) ((long long) pos & NRG_MASK);
-                               break;
-                       default:
-                               idx = 0;
-                               break;
-                       }
-                       /* Full composition */
-                       s = lgr->vector + 4 * idx;
-                       if (s[3] == 255) {
-                               d[0] = s[0];
-                               d[1] = s[1];
-                               d[2] = s[2];
-                               d[3] = 255;
-                       } else if (s[3] != 0) {
-                               ca = NR_COMPOSEA_112(s[3],d[3]);
-                               d[0] = NR_COMPOSENNN_111121 (s[0], s[3], d[0], d[3], ca);
-                               d[1] = NR_COMPOSENNN_111121 (s[1], s[3], d[1], d[3], ca);
-                               d[2] = NR_COMPOSENNN_111121 (s[2], s[3], d[2], d[3], ca);
-                               d[3] = NR_NORMALIZE_21(ca);
-                       }
-                       d += 4;
-                       pos += lgr->dx;
-               }
-       }
+    dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
+    dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
+    dest[2] = NR_COMPOSEN11_1111(src[2], src[3], dest[2]);
 }
+};
 
+template <typename Subtype, typename spread>
 static void
-nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
+render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
 {
-       int x, y;
-       unsigned char *d;
-       double pos;
-
-       for (y = 0; y < height; y++) {
-               d = px + y * rs;
-               pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
-               for (x = 0; x < width; x++) {
-                       int idx;
-                       const unsigned char *s;
-                       switch (lgr->spread) {
-                       case NR_GRADIENT_SPREAD_PAD:
-                               idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
-                               break;
-                       case NR_GRADIENT_SPREAD_REFLECT:
-                               idx = (int) ((long long) pos & NRG_2MASK);
-                               if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                               break;
-                       case NR_GRADIENT_SPREAD_REPEAT:
-                               idx = (int) ((long long) pos & NRG_MASK);
-                               break;
-                       default:
-                               idx = 0;
-                               break;
-                       }
-                       /* Full composition */
-                       s = lgr->vector + 4 * idx;
-                       d[0] = NR_COMPOSEN11_1111 (s[0], s[3], d[0]);
-                       d[1] = NR_COMPOSEN11_1111 (s[1], s[3], d[1]);
-                       d[2] = NR_COMPOSEN11_1111 (s[2], s[3], d[2]);
-                       d += 3;
-                       pos += lgr->dx;
-               }
-       }
+    switch (pb->mode) {
+    case NR_PIXBLOCK_MODE_R8G8B8A8N:
+        if (pb->empty) {
+            typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
+            Subtype::template render<compose, spread>(gr, pb);
+        } else {
+            typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
+            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>(gr, pb);
+        } else {
+            typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
+            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>(gr, pb);
+        } else {
+            typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
+            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>(gr, pb);
+        } else {
+            typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
+            Subtype::template render<compose, spread>(gr, pb);
+        }
+        break;
+    }
 }
 
+template <typename Subtype>
 static void
-nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
+render(NRRenderer *r, NRPixBlock *pb, NRPixBlock */*m*/)
 {
-       int x, y;
-       unsigned char *d;
-       double pos;
-       int bpp;
-       NRPixBlock spb;
-       int x0, y0, width, height, rs;
-
-       x0 = pb->area.x0;
-       y0 = pb->area.y0;
-       width = pb->area.x1 - pb->area.x0;
-       height = pb->area.y1 - pb->area.y0;
-       rs = pb->rs;
-
-       nr_pixblock_setup_extern (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
-                                 (unsigned char *) lgr->vector,
-                                 4 * NR_GRADIENT_VECTOR_LENGTH,
-                                 0, 0);
-    spb.visible_area = pb->visible_area; 
-       bpp = (pb->mode == NR_PIXBLOCK_MODE_A8) ? 1 : (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
-
-       for (y = 0; y < height; y++) {
-               d = NR_PIXBLOCK_PX (pb) + y * rs;
-               pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
-               for (x = 0; x < width; x++) {
-                       int idx;
-                       const unsigned char *s;
-                       switch (lgr->spread) {
-                       case NR_GRADIENT_SPREAD_PAD:
-                               idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
-                               break;
-                       case NR_GRADIENT_SPREAD_REFLECT:
-                               idx = (int) ((long long) pos & NRG_2MASK);
-                               if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                               break;
-                       case NR_GRADIENT_SPREAD_REPEAT:
-                               idx = (int) ((long long) pos & NRG_MASK);
-                               break;
-                       default:
-                               idx = 0;
-                               break;
-                       }
-                       s = lgr->vector + 4 * idx;
-                       nr_compose_pixblock_pixblock_pixel (pb, d, &spb, s);
-                       d += bpp;
-                       pos += lgr->dx;
-               }
-       }
-
-       nr_pixblock_release (&spb);
+    NRGradientRenderer *gr = static_cast<NRGradientRenderer *>(r);
+
+    switch (gr->spread) {
+    case NR_GRADIENT_SPREAD_REPEAT:
+        render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
+        break;
+    case NR_GRADIENT_SPREAD_REFLECT:
+        render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
+        break;
+    case NR_GRADIENT_SPREAD_PAD:
+    default:
+        render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
+    }
+}
 }
 
-/* Radial */
-
-static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
-static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
-static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
-static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
-static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
-
-NRRenderer *
-nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
-                            unsigned char const *cv,
-                            unsigned spread,
-                            NRMatrix const *gs2px,
-                            float cx, float cy,
-                            float fx, float fy,
-                            float r)
-{
-    rgr->vector = cv;
-    rgr->spread = spread;
+/* Linear */
 
-    if (r < NR_EPSILON) {
-        rgr->renderer.render = nr_rgradient_render_block_end;
-    } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
-               NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
-        rgr->renderer.render = nr_rgradient_render_block_symmetric;
-
-        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);
+namespace {
 
-        rgr->cx = 0.0;
-        rgr->cy = 0.0;
-        rgr->fx = rgr->cx;
-        rgr->fy = rgr->cy;
-        rgr->r = 1.0;
-    } else {
-        rgr->renderer.render = nr_rgradient_render_block_optimized;
+struct Linear {
+template <typename compose, typename spread>
+static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
+    NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
 
-        NR::Coord const df = hypot(fx - cx, fy - cy);
-        if (df >= r) {
-            fx = cx + (fx - cx ) * r / (float) df;
-            fy = cy + (fy - cy ) * r / (float) df;
+    int x, y;
+    unsigned char *d;
+    double pos;
+    NRPixBlock spb;
+    int x0, y0, width, height, rs;
+
+    x0 = pb->area.x0;
+    y0 = pb->area.y0;
+    width = pb->area.x1 - pb->area.x0;
+    height = pb->area.y1 - pb->area.y0;
+    rs = pb->rs;
+
+    nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
+                             0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
+                             (unsigned char *) lgr->vector,
+                             4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
+
+    for (y = 0; y < height; y++) {
+        d = NR_PIXBLOCK_PX(pb) + y * rs;
+        pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
+        for (x = 0; x < width; x++) {
+            unsigned char const *s=spread::color_at(pos, lgr->vector);
+            compose::compose(pb, d, &spb, s);
+            d += compose::bpp;
+            pos += lgr->dx;
         }
-
-        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);
-
-        rgr->cx = 1.0;
-        rgr->cy = 0.0;
-        rgr->fx = 0.0;
-        rgr->fy = 0.0;
-        rgr->r = r / (float) hypot(fx - cx, fy - cy);
-        rgr->C = 1.0F - rgr->r * rgr->r;
-        /* INVARIANT: C < 0 */
-        rgr->C = MIN(rgr->C, -NR_EPSILON);
     }
 
-    return (NRRenderer *) rgr;
+    nr_pixblock_release(&spb);
 }
+};
 
-static void
-nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
-{
-    NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
-    nr_rgradient_render_generic_symmetric(rgr, pb);
 }
 
-static void
-nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+NRRenderer *
+nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
+                            const unsigned char *cv, 
+                            unsigned int spread, 
+                            const NR::Matrix *gs2px,
+                            float x0, float y0,
+                            float x1, float y1)
 {
-    NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
-    nr_rgradient_render_generic_optimized(rgr, pb);
-}
+       NR::Matrix n2gs, n2px, px2n;
 
-static void
-nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
-{
-    unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
+       lgr->render = &render<Linear>;
 
-    nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
+       lgr->vector = cv;
+       lgr->spread = spread;
+
+       n2gs[0] = x1 - x0;
+       n2gs[1] = y1 - y0;
+       n2gs[2] = y1 - y0;
+       n2gs[3] = x0 - x1;
+       n2gs[4] = x0;
+       n2gs[5] = y0;
+
+       n2px = n2gs * (*gs2px);
+       px2n = n2px.inverse();
+
+       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;
 }
 
+/* Radial */
+
 /*
  * The archetype is following
  *
@@ -434,114 +369,47 @@ nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
  * (12)  Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
  */
 
-static void
-nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
+namespace {
+
+struct SymmetricRadial {
+template <typename compose, typename spread>
+static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 {
-    NR::Coord const dx = rgr->px2gs.c[0];
-    NR::Coord const dy = rgr->px2gs.c[1];
-
-    if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
-        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];
-            for (int x = pb->area.x0; x < pb->area.x1; x++) {
-                NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
-                int idx;
-                if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
-                    idx = (int) ((long long) pos & NRG_2MASK);
-                    if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
-                    idx = (int) ((long long) pos & NRG_MASK);
-                } else {
-                    idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
-                }
-                unsigned char const *s = rgr->vector + 4 * idx;
-                d[0] = NR_COMPOSENPP_1111(s[0], s[3], d[0]);
-                d[1] = NR_COMPOSENPP_1111(s[1], s[3], d[1]);
-                d[2] = NR_COMPOSENPP_1111(s[2], s[3], d[2]);
-                d[3] = NR_COMPOSEA_111(s[3], d[3]);
-                d += 4;
-                gx += dx;
-                gy += dy;
-            }
-        }
-    } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
-        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];
-            for (int x = pb->area.x0; x < pb->area.x1; x++) {
-                NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
-                int idx;
-                if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
-                    idx = (int) ((long long) pos & NRG_2MASK);
-                    if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
-                    idx = (int) ((long long) pos & NRG_MASK);
-                } else {
-                    idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
-                }
-                unsigned char const *s = rgr->vector + 4 * idx;
-                if (s[3] == 255) {
-                    d[0] = s[0];
-                    d[1] = s[1];
-                    d[2] = s[2];
-                    d[3] = 255;
-                } else if (s[3] != 0) {
-                    unsigned ca = NR_COMPOSEA_112(s[3], d[3]);
-                    d[0] = NR_COMPOSENNN_111121(s[0], s[3], d[0], d[3], ca);
-                    d[1] = NR_COMPOSENNN_111121(s[1], s[3], d[1], d[3], ca);
-                    d[2] = NR_COMPOSENNN_111121(s[2], s[3], d[2], d[3], ca);
-                    d[3] = NR_NORMALIZE_21(ca);
-                }
-                d += 4;
-                gx += dx;
-                gy += dy;
-            }
-        }
-    } else {
-        NRPixBlock spb;
-        nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
-                                 (unsigned char *) rgr->vector,
-                                 4 * NR_GRADIENT_VECTOR_LENGTH,
-                                 0, 0);
-        int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
-                          ? 1
-                          : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
-                          ? 3
-                          : 4 );
-
-        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];
-            for (int x = pb->area.x0; x < pb->area.x1; x++) {
-                NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
-                int idx;
-                if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
-                    idx = (int) ((long long) pos & NRG_2MASK);
-                    if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
-                    idx = (int) ((long long) pos & NRG_MASK);
-                } else {
-                    idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
-                }
-                unsigned char const *s = rgr->vector + 4 * idx;
-                nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
-                d += bpp;
-                gx += dx;
-                gy += dy;
-            }
-        }
+    NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
+
+    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,
+                             0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
+                             (unsigned char *) rgr->vector,
+                             4 * NR_GRADIENT_VECTOR_LENGTH,
+                             0, 0);
 
-        nr_pixblock_release(&spb);
+    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[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 += compose::bpp;
+            gx += dx;
+            gy += dy;
+        }
     }
+
+    nr_pixblock_release(&spb);
 }
+};
 
-static void
-nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
+struct Radial {
+template <typename compose, typename spread>
+static void render(NRGradientRenderer *gr, NRPixBlock *pb)
 {
+    NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
     int const x0 = pb->area.x0;
     int const y0 = pb->area.y0;
     int const x1 = pb->area.x1;
@@ -549,22 +417,18 @@ nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
     int const rs = pb->rs;
 
     NRPixBlock spb;
-    nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
+    nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
+                             0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
                              (unsigned char *) rgr->vector,
                              4 * NR_GRADIENT_VECTOR_LENGTH,
                              0, 0);
-    int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
-                      ? 1
-                      : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
-                      ? 3
-                      : 4 );
 
     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;
@@ -574,23 +438,17 @@ nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, 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;
-            int idx;
+            NR::Coord const pos = gxy2 / pxgx * (NR_GRADIENT_VECTOR_LENGTH-1);
+
+            unsigned char const *s;
             if (pos < (1U << 31)) {
-                if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
-                    idx = (int) ((long long) pos & NRG_2MASK);
-                    if (idx > NRG_MASK) idx = NRG_2MASK - idx;
-                } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
-                    idx = (int) ((long long) pos & NRG_MASK);
-                } else {
-                    idx = (int) CLAMP(pos, 0, (double) (NR_GRADIENT_VECTOR_LENGTH - 1));
-                }
+                s = spread::color_at(pos, rgr->vector);
             } else {
-                idx = NR_GRADIENT_VECTOR_LENGTH - 1;
+                s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
             }
-            unsigned char const *s = rgr->vector + 4 * idx;
-            nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
-            d += bpp;
+
+            compose::compose(pb, d, &spb, s);
+            d += compose::bpp;
 
             gx += dx;
             gy += dy;
@@ -599,7 +457,90 @@ nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
 
     nr_pixblock_release(&spb);
 }
+};
+
+}
+
+static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
+
+NRRenderer *
+nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
+                            unsigned char const *cv,
+                            unsigned spread,
+                            NR::Matrix const *gs2px,
+                            float cx, float cy,
+                            float fx, float fy,
+                            float r)
+{
+    rgr->vector = cv;
+    rgr->spread = spread;
+
+    if (r < NR_EPSILON) {
+        rgr->render = nr_rgradient_render_block_end;
+    } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
+               NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
+        rgr->render = render<SymmetricRadial>;
+
+        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;
+        rgr->fx = rgr->cx;
+        rgr->fy = rgr->cy;
+        rgr->r = 1.0;
+    } else {
+        rgr->render = render<Radial>;
+
+        NR::Coord const df = hypot(fx - cx, fy - cy);
+        if (df >= r) {
+            fx = cx + (fx - cx ) * r / (float) df;
+            fy = cy + (fy - cy ) * r / (float) df;
+        }
+
+        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;
+        rgr->fx = 0.0;
+        rgr->fy = 0.0;
+        rgr->r = r / (float) hypot(fx - cx, fy - cy);
+        rgr->C = 1.0F - rgr->r * rgr->r;
+        /* INVARIANT: C < 0 */
+        rgr->C = MIN(rgr->C, -NR_EPSILON);
+    }
+
+    return (NRRenderer *) rgr;
+}
 
+static void
+nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+{
+    unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
+
+    nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
+}
 
 /*
   Local Variables:
@@ -610,4 +551,4 @@ nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :