index fa4f9f91fb05b804d0e9904ed8ba5b1572849774..cb03c048bd547995109312a60426b574440bfce4 100644 (file)
*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
+ * MenTaLguY <mental@rydia.net>
+ *...Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
*
- * This code is in public domain
+ * Copyright (C) 2009 Jasper van de Gronde
+ * Copyright (C) 2007 MenTaLguY
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+/*
+ * 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 NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
#define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
-/* Radial */
+namespace {
+inline unsigned char const *vector_index(int idx,
+ unsigned char const *vector)
+{
+ return vector + 4 * idx;
+}
-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);
+template <NRGradientSpread spread> struct Spread;
-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)
+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)
{
- rgr->vector = cv;
- rgr->spread = spread;
+ 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);
+}
+};
- 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);
+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);
+}
+};
- 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;
+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);
+}
+};
- 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;
- }
+template <NR_PIXBLOCK_MODE mode> struct ModeTraits;
- 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;
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8N> {
+static const unsigned bpp=4;
+};
- NRMatrix n2px;
- nr_matrix_multiply(&n2px, &n2gs, gs2px);
- nr_matrix_invert(&rgr->px2gs, &n2px);
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8P> {
+static const unsigned bpp=4;
+};
- 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);
- }
+template <>
+struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8> {
+static const unsigned bpp=3;
+};
- return (NRRenderer *) rgr;
+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);
}
+};
-static void
-nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+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);
+}
+};
+
+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];
+}
+};
+
+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 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]);
+ 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);
+}
+};
+
+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)
+{
+ 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]);
+}
+};
+
+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)
{
- NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
- nr_rgradient_render_generic_symmetric(rgr, pb);
+ 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_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
{
- NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
- nr_rgradient_render_generic_optimized(rgr, pb);
+ 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_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+render(NRRenderer *r, NRPixBlock *pb, NRPixBlock */*m*/)
{
- unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
+ NRGradientRenderer *gr = static_cast<NRGradientRenderer *>(r);
- nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
+ 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);
+ }
+}
+}
+
+/* Linear */
+
+namespace {
+
+struct Linear {
+template <typename compose, typename spread>
+static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
+ NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
+
+ 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;
+ }
+ }
+
+ nr_pixblock_release(&spb);
+}
+};
+
+}
+
+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)
+{
+ NR::Matrix n2gs, n2px, px2n;
+
+ lgr->render = &render<Linear>;
+
+ 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
*
* (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 = 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;
- }
- }
+ NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
- nr_pixblock_release(&spb);
+ 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);
+
+ 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;
@@ -253,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;
@@ -278,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;
@@ -303,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:
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 :