summaryrefslogtreecommitdiffstats
path: root/src/libnr/nr-gradient.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/libnr/nr-gradient.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/libnr/nr-gradient.cpp')
-rw-r--r--src/libnr/nr-gradient.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/libnr/nr-gradient.cpp b/src/libnr/nr-gradient.cpp
new file mode 100644
index 000000000..fa4f9f91f
--- /dev/null
+++ b/src/libnr/nr-gradient.cpp
@@ -0,0 +1,317 @@
+#define __NR_GRADIENT_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixops.h>
+#include <libnr/nr-pixblock-pixel.h>
+#include <libnr/nr-blit.h>
+#include <libnr/nr-gradient.h>
+
+#define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
+#define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
+
+/* 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;
+
+ 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);
+
+ 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;
+
+ 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;
+ }
+
+ 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;
+}
+
+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)
+{
+ NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
+ nr_rgradient_render_generic_optimized(rgr, pb);
+}
+
+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]);
+}
+
+/*
+ * The archetype is following
+ *
+ * gx gy - pixel coordinates
+ * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
+ *
+ * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
+ * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
+ *
+ * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
+ * (4) (gy - fy) / (gx - fx) = D
+ * (5) Py = D * Px - D * fx + fy
+ *
+ * (6) D * fx - fy + cy = N
+ * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
+ * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
+ *
+ * (9) A = D * D + 1
+ * (10) B = -2 * (cx + D * N)
+ * (11) C = cx * cx + N * N - r * r
+ *
+ * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
+ */
+
+static void
+nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, 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 = hypot(gx, 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(s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSENPP(s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSENPP(s[2], s[3], d[2], d[3]);
+ d[3] = (255*255 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
+ 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 = hypot(gx, 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 = 255*255 - (255 - s[3]) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7(s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7(s[1], s[3], d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7(s[2], s[3], d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ 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 = hypot(gx, 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;
+ }
+ }
+
+ nr_pixblock_release(&spb);
+ }
+}
+
+static void
+nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
+{
+ int const x0 = pb->area.x0;
+ int const y0 = pb->area.y0;
+ int const x1 = pb->area.x1;
+ int const y1 = pb->area.y1;
+ int const rs = pb->rs;
+
+ 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 = 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];
+ for (int x = x0; x < x1; x++) {
+ NR::Coord const gx2 = gx * gx;
+ NR::Coord const gxy2 = gx2 + gy * gy;
+ NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
+ /* INVARIANT: qgx2_4 >= 0.0 */
+ /* qgx2_4 = MAX(qgx2_4, 0.0); */
+ 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;
+ 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));
+ }
+ } else {
+ idx = NR_GRADIENT_VECTOR_LENGTH - 1;
+ }
+ unsigned char const *s = rgr->vector + 4 * idx;
+ nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
+ d += bpp;
+
+ gx += dx;
+ gy += dy;
+ }
+ }
+
+ nr_pixblock_release(&spb);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :