summaryrefslogtreecommitdiffstats
path: root/src/display/sodipodi-ctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/sodipodi-ctrl.cpp')
-rw-r--r--src/display/sodipodi-ctrl.cpp361
1 files changed, 192 insertions, 169 deletions
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp
index a165fd52e..5e939ffee 100644
--- a/src/display/sodipodi-ctrl.cpp
+++ b/src/display/sodipodi-ctrl.cpp
@@ -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;
}
@@ -176,9 +176,7 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
break;
case ARG_FILL_COLOR: {
- // treat colors with zero alpha as opaque
guint32 fill = GTK_VALUE_INT (*arg);
- fill = ((fill & 0xff) == 0 && fill) ? fill | 0xff : fill;
ctrl->fill_color = fill;
ctrl->build = FALSE;
sp_canvas_item_request_update (item);
@@ -191,9 +189,7 @@ sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
break;
case ARG_STROKE_COLOR: {
- // treat colors with zero alpha as opaque
guint32 stroke = GTK_VALUE_INT (*arg);
- stroke = ((stroke & 0xff) == 0 && stroke) ? stroke | 0xff : stroke;
ctrl->stroke_color = stroke;
ctrl->build = FALSE;
sp_canvas_item_request_update (item);
@@ -303,101 +299,162 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
static void
sp_ctrl_build_cache (SPCtrl *ctrl)
{
- //guchar * p, *q;
- //int size, x, y, z, s, a, side, c;
- //guint8 fr, fg, fb, fa, sr, sg, sb, sa;
-
- /*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;
+ guint32 *p, *q;
+ gint size, x, y, z, s, a, side, c;
+ guint32 stroke_color, fill_color;
+
+ if (ctrl->filled) {
+ 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;
+ }
- int w, h; // for clarity; w and h are always odd
- w = h = (ctrl->span * 2 +1);
- int c = ctrl->span ;
- if (ctrl->cache) {
- cairo_surface_finish(ctrl->cache);
- cairo_surface_destroy(ctrl->cache);
- }
- ctrl->cache = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
- cairo_t *cr = cairo_create(ctrl->cache);
+ side = (ctrl->span * 2 +1);
+ c = ctrl->span;
+ size = side * side;
+ if (side < 2) return;
- bool supress_paint = false;
+ if (ctrl->cache) delete[] ctrl->cache;
+ ctrl->cache = new guint32[size];
switch (ctrl->shape) {
case SP_CTRL_SHAPE_SQUARE:
- cairo_rectangle(cr, 0, 0, w, h);
+ p = ctrl->cache;
+ // top edge
+ for (x=0; x < side; x++) {
+ *p++ = stroke_color;
+ }
+ // middle
+ for (y = 2; y < side; y++) {
+ *p++ = stroke_color; // stroke at first and last pixel
+ for (x=2; x < side; x++) {
+ *p++ = fill_color; // fill in the middle
+ }
+ *p++ = stroke_color;
+ }
+ // bottom edge
+ for (x=0; x < side; x++) {
+ *p++ = stroke_color;
+ }
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_DIAMOND:
- cairo_move_to(cr, c, 0); // c stands for "center" - it is half of the width / height
- cairo_line_to(cr, w, c);
- cairo_line_to(cr, c, h);
- cairo_line_to(cr, 0, c);
- cairo_close_path(cr);
+ p = ctrl->cache;
+ for (y = 0; y < side; y++) {
+ z = abs (c - y);
+ for (x = 0; x < z; x++) {
+ *p++ = 0;
+ }
+ *p++ = stroke_color; x++;
+ for (; x < side - z -1; x++) {
+ *p++ = fill_color;
+ }
+ if (z != c) {
+ *p++ = stroke_color; x++;
+ }
+ for (; x < side; x++) {
+ *p++ = 0;
+ }
+ }
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_CIRCLE:
- cairo_arc(cr, 0.5+c, 0.5+c, c, 0, 2*M_PI);
- cairo_close_path(cr);
+ p = ctrl->cache;
+ q = p + size -1;
+ s = -1;
+ for (y = 0; y <= c ; y++) {
+ a = abs (c - y);
+ z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
+ x = 0;
+ while (x < c-z) {
+ *p++ = 0;
+ *q-- = 0;
+ x++;
+ }
+ do {
+ *p++ = stroke_color;
+ *q-- = stroke_color;
+ x++;
+ } while (x < c-s);
+ while (x < MIN(c+s+1, c+z)) {
+ *p++ = fill_color;
+ *q-- = fill_color;
+ x++;
+ }
+ do {
+ *p++ = stroke_color;
+ *q-- = stroke_color;
+ x++;
+ } while (x <= c+z);
+ while (x < side) {
+ *p++ = 0;
+ *q-- = 0;
+ x++;
+ }
+ s = z;
+ }
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_CROSS:
- cairo_move_to(cr, 0.5, 0.5);
- cairo_line_to(cr, -0.5+w, -0.5+h);
- cairo_move_to(cr, -0.5+w, 0.5); // right stroke
- cairo_line_to(cr, 0.5, -0.5+h);
- cairo_set_line_width(cr, 1);
- cairo_stroke(cr);
- supress_paint = true;
+ p = ctrl->cache;
+ for (y = 0; y < side; y++) {
+ z = abs (c - y);
+ for (x = 0; x < c-z; x++) {
+ *p++ = 0;
+ }
+ *p++ = stroke_color; x++;
+ for (; x < c + z; x++) {
+ *p++ = 0;
+ }
+ if (z != 0) {
+ *p++ = stroke_color; x++;
+ }
+ for (; x < side; x++) {
+ *p++ = 0;
+ }
+ }
ctrl->build = TRUE;
break;
case SP_CTRL_SHAPE_BITMAP:
if (ctrl->pixbuf) {
- gdk_cairo_set_source_pixbuf(cr, ctrl->pixbuf, 0, 0);
- cairo_paint(cr);
- cairo_surface_flush(ctrl->cache);
-
- // TODO lame!!! find a way to do this without direct pixel manipulation.
- int stride = cairo_image_surface_get_stride(ctrl->cache);
- guint32 *px = reinterpret_cast<guint32*>(cairo_image_surface_get_data(ctrl->cache));
-
- // fix byte order. fill_color is 0xrrggbbaa, cairo needs 0xaarrggbb.
- // both quantities are native-endian, so it should be portable.
- guint32 fill = ctrl->fill_color;
- guint32 stroke = ctrl->stroke_color;
- fill = ((fill & 0xff) << 24) | ((fill & 0xffffff00) >> 8);
- stroke = ((stroke & 0xff) << 24) | ((stroke & 0xffffff00) >> 8);
-
- for (int i = 0; i < h; ++i) {
- for (int j = 0; j < w; ++j) {
- int index = i * stride / 4 + j;
- if (px[index] & 0xff000000) {
- px[index] = (px[index] & 0x00ffffff) ? stroke : fill;
+ unsigned char *px;
+ unsigned int rs;
+ px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
+ rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
+ for (y = 0; y < side; y++){
+ guint32 *d;
+ unsigned char *s;
+ s = px + y * rs;
+ d = ctrl->cache + side * y;
+ for (x = 0; x < side; x++) {
+ if (s[3] < 0x80) {
+ *d++ = 0;
+ } else if (s[0] < 0x80) {
+ *d++ = stroke_color;
} else {
- px[index] = 0;
+ *d++ = fill_color;
}
+ s += 4;
}
}
- cairo_surface_mark_dirty(ctrl->cache);
- supress_paint = true;
} else {
g_print ("control has no pixmap\n");
}
@@ -406,9 +463,16 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
case SP_CTRL_SHAPE_IMAGE:
if (ctrl->pixbuf) {
- gdk_cairo_set_source_pixbuf(cr, ctrl->pixbuf, 0, 0);
- cairo_paint(cr);
- supress_paint = true;
+ guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
+ guint32 *px;
+ guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf);
+ p = ctrl->cache;
+ for (y = 0; y < side; y++){
+ px = reinterpret_cast<guint32*>(data + y * r);
+ for (x = 0; x < side; x++) {
+ *p++ = *px++;
+ }
+ }
} else {
g_print ("control has no pixmap\n");
}
@@ -418,27 +482,13 @@ sp_ctrl_build_cache (SPCtrl *ctrl)
default:
break;
}
-
- if (ctrl->build && !supress_paint) {
- if (ctrl->filled) {
- ink_cairo_set_source_rgba32(cr, ctrl->fill_color);
- cairo_fill_preserve(cr);
- }
- if (ctrl->stroked) {
- ink_cairo_set_source_rgba32(cr, ctrl->stroke_color);
- cairo_set_line_width(cr, 2);
- cairo_clip_preserve(cr);
- cairo_stroke(cr);
- }
- }
-
- cairo_destroy(cr);
}
-// 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)
@@ -456,88 +506,61 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
sp_ctrl_build_cache (ctrl);
}
- cairo_set_source_surface(buf->ct, ctrl->cache,
- ctrl->box.x0 - buf->rect.x0, ctrl->box.y0 - buf->rect.y0);
- cairo_paint(buf->ct);
-
- /*
- double x0 = ctrl->box.x0;
- double y0 = ctrl->box.y0;
- double w = ctrl->box.x1 - ctrl->box.x0 + 1;
- double h = ctrl->box.y1 - ctrl->box.y0 + 1;
- //guint32 fill = ctrl->fill_color;
- //fill = (fill & 0xff == 0 && fill) ? fill | 0xff : fill;
-
-
- switch (ctrl->shape) {
- case SP_CTRL_SHAPE_SQUARE:
- cairo_rectangle(buf->ct, x0, y0, w, h);
- break;
- case SP_CTRL_SHAPE_DIAMOND:
- cairo_move_to(buf->ct, x0 + w/2, y0);
- cairo_line_to(buf->ct, x0 + w, y0 + h/2);
- cairo_line_to(buf->ct, x0 + w/2, y0 + h);
- cairo_line_to(buf->ct, x0, y0 + h/2);
- cairo_close_path(buf->ct);
- break;
- //case SP_CTRL_SHAPE_CIRCLE:
- default:
- cairo_arc(buf->ct, x0 + w/2, y0 + h/2, w/2, 0, 2*M_PI);
- cairo_close_path(buf->ct);
- break;
- }
-
- //if (ctrl->mode == SP_CTRL_MODE_XOR) {
- // cairo_set_operator(buf->ct, CAIRO_OPERATOR_XOR);
- //}
- if (ctrl->filled) {
- ink_cairo_set_source_rgba32(buf->ct, ctrl->fill_color);
- cairo_fill_preserve(buf->ct);
- }
- if (ctrl->stroked) {
- ink_cairo_set_source_rgba32(buf->ct, ctrl->stroke_color);
- cairo_set_line_width(buf->ct, 2);
- cairo_clip_preserve(buf->ct);
- cairo_stroke_preserve(buf->ct);
- }
+ int w, h;
+ w = h = (ctrl->span * 2 +1);
- cairo_new_path(buf->ct);
- cairo_restore(buf->ct);*/
-
- #if 0
- // 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);
-
- 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;
+ // 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);
}
- #endif
ctrl->shown = TRUE;
}