diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2017-11-27 14:09:09 +0000 |
|---|---|---|
| committer | Tavmjong Bah <tavmjong@free.fr> | 2017-11-27 14:09:09 +0000 |
| commit | 54340f1958acd5a3fb1f0c3db480a96e42156866 (patch) | |
| tree | e987f2778e532da95242ef96a31aa1de8d16facb /src/display/sodipodi-ctrl.cpp | |
| parent | Simple scaling of controls for high DPI screens (nodes, arrows, etc.). (diff) | |
| download | inkscape-54340f1958acd5a3fb1f0c3db480a96e42156866.tar.gz inkscape-54340f1958acd5a3fb1f0c3db480a96e42156866.zip | |
Adapt control shapes for hi DPI displays.
Rework how shapes are generated to simplify code.
Diffstat (limited to 'src/display/sodipodi-ctrl.cpp')
| -rw-r--r-- | src/display/sodipodi-ctrl.cpp | 349 |
1 files changed, 182 insertions, 167 deletions
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 80020c0bd..fdb079446 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -7,6 +7,7 @@ */ #include <2geom/transforms.h> +#include <2geom/line.h> #include "sp-canvas-util.h" #include "sodipodi-ctrl.h" #include "display/cairo-utils.h" @@ -303,9 +304,9 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) } bool -sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1){ - //http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment - return Geom::are_near(Geom::distance(a,c) + Geom::distance(c,b) , Geom::distance(a,b), tolerance); +sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1) { + Geom::LineSegment segment(a, b); + return Geom::are_near(c, segment, tolerance); } bool @@ -320,12 +321,9 @@ sp_point_inside_triangle(Geom::Point p1,Geom::Point p2,Geom::Point p3, Geom::Poi } static void -sp_ctrl_build_cache (SPCtrl *ctrl) +sp_ctrl_build_cache (SPCtrl *ctrl, int device_scale) { - guint32 *p, *q; - gint size, x, y, z, s, a, width, height, c; guint32 stroke_color, fill_color; - if (ctrl->filled) { if (ctrl->mode == SP_CTRL_MODE_XOR) { fill_color = ctrl->fill_color; @@ -335,6 +333,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) } else { fill_color = 0; } + if (ctrl->stroked) { if (ctrl->mode == SP_CTRL_MODE_XOR) { stroke_color = ctrl->stroke_color; @@ -344,203 +343,226 @@ sp_ctrl_build_cache (SPCtrl *ctrl) } else { stroke_color = fill_color; } - gint32 stroke_color_smooth = SP_RGBA32_F_COMPOSE(SP_RGBA32_R_F(stroke_color), SP_RGBA32_G_F(stroke_color), SP_RGBA32_B_F(stroke_color), 0.15); - width = (ctrl->width * 2 +1); - height = (ctrl->height * 2 +1); - c = ctrl->width; // Only used for pre-set square drawing - size = width * height; + + gint width = (ctrl->width * 2 + 1) * device_scale; + gint height = (ctrl->height * 2 + 1) * device_scale; if (width < 2) return; + gint size = width * height; if (ctrl->cache) delete[] ctrl->cache; ctrl->cache = new guint32[size]; - Geom::Point point; - Geom::Point p1; - Geom::Point p2; - Geom::Point p3; - if(ctrl->shape == SP_CTRL_SHAPE_TRIANGLE){ - Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0)); - m *= Geom::Rotate(-ctrl->angle); - m *= Geom::Translate(Geom::Point(width/2.0, height/2.0)); - p1 = Geom::Point(0,height/2); - p2 = Geom::Point(width - (width/M_PI), height/M_PI); - p3 = Geom::Point(width - (width/M_PI), height-(height/M_PI)); - p1 *= m; - p2 *= m; - p3 *= m; - p1 = p1.floor(); - p2 = p2.floor(); - p3 = p3.floor(); - } + switch (ctrl->shape) { case SP_CTRL_SHAPE_SQUARE: - p = ctrl->cache; - // top edge - for (x=0; x < width; x++) { - *p++ = stroke_color; - } - // middle - for (y = 2; y < height; y++) { - *p++ = stroke_color; // stroke at first and last pixel - for (x=2; x < width; x++) { - *p++ = fill_color; // fill in the middle + { + guint32* p = ctrl->cache; + + for (int i = 0; i < width; ++i) { + for (int j = 0; j < height; ++j) { + if ( i > device_scale - 1 && + j > device_scale - 1 && + width - i > device_scale && + height -j > device_scale) { + *p++ = fill_color; + } else { + *p++ = stroke_color; + } } - *p++ = stroke_color; - } - // bottom edge - for (x=0; x < width; x++) { - *p++ = stroke_color; } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_DIAMOND: - p = ctrl->cache; - for (y = 0; y < height; y++) { - z = abs (c - y); - for (x = 0; x < z; x++) { - *p++ = 0; - } - *p++ = stroke_color; x++; - for (; x < width - z -1; x++) { - *p++ = fill_color; - } - if (z != c) { - *p++ = stroke_color; x++; - } - for (; x < width; x++) { - *p++ = 0; + { + // width == height + guint32* p = ctrl->cache; + int m = (width+1)/2; + + for (int i = 0; i < width; ++i) { + for (int j = 0; j < height; ++j) { + if ( i + j > m-1+device_scale && + (width-1-i) + j > m-1+device_scale && + (width-1-i) + (height-1-j) > m-1+device_scale && + i + (height-1-j) > m-1+device_scale ) { + *p++ = fill_color; + } else + if ( i + j > m-2 && + (width-1-i) + j > m-2 && + (width-1-i) + (height-1-j) > m-2 && + i + (height-1-j) > m-2 ) { + *p++ = stroke_color; + } else { + *p++ = 0; + } } } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_CIRCLE: - 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 < width) { - *p++ = 0; - *q-- = 0; - x++; + { + guint32* p = ctrl->cache; + + double rs = width/2.0; + double rs2 = rs*rs; + double rf = rs-device_scale; + double rf2 = rf*rf; + + for (int i = 0; i < width; ++i) { + for (int j = 0; j < height; ++j) { + + double rx = i - (width /2.0) + 0.5; + double ry = j - (height/2.0) + 0.5; + double r2 = rx*rx + ry*ry; + + if (r2 < rf2) { + *p++ = fill_color; + } else if (r2 < rs2) { + *p++ = stroke_color; + } else { + *p++ = 0; + } } - s = z; } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_TRIANGLE: - p = ctrl->cache; - for(y = 0; y < height; y++) { - for(x = 0; x < width; x++) { - point = Geom::Point(x,y); - if (sp_point_inside_triangle(p1, p2, p3, point)) { + { + guint* p = ctrl->cache; + + Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0)); + m *= Geom::Rotate(-ctrl->angle); + m *= Geom::Translate(Geom::Point(width/2.0, height/2.0)); + + // Construct an arrowhead (triangle) of maximum size that won't leak out of rectangle + // defined by width and height, assuming width == height. + double w2 = width/2.0; + double h2 = height/2.0; + double w2cos = w2 * cos( M_PI/6 ); + double h2sin = h2 * sin( M_PI/6 ); + Geom::Point p1s(0, h2); + Geom::Point p2s(w2 + w2cos, h2 + h2sin); + Geom::Point p3s(w2 + w2cos, h2 - h2sin); + // Needed for constructing smaller arrowhead below. + double theta = atan2( Geom::Point( p2s - p1s ) ); + p1s *= m; + p2s *= m; + p3s *= m; + + // Construct a smaller arrow head for fill. + Geom::Point p1f(device_scale/sin(theta), h2); + Geom::Point p2f(w2 + w2cos, h2 - h2sin + device_scale/cos(theta)); + Geom::Point p3f(w2 + w2cos, h2 + h2sin - device_scale/cos(theta)); + p1f *= m; + p2f *= m; + p3f *= m; + + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + Geom::Point point = Geom::Point(x+0.5,y+0.5); + if (sp_point_inside_triangle(p1f, p2f, p3f, point)) { p[(y*width)+x] = fill_color; - } else if (point == p1 || point == p2 || point == p3 || sp_point_inside_line(p1, p2, point, 0.2) || - sp_point_inside_line(p3, p1, point, 0.2)) - { + } else + if (sp_point_inside_triangle(p1s, p2s, p3s, point)) { p[(y*width)+x] = stroke_color; - } else if (sp_point_inside_line(p1, p2, point, 0.5) || - sp_point_inside_line(p3, p1, point, 0.5)) - { - p[(y*width)+x] = stroke_color_smooth; } else { p[(y*width)+x] = 0; } } } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_CROSS: - p = ctrl->cache; - for (y = 0; y < height; 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 < width; x++) { - *p++ = 0; + { + guint* p = ctrl->cache; + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + if ( abs(x - y) < device_scale || + abs(width - 1 - x - y) < device_scale ) { + *p++ = stroke_color; + } else { + *p++ = 0; + } } } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_BITMAP: + { if (ctrl->pixbuf) { - unsigned char *px; - unsigned int rs; - px = gdk_pixbuf_get_pixels (ctrl->pixbuf); - rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); - for (y = 0; y < height; y++){ - guint32 *d; - unsigned char *s; - s = px + y * rs; - d = ctrl->cache + height * y; - for (x = 0; x < width; x++) { + unsigned char* px = gdk_pixbuf_get_pixels (ctrl->pixbuf); + unsigned int rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); + for (int y = 0; y < height/device_scale; y++){ + for (int x = 0; x < width/device_scale; x++) { + unsigned char *s = px + rs*y + 4*x; + guint32 color; if (s[3] < 0x80) { - *d++ = 0; + color = 0; } else if (s[0] < 0x80) { - *d++ = stroke_color; + color = stroke_color; } else { - *d++ = fill_color; + color = fill_color; + } + + // Fill in device_scale x device_scale block + for (int i = 0; i < device_scale; ++i) { + for (int j = 0; j < device_scale; ++j) { + guint* p = ctrl->cache + + (x * device_scale + i) + // Column + (y * device_scale + j) * width; // Row + *p = color; + } } - s += 4; } } } else { - g_print ("control has no pixmap\n"); + g_print ("control has no pixbuf\n"); } + ctrl->build = TRUE; break; + } case SP_CTRL_SHAPE_IMAGE: + { if (ctrl->pixbuf) { - guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf); - guint32 *px; + guint rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf); - p = ctrl->cache; - for (y = 0; y < height; y++){ - px = reinterpret_cast<guint32*>(data + y * r); - for (x = 0; x < width; x++) { - *p++ = *px++; + + for (int y = 0; y < height/device_scale; y++){ + for (int x = 0; x < width/device_scale; x++) { + guint32 *px = reinterpret_cast<guint32*>(data + rs*y + 4*x); + + // Fill in device_scale x device_scale block + for (int i = 0; i < device_scale; ++i) { + for (int j = 0; j < device_scale; ++j) { + guint* p = ctrl->cache + + (x * device_scale + i) + // Column + (y * device_scale + j) * width; // Row + *p = *px; + } + } } } } else { - g_print ("control has no pixmap\n"); + g_print ("control has no pixbuf\n"); } ctrl->build = TRUE; break; + } default: break; @@ -556,80 +578,73 @@ static inline guint32 compose_xor(guint32 bg, guint32 fg, guint32 a) static void sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf) { - //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; + // Find device scale of source surface. + double x_scale = 0; + double y_scale = 0; + cairo_surface_get_device_scale(cairo_get_target(buf->ct), &x_scale, &y_scale); + int device_scale = x_scale; + // the control-image is rendered into ctrl->cache if (!ctrl->build) { - sp_ctrl_build_cache (ctrl); + sp_ctrl_build_cache (ctrl, device_scale); } - int w = (ctrl->width * 2 + 1); - int h = (ctrl->height * 2 + 1); + // Must match width/height sp_ctrl_build_cache. + int w = (ctrl->width * 2 + 1) * device_scale; + int h = (ctrl->height * 2 + 1) * device_scale; // 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 - // Find device scale of source surface - double x_scale = 0; - double y_scale = 0; - cairo_surface_get_device_scale(cairo_get_target(buf->ct), &x_scale, &y_scale); - // Size in device pixels. Does not set device scale. - cairo_surface_t *work = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w * x_scale, h * y_scale); - cairo_surface_set_device_scale(work, x_scale, y_scale); + cairo_surface_t *work = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cairo_surface_set_device_scale(work, device_scale, device_scale); cairo_t *cr = cairo_create(work); cairo_translate(cr, -ctrl->box.left(), -ctrl->box.top()); cairo_set_source_surface(cr, cairo_get_target(buf->ct), buf->rect.left(), buf->rect.top()); cairo_paint(cr); cairo_destroy(cr); + cairo_surface_write_to_png( work, "ctrl0.png" ); // 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); - int device_scale = x_scale; guint32 *p = ctrl->cache; for (int i=0; i<h; ++i) { - guint32 *pb = reinterpret_cast<guint32*>(pxb + i*strideb*device_scale); + 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); + *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; - } - if (device_scale == 2) { - *(pb+1) = *pb; - *(pb+strideb/4) = *pb; - *(pb+strideb/4+1) = *pb; - pb++; + *pb++ = px; } - pb++; } } cairo_surface_mark_dirty(work); + cairo_surface_write_to_png( work, "ctrl1.png" ); // 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.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top()); - cairo_rectangle(buf->ct, ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top(), w, h); + cairo_rectangle(buf->ct, ctrl->box.left() - buf->rect.left(), ctrl->box.top() - buf->rect.top(), w/device_scale, h/device_scale); cairo_clip(buf->ct); cairo_set_operator(buf->ct, CAIRO_OPERATOR_SOURCE); cairo_paint(buf->ct); |
