diff options
author | Alexander Larsson <alexl@redhat.com> | 2010-02-09 17:08:18 +0100 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2010-02-23 22:52:05 +0100 |
commit | 864881c318638677546ce22c0874f9da715008a2 (patch) | |
tree | 13a34ff2421377f7dbf1aab667b9546268c9daaf /common | |
parent | a7ceb98ea1688f0de1613ee12bb78d246869242c (diff) | |
download | spice-864881c318638677546ce22c0874f9da715008a2.tar.gz spice-864881c318638677546ce22c0874f9da715008a2.tar.xz spice-864881c318638677546ce22c0874f9da715008a2.zip |
Convert cairo canvas draw_fill() to using pixman
Diffstat (limited to 'common')
-rw-r--r-- | common/cairo_canvas.c | 484 | ||||
-rw-r--r-- | common/canvas_base.c | 38 |
2 files changed, 503 insertions, 19 deletions
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c index 433f3c94..6b255057 100644 --- a/common/cairo_canvas.c +++ b/common/cairo_canvas.c @@ -30,8 +30,306 @@ struct CairoCanvas { uint32_t *private_data; int private_data_size; pixman_image_t *image; + pixman_region32_t canvas_region; }; +typedef enum { + ROP_INPUT_SRC, + ROP_INPUT_BRUSH, + ROP_INPUT_DEST +} ROPInput; + +SpiceROP ropd_descriptor_to_rop(int desc, + ROPInput src_input, + ROPInput dest_input) +{ + int old; + int invert_masks[] = { + SPICE_ROPD_INVERS_SRC, + SPICE_ROPD_INVERS_BRUSH, + SPICE_ROPD_INVERS_DEST + }; + + old = desc; + + desc &= ~(SPICE_ROPD_INVERS_SRC | SPICE_ROPD_INVERS_DEST); + if (old & invert_masks[src_input]) { + desc |= SPICE_ROPD_INVERS_SRC; + } + + if (old & invert_masks[dest_input]) { + desc |= SPICE_ROPD_INVERS_DEST; + } + + if (desc & SPICE_ROPD_OP_PUT) { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_RES) { + return SPICE_ROP_COPY; + } + return SPICE_ROP_COPY_INVERTED; + } else { + if (desc & SPICE_ROPD_INVERS_RES) { + return SPICE_ROP_COPY_INVERTED; + } + return SPICE_ROP_COPY; + } + } else if (desc & SPICE_ROPD_OP_OR) { + + if (desc & SPICE_ROPD_INVERS_RES) { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(!src or !dest) == src and dest*/ + return SPICE_ROP_AND; + } else { + /* ! (!src or dest) = src and !dest*/ + return SPICE_ROP_AND_REVERSE; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(src or !dest) == !src and dest */ + return SPICE_ROP_AND_INVERTED; + } else { + /* !(src or dest) */ + return SPICE_ROP_NOR; + } + } + } else { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !src or !dest == !(src and dest)*/ + return SPICE_ROP_NAND; + } else { + /* !src or dest */ + return SPICE_ROP_OR_INVERTED; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* src or !dest */ + return SPICE_ROP_OR_REVERSE; + } else { + /* src or dest */ + return SPICE_ROP_OR; + } + } + } + + } else if (desc & SPICE_ROPD_OP_AND) { + + if (desc & SPICE_ROPD_INVERS_RES) { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(!src and !dest) == src or dest*/ + return SPICE_ROP_OR; + } else { + /* ! (!src and dest) = src or !dest*/ + return SPICE_ROP_OR_REVERSE; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(src and !dest) == !src or dest */ + return SPICE_ROP_OR_INVERTED; + } else { + /* !(src and dest) */ + return SPICE_ROP_NAND; + } + } + } else { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !src and !dest == !(src or dest)*/ + return SPICE_ROP_NOR; + } else { + /* !src and dest */ + return SPICE_ROP_AND_INVERTED; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* src and !dest */ + return SPICE_ROP_AND_REVERSE; + } else { + /* src and dest */ + return SPICE_ROP_AND; + } + } + } + + } else if (desc & SPICE_ROPD_OP_XOR) { + + if (desc & SPICE_ROPD_INVERS_RES) { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(!src xor !dest) == !src xor dest */ + return SPICE_ROP_EQUIV; + } else { + /* ! (!src xor dest) = src xor dest*/ + return SPICE_ROP_XOR; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !(src xor !dest) == src xor dest */ + return SPICE_ROP_XOR; + } else { + /* !(src xor dest) */ + return SPICE_ROP_EQUIV; + } + } + } else { + if (desc & SPICE_ROPD_INVERS_SRC) { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* !src xor !dest == src xor dest */ + return SPICE_ROP_XOR; + } else { + /* !src xor dest */ + return SPICE_ROP_EQUIV; + } + } else { + if (desc & SPICE_ROPD_INVERS_DEST) { + /* src xor !dest */ + return SPICE_ROP_EQUIV; + } else { + /* src xor dest */ + return SPICE_ROP_XOR; + } + } + } + + } else if (desc & SPICE_ROPD_OP_BLACKNESS) { + return SPICE_ROP_CLEAR; + } else if (desc & SPICE_ROPD_OP_WHITENESS) { + return SPICE_ROP_SET; + } else if (desc & SPICE_ROPD_OP_INVERS) { + return SPICE_ROP_INVERT; + } + return SPICE_ROP_COPY; +} + +static void canvas_clip_pixman(CairoCanvas *canvas, + pixman_region32_t *dest_region, + SpiceClip *clip) +{ + pixman_region32_intersect(dest_region, dest_region, &canvas->canvas_region); + + switch (clip->type) { + case SPICE_CLIP_TYPE_NONE: + break; + case SPICE_CLIP_TYPE_RECTS: { + uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data); + access_test(&canvas->base, n, sizeof(uint32_t)); + + SpiceRect *now = (SpiceRect *)(n + 1); + access_test(&canvas->base, now, (unsigned long)(now + *n) - (unsigned long)now); + + pixman_region32_t clip; + + if (spice_pixman_region32_init_rects(&clip, now, *n)) { + pixman_region32_intersect(dest_region, dest_region, &clip); + pixman_region32_fini(&clip); + } + + break; + } + case SPICE_CLIP_TYPE_PATH: + CANVAS_ERROR("clip paths not supported anymore"); + break; + default: + CANVAS_ERROR("invalid clip type"); + } +} + +static void canvas_mask_pixman(CairoCanvas *canvas, + pixman_region32_t *dest_region, + SpiceQMask *mask, int x, int y) +{ + pixman_image_t *image, *subimage; + int needs_invert; + pixman_region32_t mask_region; + uint32_t *mask_data; + int mask_x, mask_y; + int mask_width, mask_height, mask_stride; + pixman_box32_t extents; + + needs_invert = FALSE; + image = canvas_get_mask(&canvas->base, + mask, + &needs_invert); + + if (image == NULL) { + return; /* no mask */ + } + + mask_data = pixman_image_get_data(image); + mask_width = pixman_image_get_width(image); + mask_height = pixman_image_get_height(image); + mask_stride = pixman_image_get_stride(image); + + mask_x = mask->pos.x; + mask_y = mask->pos.y; + + /* We need to subset the area of the mask that we turn into a region, + because a cached mask may be much larger than what is used for + the clip operation. */ + extents = *pixman_region32_extents(dest_region); + + /* convert from destination pixels to mask pixels */ + extents.x1 -= x - mask_x; + extents.y1 -= y - mask_y; + extents.x2 -= x - mask_x; + extents.y2 -= y - mask_y; + + /* clip to mask size */ + if (extents.x1 < 0) { + extents.x1 = 0; + } + if (extents.x2 >= mask_width) { + extents.x2 = mask_width; + } + if (extents.x2 < extents.x1) { + extents.x2 = extents.x1; + } + if (extents.y1 < 0) { + extents.y1 = 0; + } + if (extents.y2 >= mask_height) { + extents.y2 = mask_height; + } + if (extents.y2 < extents.y1) { + extents.y2 = extents.y1; + } + + /* round down X to even 32 pixels (i.e. uint32_t) */ + extents.x1 = extents.x1 & ~(0x1f); + + mask_data = (uint32_t *)((uint8_t *)mask_data + mask_stride * extents.y1 + extents.x1 / 32); + mask_x -= extents.x1; + mask_y -= extents.y1; + mask_width = extents.x2 - extents.x1; + mask_height = extents.y2 - extents.y1; + + subimage = pixman_image_create_bits(PIXMAN_a1, mask_width, mask_height, + mask_data, mask_stride); + pixman_region32_init_from_image(&mask_region, + subimage); + pixman_image_unref(subimage); + + if (needs_invert) { + pixman_box32_t rect; + + rect.x1 = rect.y1 = 0; + rect.x2 = mask_width; + rect.y2 = mask_height; + + pixman_region32_inverse(&mask_region, &mask_region, &rect); + } + + pixman_region32_translate(&mask_region, + -mask_x + x, -mask_y + y); + + pixman_region32_intersect(dest_region, dest_region, &mask_region); + pixman_region32_fini(&mask_region); + + pixman_image_unref(image); +} + static void canvas_set_path(CairoCanvas *canvas, void *addr) { cairo_t *cairo = canvas->cairo; @@ -701,27 +999,175 @@ static void __draw_mask(void *data) cairo_mask(((DrawMaskData *)data)->cairo, ((DrawMaskData *)data)->mask); } +static void fill_solid_rects(CairoCanvas *canvas, + pixman_region32_t *region, + uint32_t color) +{ + pixman_box32_t *rects; + int n_rects; + int i; + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + spice_pixman_fill_rect(canvas->image, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + color); + } +} + +static void fill_solid_rects_rop(CairoCanvas *canvas, + pixman_region32_t *region, + uint32_t color, + SpiceROP rop) +{ + pixman_box32_t *rects; + int n_rects; + int i; + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + spice_pixman_fill_rect_rop(canvas->image, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + color, rop); + } +} + +static void fill_tiled_rects(CairoCanvas *canvas, + pixman_region32_t *region, + SpicePattern *pattern) +{ + pixman_image_t *tile; + int offset_x, offset_y; + pixman_box32_t *rects; + int n_rects; + int i; + + rects = pixman_region32_rectangles(region, &n_rects); + + tile = canvas_get_image(&canvas->base, pattern->pat); + offset_x = pattern->pos.x; + offset_y = pattern->pos.y; + + for (i = 0; i < n_rects; i++) { + spice_pixman_tile_rect(canvas->image, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + tile, offset_x, offset_y); + } + + pixman_image_unref(tile); +} + +static void fill_tiled_rects_rop(CairoCanvas *canvas, + pixman_region32_t *region, + SpicePattern *pattern, + SpiceROP rop) +{ + pixman_image_t *tile; + int offset_x, offset_y; + pixman_box32_t *rects; + int n_rects; + int i; + + rects = pixman_region32_rectangles(region, &n_rects); + + tile = canvas_get_image(&canvas->base, pattern->pat); + offset_x = pattern->pos.x; + offset_y = pattern->pos.y; + + for (i = 0; i < n_rects; i++) { + spice_pixman_tile_rect_rop(canvas->image, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + tile, offset_x, offset_y, + rop); + } + + pixman_image_unref(tile); +} + +static void draw_brush(CairoCanvas *canvas, + pixman_region32_t *region, + SpiceBrush *brush, + SpiceROP rop) +{ + uint32_t color; + SpicePattern *pattern; + + switch (brush->type) { + case SPICE_BRUSH_TYPE_SOLID: + color = brush->u.color; + if (rop == SPICE_ROP_COPY) { + fill_solid_rects(canvas, region, color); + } else { + fill_solid_rects_rop(canvas, region, color, rop); + } + break; + case SPICE_BRUSH_TYPE_PATTERN: + pattern = &brush->u.pattern; + + if (rop == SPICE_ROP_COPY) { + fill_tiled_rects(canvas, region, pattern); + } else { + fill_tiled_rects_rop(canvas, region, pattern, rop); + } + break; + case SPICE_BRUSH_TYPE_NONE: + /* Still need to do *something* here, because rop could be e.g invert dest */ + fill_solid_rects_rop(canvas, region, 0, rop); + break; + default: + CANVAS_ERROR("invalid brush type"); + } +} + +/* If we're exiting early we may still have to load an image in case + it has to be cached or something */ +static void touch_brush(CairoCanvas *canvas, SpiceBrush *brush) +{ + SpicePattern *pattern; + if (brush->type == SPICE_BRUSH_TYPE_PATTERN) { + pattern = &brush->u.pattern; + canvas_touch_image(&canvas->base, pattern->pat); + } +} + void canvas_draw_fill(CairoCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill) { - DrawMaskData draw_data; - draw_data.cairo = canvas->cairo; + pixman_region32_t dest_region; + SpiceROP rop; - cairo_save(draw_data.cairo); - canvas_clip(canvas, clip); - if ((draw_data.mask = canvas_get_mask_pattern(canvas, &fill->mask, bbox->left, bbox->top))) { - cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left, - bbox->bottom - bbox->top); - cairo_clip(draw_data.cairo); - canvas_draw(canvas, &fill->brush, fill->rop_decriptor, __draw_mask, &draw_data); - cairo_pattern_destroy(draw_data.mask); - } else { - cairo_rectangle(draw_data.cairo, bbox->left, bbox->top, bbox->right - bbox->left, - bbox->bottom - bbox->top); - canvas_draw(canvas, &fill->brush, fill->rop_decriptor, (DrawMethod)cairo_fill_preserve, - draw_data.cairo); - cairo_new_path(draw_data.cairo); + pixman_region32_init_rect(&dest_region, + bbox->left, bbox->top, + bbox->right - bbox->left, + bbox->bottom - bbox->top); + + + canvas_clip_pixman(canvas, &dest_region, clip); + canvas_mask_pixman(canvas, &dest_region, &fill->mask, + bbox->left, bbox->top); + + rop = ropd_descriptor_to_rop(fill->rop_decriptor, + ROP_INPUT_BRUSH, + ROP_INPUT_DEST); + + if (rop == SPICE_ROP_NOOP || pixman_region32_n_rects(&dest_region) == 0) { + touch_brush(canvas, &fill->brush); + pixman_region32_fini(&dest_region); + return; } - cairo_restore(draw_data.cairo); + + draw_brush(canvas, &dest_region, &fill->brush, rop); + + pixman_region32_fini(&dest_region); } static cairo_pattern_t *canvas_src_image_to_pat(CairoCanvas *canvas, SPICE_ADDRESS src_bitmap, @@ -1664,6 +2110,10 @@ CairoCanvas *canvas_create(cairo_t *cairo, int bits cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); canvas->image = pixman_image_from_surface (cairo_get_target (cairo)); + pixman_region32_init_rect(&canvas->canvas_region, + 0, 0, + pixman_image_get_width (canvas->image), + pixman_image_get_height (canvas->image)); return canvas; } diff --git a/common/canvas_base.c b/common/canvas_base.c index 6659385a..a96bd428 100644 --- a/common/canvas_base.c +++ b/common/canvas_base.c @@ -841,7 +841,9 @@ static void dump_surface(pixman_image_t *surface, int cache) //#define DEBUG_LZ -static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr) +/* If real get is FALSE, then only do whatever is needed but don't return an image. For instance, + if we need to read it to cache it we do. */ +static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRESS addr, int real_get) { SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr); pixman_image_t *surface; @@ -850,6 +852,15 @@ static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr) LOG_DEBUG("canvas_get_image image type: " << (int)descriptor->type); #endif + /* When touching, only really allocate if we need to cache, or + * if we're loading a GLZ stream (since those need inter-thread communication + * to happen which breaks if we don't. */ + if (!real_get && + !(descriptor->flags & SPICE_IMAGE_FLAGS_CACHE_ME) && + (descriptor->type != SPICE_IMAGE_TYPE_GLZ_RGB)) { + return NULL; + } + switch (descriptor->type) { case SPICE_IMAGE_TYPE_QUIC: { SpiceQUICImage *image = (SpiceQUICImage *)descriptor; @@ -901,17 +912,28 @@ static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr) dump_surface(surface, 0); #endif } + + if (!real_get) { + pixman_image_unref(surface); + return NULL; + } + return surface; } #else -static pixman_image_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr) +static pixman_image_t *canvas_get_image_internal(CairoCanvas *canvas, SPICE_ADDRESS addr, int real_get) { SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr); access_test(canvas, descriptor, sizeof(SpiceImageDescriptor)); + /* When touching, never load image. */ + if (!real_get) { + return NULL; + } + switch (descriptor->type) { case SPICE_IMAGE_TYPE_QUIC: { SpiceQUICImage *image = (SpiceQUICImage *)descriptor; @@ -930,6 +952,18 @@ static pixman_image_t *canvas_get_image(CairoCanvas *canvas, SPICE_ADDRESS addr) #endif +static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr) +{ + return canvas_get_image_internal(canvas, addr, TRUE); +} + +#ifdef CANVAS_USE_PIXMAN +static void canvas_touch_image(CanvasBase *canvas, SPICE_ADDRESS addr) +{ + canvas_get_image_internal(canvas, addr, FALSE); +} +#endif + static inline uint8_t revers_bits(uint8_t byte) { uint8_t ret = 0; |