diff options
Diffstat (limited to 'src/display/sodipodi-ctrl.cpp')
| -rw-r--r-- | src/display/sodipodi-ctrl.cpp | 238 |
1 files changed, 126 insertions, 112 deletions
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 202fc206e..5e939ffee 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -11,7 +11,7 @@ #include <2geom/transforms.h> #include "sp-canvas-util.h" #include "sodipodi-ctrl.h" -#include "libnr/nr-pixops.h" +#include "display/cairo-utils.h" enum { ARG_0, @@ -125,7 +125,7 @@ sp_ctrl_destroy (GtkObject *object) ctrl = SP_CTRL (object); if (ctrl->cache) { - g_free(ctrl->cache); + delete[] ctrl->cache; ctrl->cache = NULL; } @@ -175,11 +175,12 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) sp_canvas_item_request_update (item); break; - case ARG_FILL_COLOR: - ctrl->fill_color = GTK_VALUE_INT (*arg); + case ARG_FILL_COLOR: { + guint32 fill = GTK_VALUE_INT (*arg); + ctrl->fill_color = fill; ctrl->build = FALSE; sp_canvas_item_request_update (item); - break; + } break; case ARG_STROKED: ctrl->stroked = GTK_VALUE_BOOL (*arg); @@ -187,11 +188,12 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) sp_canvas_item_request_update (item); break; - case ARG_STROKE_COLOR: - ctrl->stroke_color = GTK_VALUE_INT (*arg); + case ARG_STROKE_COLOR: { + guint32 stroke = GTK_VALUE_INT (*arg); + ctrl->stroke_color = stroke; ctrl->build = FALSE; sp_canvas_item_request_update (item); - break; + } break; case ARG_PIXBUF: pixbuf = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg)); @@ -297,52 +299,56 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) static void sp_ctrl_build_cache (SPCtrl *ctrl) { - guchar * p, *q; + guint32 *p, *q; gint size, x, y, z, s, a, side, c; - guint8 fr, fg, fb, fa, sr, sg, sb, sa; + guint32 stroke_color, fill_color; if (ctrl->filled) { - fr = (ctrl->fill_color >> 24) & 0xff; - fg = (ctrl->fill_color >> 16) & 0xff; - fb = (ctrl->fill_color >> 8) & 0xff; - fa = (ctrl->fill_color) & 0xff; + if (ctrl->mode == SP_CTRL_MODE_XOR) { + fill_color = ctrl->fill_color; + } else { + fill_color = argb32_from_rgba(ctrl->fill_color); + } } else { - fr = 0x00; fg = 0x00; fb = 0x00; fa = 0x00; + fill_color = 0; } if (ctrl->stroked) { - sr = (ctrl->stroke_color >> 24) & 0xff; - sg = (ctrl->stroke_color >> 16) & 0xff; - sb = (ctrl->stroke_color >> 8) & 0xff; - sa = (ctrl->stroke_color) & 0xff; + if (ctrl->mode == SP_CTRL_MODE_XOR) { + stroke_color = ctrl->stroke_color; + } else { + stroke_color = argb32_from_rgba(ctrl->stroke_color); + } } else { - sr = fr; sg = fg; sb = fb; sa = fa; + stroke_color = fill_color; } side = (ctrl->span * 2 +1); - c = ctrl->span ; - size = (side) * (side) * 4; + c = ctrl->span; + size = side * side; if (side < 2) return; - if (ctrl->cache) - g_free (ctrl->cache); - ctrl->cache = (guchar*)g_malloc (size); + if (ctrl->cache) delete[] ctrl->cache; + ctrl->cache = new guint32[size]; switch (ctrl->shape) { case SP_CTRL_SHAPE_SQUARE: p = ctrl->cache; + // top edge for (x=0; x < side; x++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } + // middle for (y = 2; y < side; y++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; // stroke at first and last pixel for (x=2; x < side; x++) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; + *p++ = fill_color; // fill in the middle } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } + // bottom edge for (x=0; x < side; x++) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; + *p++ = stroke_color; } ctrl->build = TRUE; break; @@ -352,19 +358,20 @@ sp_ctrl_build_cache (SPCtrl *ctrl) for (y = 0; y < side; y++) { z = abs (c - y); for (x = 0; x < z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; for (; x < side - z -1; x++) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; + *p++ = fill_color; } if (z != c) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; } for (; x < side; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } } + ctrl->build = TRUE; break; case SP_CTRL_SHAPE_CIRCLE: @@ -376,28 +383,28 @@ sp_ctrl_build_cache (SPCtrl *ctrl) z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a)); x = 0; while (x < c-z) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; - *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; + *p++ = 0; + *q-- = 0; x++; } do { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; - *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr; + *p++ = stroke_color; + *q-- = stroke_color; x++; } while (x < c-s); while (x < MIN(c+s+1, c+z)) { - *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; - *q-- = fa; *q-- = fb; *q-- = fg; *q-- = fr; + *p++ = fill_color; + *q-- = fill_color; x++; } do { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; - *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr; + *p++ = stroke_color; + *q-- = stroke_color; x++; } while (x <= c+z); while (x < side) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; - *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; + *p++ = 0; + *q-- = 0; x++; } s = z; @@ -410,17 +417,17 @@ sp_ctrl_build_cache (SPCtrl *ctrl) for (y = 0; y < side; y++) { z = abs (c - y); for (x = 0; x < c-z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; for (; x < c + z; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } if (z != 0) { - *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; + *p++ = stroke_color; x++; } for (; x < side; x++) { - *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; + *p++ = 0; } } ctrl->build = TRUE; @@ -433,28 +440,19 @@ sp_ctrl_build_cache (SPCtrl *ctrl) px = gdk_pixbuf_get_pixels (ctrl->pixbuf); rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); for (y = 0; y < side; y++){ - unsigned char *s, *d; + guint32 *d; + unsigned char *s; s = px + y * rs; - d = ctrl->cache + 4 * side * y; + d = ctrl->cache + side * y; for (x = 0; x < side; x++) { if (s[3] < 0x80) { - d[0] = 0x00; - d[1] = 0x00; - d[2] = 0x00; - d[3] = 0x00; + *d++ = 0; } else if (s[0] < 0x80) { - d[0] = sr; - d[1] = sg; - d[2] = sb; - d[3] = sa; + *d++ = stroke_color; } else { - d[0] = fr; - d[1] = fg; - d[2] = fb; - d[3] = fa; + *d++ = fill_color; } s += 4; - d += 4; } } } else { @@ -466,16 +464,13 @@ sp_ctrl_build_cache (SPCtrl *ctrl) case SP_CTRL_SHAPE_IMAGE: if (ctrl->pixbuf) { guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf); - guchar * pix; - q = gdk_pixbuf_get_pixels (ctrl->pixbuf); + guint32 *px; + guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf); p = ctrl->cache; for (y = 0; y < side; y++){ - pix = q + (y * r); + px = reinterpret_cast<guint32*>(data + y * r); for (x = 0; x < side; x++) { - *p++ = *pix++; - *p++ = *pix++; - *p++ = *pix++; - *p++ = *pix++; + *p++ = *px++; } } } else { @@ -487,65 +482,84 @@ sp_ctrl_build_cache (SPCtrl *ctrl) default: break; } - } -// composite background, foreground, alpha for xor mode -#define COMPOSE_X(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) ((b ^ ~f) + b/4 - (b>127? 63 : 0))) * ((guchar) a) ) ) -// composite background, foreground, alpha for color mode -#define COMPOSE_N(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) f) * ((guchar) a) ) ) +static inline guint32 compose_xor(guint32 bg, guint32 fg, guint32 a) +{ + guint32 c = bg * (255-a) + (((bg ^ ~fg) + (bg >> 2) - (bg > 127 ? 63 : 0)) & 255) * a; + return (c + 127) / 255; +} static void sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf) { - gint y0, y1, y, x0,x1,x; - guchar *p, *q, a; + //gint y0, y1, y, x0,x1,x; + //guchar *p, *q, a; SPCtrl *ctrl = SP_CTRL (item); if (!ctrl->defined) return; if ((!ctrl->filled) && (!ctrl->stroked)) return; - sp_canvas_prepare_buffer (buf); - // the control-image is rendered into ctrl->cache if (!ctrl->build) { sp_ctrl_build_cache (ctrl); } - // then we render from ctrl->cache - y0 = MAX (ctrl->box.y0, buf->rect.y0); - y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1); - x0 = MAX (ctrl->box.x0, buf->rect.x0); - x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1); - - bool colormode; - - for (y = y0; y <= y1; y++) { - p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4; - q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4; - for (x = x0; x <= x1; x++) { - a = *(q + 3); - // 00000000 is the only way to get invisible; all other colors with alpha 00 are treated as mode_color with alpha ff - colormode = false; - if (a == 0x00 && !(q[0] == 0x00 && q[1] == 0x00 && q[2] == 0x00)) { - a = 0xff; - colormode = true; - } - if (ctrl->mode == SP_CTRL_MODE_COLOR || colormode) { - p[0] = COMPOSE_N (p[0], q[0], a); - p[1] = COMPOSE_N (p[1], q[1], a); - p[2] = COMPOSE_N (p[2], q[2], a); - q += 4; - p += 4; - } else if (ctrl->mode == SP_CTRL_MODE_XOR) { - p[0] = COMPOSE_X (p[0], q[0], a); - p[1] = COMPOSE_X (p[1], q[1], a); - p[2] = COMPOSE_X (p[2], q[2], a); - q += 4; - p += 4; + int w, h; + w = h = (ctrl->span * 2 +1); + + // The code below works even when the target is not an image surface + if (ctrl->mode == SP_CTRL_MODE_XOR) { + // 1. Copy the affected part of output to a temporary surface + cairo_surface_t *work = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cairo_t *cr = cairo_create(work); + cairo_translate(cr, -ctrl->box.x0, -ctrl->box.y0); + cairo_set_source_surface(cr, cairo_get_target(buf->ct), buf->rect.x0, buf->rect.y0); + cairo_paint(cr); + cairo_destroy(cr); + + // 2. Composite the control on a temporary surface + cairo_surface_flush(work); + int strideb = cairo_image_surface_get_stride(work); + unsigned char *pxb = cairo_image_surface_get_data(work); + guint32 *p = ctrl->cache; + for (int i=0; i<h; ++i) { + guint32 *pb = reinterpret_cast<guint32*>(pxb + i*strideb); + for (int j=0; j<w; ++j) { + guint32 cc = *p++; + guint32 ac = cc & 0xff; + if (ac == 0 && cc != 0) { + *pb++ = argb32_from_rgba(cc | 0x000000ff); + } else { + EXTRACT_ARGB32(*pb, ab,rb,gb,bb) + guint32 ro = compose_xor(rb, (cc & 0xff000000) >> 24, ac); + guint32 go = compose_xor(gb, (cc & 0x00ff0000) >> 16, ac); + guint32 bo = compose_xor(bb, (cc & 0x0000ff00) >> 8, ac); + ASSEMBLE_ARGB32(px, ab,ro,go,bo) + *pb++ = px; + } } } + cairo_surface_mark_dirty(work); + + // 3. Replace the affected part of output with contents of temporary surface + cairo_save(buf->ct); + cairo_set_source_surface(buf->ct, work, + ctrl->box.x0 - buf->rect.x0, ctrl->box.y0 - buf->rect.y0); + cairo_rectangle(buf->ct, ctrl->box.x0 - buf->rect.x0, ctrl->box.y0 - buf->rect.y0, w, h); + cairo_clip(buf->ct); + cairo_set_operator(buf->ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(buf->ct); + cairo_restore(buf->ct); + cairo_surface_destroy(work); + } else { + cairo_surface_t *cache = cairo_image_surface_create_for_data( + reinterpret_cast<unsigned char*>(ctrl->cache), CAIRO_FORMAT_ARGB32, w, h, w*4); + cairo_set_source_surface(buf->ct, cache, + ctrl->box.x0 - buf->rect.x0, ctrl->box.y0 - buf->rect.y0); + cairo_paint(buf->ct); + cairo_surface_destroy(cache); } ctrl->shown = TRUE; } |
