diff options
Diffstat (limited to 'common/sw_canvas.c')
-rw-r--r-- | common/sw_canvas.c | 1332 |
1 files changed, 1332 insertions, 0 deletions
diff --git a/common/sw_canvas.c b/common/sw_canvas.c new file mode 100644 index 00000000..a541c7df --- /dev/null +++ b/common/sw_canvas.c @@ -0,0 +1,1332 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2009 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <math.h> +#include "sw_canvas.h" +#define CANVAS_USE_PIXMAN +#define CANVAS_SINGLE_INSTANCE +#include "canvas_base.c" +#include "rect.h" +#include "region.h" +#include "pixman_utils.h" + +typedef struct SwCanvas SwCanvas; + +struct SwCanvas { + CanvasBase base; + uint32_t *private_data; + int private_data_size; + pixman_image_t *image; +}; + +static pixman_image_t *canvas_get_pixman_brush(SwCanvas *canvas, + SpiceBrush *brush) +{ + switch (brush->type) { + case SPICE_BRUSH_TYPE_SOLID: { + uint32_t color = brush->u.color; + pixman_color_t c; + + c.blue = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask; + color >>= canvas->base.color_shift; + c.green = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask; + color >>= canvas->base.color_shift; + c.red = ((color & canvas->base.color_mask) * 0xffff) / canvas->base.color_mask; + c.alpha = 0xffff; + + return pixman_image_create_solid_fill(&c); + } + case SPICE_BRUSH_TYPE_PATTERN: { + SwCanvas *surface_canvas; + pixman_image_t* surface; + pixman_transform_t t; + + surface_canvas = (SwCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat); + if (surface_canvas) { + surface = surface_canvas->image; + surface = pixman_image_ref(surface); + } else { + surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE); + } + pixman_transform_init_translate(&t, + pixman_int_to_fixed(-brush->u.pattern.pos.x), + pixman_int_to_fixed(-brush->u.pattern.pos.y)); + pixman_image_set_transform(surface, &t); + pixman_image_set_repeat(surface, PIXMAN_REPEAT_NORMAL); + return surface; + } + case SPICE_BRUSH_TYPE_NONE: + return NULL; + default: + CANVAS_ERROR("invalid brush type"); + } +} + +static pixman_image_t *get_image(SpiceCanvas *canvas) +{ + SwCanvas *sw_canvas = (SwCanvas *)canvas; + + pixman_image_ref(sw_canvas->image); + + return sw_canvas->image; +} + +static void copy_region(SpiceCanvas *spice_canvas, + pixman_region32_t *dest_region, + int dx, int dy) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_box32_t *dest_rects; + int n_rects; + int i, j, end_line; + + dest_rects = pixman_region32_rectangles(dest_region, &n_rects); + + if (dy > 0) { + if (dx >= 0) { + /* south-east: copy x and y in reverse order */ + for (i = n_rects - 1; i >= 0; i--) { + spice_pixman_copy_rect(canvas->image, + dest_rects[i].x1 - dx, dest_rects[i].y1 - dy, + dest_rects[i].x2 - dest_rects[i].x1, + dest_rects[i].y2 - dest_rects[i].y1, + dest_rects[i].x1, dest_rects[i].y1); + } + } else { + /* south-west: Copy y in reverse order, but x in forward order */ + i = n_rects - 1; + + while (i >= 0) { + /* Copy all rects with same y in forward order */ + for (end_line = i - 1; + end_line >= 0 && dest_rects[end_line].y1 == dest_rects[i].y1; + end_line--) { + } + for (j = end_line + 1; j <= i; j++) { + spice_pixman_copy_rect(canvas->image, + dest_rects[j].x1 - dx, dest_rects[j].y1 - dy, + dest_rects[j].x2 - dest_rects[j].x1, + dest_rects[j].y2 - dest_rects[j].y1, + dest_rects[j].x1, dest_rects[j].y1); + } + i = end_line; + } + } + } else { + if (dx > 0) { + /* north-east: copy y in forward order, but x in reverse order */ + i = 0; + + while (i < n_rects) { + /* Copy all rects with same y in reverse order */ + for (end_line = i; + end_line < n_rects && dest_rects[end_line].y1 == dest_rects[i].y1; + end_line++) { + } + for (j = end_line - 1; j >= i; j--) { + spice_pixman_copy_rect(canvas->image, + dest_rects[j].x1 - dx, dest_rects[j].y1 - dy, + dest_rects[j].x2 - dest_rects[j].x1, + dest_rects[j].y2 - dest_rects[j].y1, + dest_rects[j].x1, dest_rects[j].y1); + } + i = end_line; + } + } else { + /* north-west: Copy x and y in forward order */ + for (i = 0; i < n_rects; i++) { + spice_pixman_copy_rect(canvas->image, + dest_rects[i].x1 - dx, dest_rects[i].y1 - dy, + dest_rects[i].x2 - dest_rects[i].x1, + dest_rects[i].y2 - dest_rects[i].y1, + dest_rects[i].x1, dest_rects[i].y1); + } + } + } +} + +static void fill_solid_spans(SpiceCanvas *spice_canvas, + SpicePoint *points, + int *widths, + int n_spans, + uint32_t color) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + int i; + + for (i = 0; i < n_spans; i++) { + spice_pixman_fill_rect(canvas->image, + points[i].x, points[i].y, + widths[i], + 1, + color); + } +} + +static void fill_solid_rects(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + uint32_t color) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + int i; + + 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(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + uint32_t color, + SpiceROP rop) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + int i; + + 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(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + pixman_image_t *tile, + int offset_x, int offset_y) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + int i; + + 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); + } +} + +static void fill_tiled_rects(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + pixman_image_t *tile, + int offset_x, int offset_y) +{ + __fill_tiled_rects(spice_canvas, rects, n_rects, tile, offset_x, offset_y); +} + +static void fill_tiled_rects_from_surface(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + SpiceCanvas *surface_canvas, + int offset_x, int offset_y) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __fill_tiled_rects(spice_canvas, rects, n_rects, sw_surface_canvas->image, offset_x, + offset_y); +} + +static void __fill_tiled_rects_rop(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + pixman_image_t *tile, + int offset_x, int offset_y, + SpiceROP rop) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + int i; + + 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); + } +} +static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + pixman_image_t *tile, + int offset_x, int offset_y, + SpiceROP rop) +{ + __fill_tiled_rects_rop(spice_canvas, rects, n_rects, tile, offset_x, offset_y, rop); +} + +static void fill_tiled_rects_rop_from_surface(SpiceCanvas *spice_canvas, + pixman_box32_t *rects, + int n_rects, + SpiceCanvas *surface_canvas, + int offset_x, int offset_y, + SpiceROP rop) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __fill_tiled_rects_rop(spice_canvas, rects, n_rects, sw_surface_canvas->image, offset_x, + offset_y, rop); +} + +/* Some pixman implementations of OP_OVER on xRGB32 sets + the high bit to 0xff (which is the right value if the + destination was ARGB32, and it should be ignored for + xRGB32. However, this fills our alpha bits with + data that is not wanted or expected by windows, and its + causing us to send rgba images rather than rgb images to + the client. So, we manually clear these bytes. */ +static void clear_dest_alpha(pixman_image_t *dest, + int x, int y, + int width, int height) +{ + uint32_t *data; + int stride; + int w, h; + + w = pixman_image_get_width(dest); + h = pixman_image_get_height(dest); + + if (x + width <= 0 || x >= w || + y + height <= 0 || y >= h || + width == 0 || height == 0) { + return; + } + + if (x < 0) { + width += x; + x = 0; + } + if (x + width > w) { + width = w - x; + } + + if (y < 0) { + height += y; + y = 0; + } + if (y + height > h) { + height = h - y; + } + + stride = pixman_image_get_stride(dest); + data = (uint32_t *) ( + (uint8_t *)pixman_image_get_data(dest) + y * stride + 4 * x); + + if ((*data & 0xff000000U) == 0xff000000U) { + spice_pixman_fill_rect_rop(dest, + x, y, width, height, + 0x00ffffff, SPICE_ROP_AND); + } +} + +static void __blit_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_box32_t *rects; + int n_rects, i; + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + int src_x, src_y, dest_x, dest_y, width, height; + + dest_x = rects[i].x1; + dest_y = rects[i].y1; + width = rects[i].x2 - rects[i].x1; + height = rects[i].y2 - rects[i].y1; + + src_x = rects[i].x1 - offset_x; + src_y = rects[i].y1 - offset_y; + + spice_pixman_blit(canvas->image, + src_image, + src_x, src_y, + dest_x, dest_y, + width, height); + } +} + +static void blit_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y) +{ + __blit_image(spice_canvas, region, src_image, offset_x, offset_y); +} + +static void blit_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int offset_x, int offset_y) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __blit_image(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y); +} + +static void __blit_image_rop(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y, + SpiceROP rop) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_box32_t *rects; + int n_rects, i; + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + int src_x, src_y, dest_x, dest_y, width, height; + + dest_x = rects[i].x1; + dest_y = rects[i].y1; + width = rects[i].x2 - rects[i].x1; + height = rects[i].y2 - rects[i].y1; + + src_x = rects[i].x1 - offset_x; + src_y = rects[i].y1 - offset_y; + + spice_pixman_blit_rop(canvas->image, + src_image, + src_x, src_y, + dest_x, dest_y, + width, height, rop); + } +} + +static void blit_image_rop(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y, + SpiceROP rop) +{ + __blit_image_rop(spice_canvas, region, src_image, offset_x, offset_y, rop); +} + +static void blit_image_rop_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int offset_x, int offset_y, + SpiceROP rop) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __blit_image_rop(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y, rop); +} + + + +static void __scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_transform_t transform; + double sx, sy; + + sx = (double)(src_width) / (dest_width); + sy = (double)(src_height) / (dest_height); + + pixman_image_set_clip_region32(canvas->image, region); + + pixman_transform_init_scale(&transform, + pixman_double_to_fixed(sx), + pixman_double_to_fixed(sy)); + + pixman_image_set_transform(src, &transform); + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || + scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST); + pixman_image_set_filter(src, + (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ? + PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD, + NULL, 0); + + pixman_image_composite32(PIXMAN_OP_SRC, + src, NULL, canvas->image, + ROUND(src_x / sx), ROUND(src_y / sy), /* src */ + 0, 0, /* mask */ + dest_x, dest_y, /* dst */ + dest_width, dest_height); + + pixman_transform_init_identity(&transform); + pixman_image_set_transform(src, &transform); + + pixman_image_set_clip_region32(canvas->image, NULL); +} + +static void scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode) +{ + __scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, dest_y, + dest_width,dest_height,scale_mode); +} + +static void scale_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __scale_image(spice_canvas, region, sw_surface_canvas->image, src_x, src_y, src_width, + src_height, dest_x, dest_y, dest_width,dest_height,scale_mode); +} + +static void __scale_image_rop(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, SpiceROP rop) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_transform_t transform; + pixman_image_t *scaled; + pixman_box32_t *rects; + int n_rects, i; + double sx, sy; + + sx = (double)(src_width) / (dest_width); + sy = (double)(src_height) / (dest_height); + + scaled = pixman_image_create_bits(spice_pixman_image_get_format(src), + dest_width, + dest_height, + NULL, 0); + + pixman_region32_translate(region, -dest_x, -dest_y); + pixman_image_set_clip_region32(scaled, region); + + pixman_transform_init_scale(&transform, + pixman_double_to_fixed(sx), + pixman_double_to_fixed(sy)); + + pixman_image_set_transform(src, &transform); + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || + scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST); + pixman_image_set_filter(src, + (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ? + PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD, + NULL, 0); + + pixman_image_composite32(PIXMAN_OP_SRC, + src, NULL, scaled, + ROUND(src_x / sx), ROUND(src_y / sy), /* src */ + 0, 0, /* mask */ + 0, 0, /* dst */ + dest_width, + dest_height); + + pixman_transform_init_identity(&transform); + pixman_image_set_transform(src, &transform); + + /* Translate back */ + pixman_region32_translate(region, dest_x, dest_y); + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + spice_pixman_blit_rop(canvas->image, + scaled, + rects[i].x1 - dest_x, + rects[i].y1 - dest_y, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + rop); + } + + pixman_image_unref(scaled); +} + +static void scale_image_rop(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, SpiceROP rop) +{ + __scale_image_rop(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, + dest_y, dest_width, dest_height, scale_mode, rop); +} + +static void scale_image_rop_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, SpiceROP rop) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __scale_image_rop(spice_canvas, region, sw_surface_canvas->image, src_x, src_y, src_width, + src_height, dest_x, dest_y, dest_width, dest_height, scale_mode, rop); +} + +static pixman_image_t *canvas_get_as_surface(SwCanvas *canvas, + int with_alpha) +{ + pixman_image_t *target; + + if (with_alpha && + canvas->base.format == SPICE_SURFACE_FMT_32_xRGB) { + target = pixman_image_create_bits(PIXMAN_a8r8g8b8, + pixman_image_get_width(canvas->image), + pixman_image_get_height(canvas->image), + pixman_image_get_data(canvas->image), + pixman_image_get_stride(canvas->image)); + } else { + target = pixman_image_ref(canvas->image); + } + + return target; +} + +static void __blend_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + pixman_image_t *src, + int src_x, int src_y, + int dest_x, int dest_y, + int width, int height, + int overall_alpha) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_image_t *mask, *dest; + + dest = canvas_get_as_surface(canvas, dest_has_alpha); + + pixman_image_set_clip_region32(dest, region); + + mask = NULL; + if (overall_alpha != 0xff) { + pixman_color_t color = { 0 }; + color.alpha = overall_alpha * 0x101; + mask = pixman_image_create_solid_fill(&color); + } + + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + + pixman_image_composite32(PIXMAN_OP_OVER, + src, mask, dest, + src_x, src_y, /* src */ + 0, 0, /* mask */ + dest_x, dest_y, /* dst */ + width, + height); + + if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB && + !dest_has_alpha) { + clear_dest_alpha(dest, dest_x, dest_y, width, height); + } + + if (mask) { + pixman_image_unref(mask); + } + + pixman_image_set_clip_region32(dest, NULL); + pixman_image_unref(dest); +} + +static void blend_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + pixman_image_t *src, + int src_x, int src_y, + int dest_x, int dest_y, + int width, int height, + int overall_alpha) +{ + __blend_image(spice_canvas, region, dest_has_alpha, src, src_x, src_y, + dest_x, dest_y, width, height, + overall_alpha); +} + +static void blend_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + SpiceCanvas *surface_canvas, + int src_has_alpha, + int src_x, int src_y, + int dest_x, int dest_y, + int width, int height, + int overall_alpha) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + pixman_image_t *src; + + src = canvas_get_as_surface(sw_surface_canvas, src_has_alpha); + __blend_image(spice_canvas, region, dest_has_alpha, + src, src_x, src_y, + dest_x, dest_y, + width, height, overall_alpha); + pixman_image_unref(src); +} + +static void __blend_scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, + int overall_alpha) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_transform_t transform; + pixman_image_t *mask, *dest; + double sx, sy; + + sx = (double)(src_width) / (dest_width); + sy = (double)(src_height) / (dest_height); + + dest = canvas_get_as_surface(canvas, dest_has_alpha); + + pixman_image_set_clip_region32(dest, region); + + pixman_transform_init_scale(&transform, + pixman_double_to_fixed(sx), + pixman_double_to_fixed(sy)); + + mask = NULL; + if (overall_alpha != 0xff) { + pixman_color_t color = { 0 }; + color.alpha = overall_alpha * 0x101; + mask = pixman_image_create_solid_fill(&color); + } + + pixman_image_set_transform(src, &transform); + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE || + scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST); + pixman_image_set_filter(src, + (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ? + PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD, + NULL, 0); + + pixman_image_composite32(PIXMAN_OP_OVER, + src, mask, dest, + ROUND(src_x / sx), ROUND(src_y / sy), /* src */ + 0, 0, /* mask */ + dest_x, dest_y, /* dst */ + dest_width, dest_height); + + if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB && + !dest_has_alpha) { + clear_dest_alpha(dest, dest_x, dest_y, dest_width, dest_height); + } + + pixman_transform_init_identity(&transform); + pixman_image_set_transform(src, &transform); + + if (mask) { + pixman_image_unref(mask); + } + + pixman_image_set_clip_region32(dest, NULL); + pixman_image_unref(dest); +} + +static void blend_scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, + int overall_alpha) +{ + __blend_scale_image(spice_canvas, region, dest_has_alpha, + src, src_x, src_y, src_width, src_height, + dest_x, dest_y, dest_width, dest_height, + scale_mode, overall_alpha); +} + +static void blend_scale_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + int dest_has_alpha, + SpiceCanvas *surface_canvas, + int src_has_alpha, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + int scale_mode, + int overall_alpha) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + pixman_image_t *src; + + src = canvas_get_as_surface(sw_surface_canvas, src_has_alpha); + __blend_scale_image(spice_canvas, region, dest_has_alpha, src, src_x, src_y, src_width, + src_height, dest_x, dest_y, dest_width, dest_height, scale_mode, + overall_alpha); + pixman_image_unref(src); +} + +static void __colorkey_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y, + uint32_t transparent_color) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_box32_t *rects; + int n_rects, i; + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + int src_x, src_y, dest_x, dest_y, width, height; + + dest_x = rects[i].x1; + dest_y = rects[i].y1; + width = rects[i].x2 - rects[i].x1; + height = rects[i].y2 - rects[i].y1; + + src_x = rects[i].x1 - offset_x; + src_y = rects[i].y1 - offset_y; + + spice_pixman_blit_colorkey(canvas->image, + src_image, + src_x, src_y, + dest_x, dest_y, + width, height, + transparent_color); + } +} + +static void colorkey_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src_image, + int offset_x, int offset_y, + uint32_t transparent_color) +{ + __colorkey_image(spice_canvas, region, src_image, offset_x, offset_y, transparent_color); +} + +static void colorkey_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int offset_x, int offset_y, + uint32_t transparent_color) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __colorkey_image(spice_canvas, region, sw_surface_canvas->image, offset_x, offset_y, + transparent_color); +} + +static void __colorkey_scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + uint32_t transparent_color) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_transform_t transform; + pixman_image_t *scaled; + pixman_box32_t *rects; + int n_rects, i; + double sx, sy; + + sx = (double)(src_width) / (dest_width); + sy = (double)(src_height) / (dest_height); + + scaled = pixman_image_create_bits(spice_pixman_image_get_format (src), + dest_width, + dest_height, + NULL, 0); + + pixman_region32_translate(region, -dest_x, -dest_y); + pixman_image_set_clip_region32(scaled, region); + + pixman_transform_init_scale(&transform, + pixman_double_to_fixed(sx), + pixman_double_to_fixed(sy)); + + pixman_image_set_transform(src, &transform); + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + pixman_image_set_filter(src, + PIXMAN_FILTER_NEAREST, + NULL, 0); + + pixman_image_composite32(PIXMAN_OP_SRC, + src, NULL, scaled, + ROUND(src_x / sx), ROUND(src_y / sy), /* src */ + 0, 0, /* mask */ + 0, 0, /* dst */ + dest_width, + dest_height); + + pixman_transform_init_identity(&transform); + pixman_image_set_transform(src, &transform); + + /* Translate back */ + pixman_region32_translate(region, dest_x, dest_y); + + rects = pixman_region32_rectangles(region, &n_rects); + + for (i = 0; i < n_rects; i++) { + spice_pixman_blit_colorkey(canvas->image, + scaled, + rects[i].x1 - dest_x, + rects[i].y1 - dest_y, + rects[i].x1, rects[i].y1, + rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1, + transparent_color); + } + + pixman_image_unref(scaled); +} + +static void colorkey_scale_image(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + pixman_image_t *src, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + uint32_t transparent_color) +{ + __colorkey_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, + dest_y, dest_width, dest_height, transparent_color); +} + +static void colorkey_scale_image_from_surface(SpiceCanvas *spice_canvas, + pixman_region32_t *region, + SpiceCanvas *surface_canvas, + int src_x, int src_y, + int src_width, int src_height, + int dest_x, int dest_y, + int dest_width, int dest_height, + uint32_t transparent_color) +{ + SwCanvas *sw_surface_canvas = (SwCanvas *)surface_canvas; + __colorkey_scale_image(spice_canvas, region, sw_surface_canvas->image, src_x, src_y, + src_width, src_height, dest_x, dest_y, dest_width, dest_height, + transparent_color); +} + +static void canvas_put_image(SpiceCanvas *spice_canvas, +#ifdef WIN32 + HDC dc, +#endif + const SpiceRect *dest, const uint8_t *src_data, + uint32_t src_width, uint32_t src_height, int src_stride, + const QRegion *clip) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_image_t *src; + int dest_width; + int dest_height; + double sx, sy; + pixman_transform_t transform; + + src = pixman_image_create_bits(PIXMAN_x8r8g8b8, + src_width, + src_height, + (uint32_t*)src_data, + src_stride); + + + if (clip) { + pixman_image_set_clip_region32 (canvas->image, (pixman_region32_t *)clip); + } + + dest_width = dest->right - dest->left; + dest_height = dest->bottom - dest->top; + + if (dest_width != src_width || dest_height != src_height) { + sx = (double)(src_width) / (dest_width); + sy = (double)(src_height) / (dest_height); + + pixman_transform_init_scale(&transform, + pixman_double_to_fixed(sx), + pixman_double_to_fixed(sy)); + pixman_image_set_transform(src, &transform); + pixman_image_set_filter(src, + PIXMAN_FILTER_NEAREST, + NULL, 0); + } + + pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE); + + pixman_image_composite32(PIXMAN_OP_SRC, + src, NULL, canvas->image, + 0, 0, /* src */ + 0, 0, /* mask */ + dest->left, dest->top, /* dst */ + dest_width, dest_height); + + + if (clip) { + pixman_image_set_clip_region32(canvas->image, NULL); + } + pixman_image_unref(src); +} + + +static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, + SpiceClip *clip, SpiceText *text) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_region32_t dest_region; + pixman_image_t *str_mask, *brush; + SpiceString *str; + SpicePoint pos; + int depth; + + pixman_region32_init_rect(&dest_region, + bbox->left, bbox->top, + bbox->right - bbox->left, + bbox->bottom - bbox->top); + + canvas_clip_pixman(&canvas->base, &dest_region, clip); + + if (!pixman_region32_not_empty(&dest_region)) { + touch_brush(&canvas->base, &text->fore_brush); + touch_brush(&canvas->base, &text->back_brush); + pixman_region32_fini(&dest_region); + return; + } + + if (!rect_is_empty(&text->back_area)) { + pixman_region32_t back_region; + + /* Nothing else makes sense for text and we should deprecate it + * and actually it means OVER really */ + ASSERT(text->fore_mode == SPICE_ROPD_OP_PUT); + + pixman_region32_init_rect(&back_region, + text->back_area.left, + text->back_area.top, + text->back_area.right - text->back_area.left, + text->back_area.bottom - text->back_area.top); + + pixman_region32_intersect(&back_region, &back_region, &dest_region); + + if (pixman_region32_not_empty(&back_region)) { + draw_brush(spice_canvas, &back_region, &text->back_brush, SPICE_ROP_COPY); + } + + pixman_region32_fini(&back_region); + } + str = (SpiceString *)SPICE_GET_ADDRESS(text->str); + + if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) { + depth = 1; + } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) { + depth = 4; + } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) { + WARN("untested path A8 glyphs"); + depth = 8; + } else { + WARN("unsupported path vector glyphs"); + pixman_region32_fini (&dest_region); + return; + } + + brush = canvas_get_pixman_brush(canvas, &text->fore_brush); + + str_mask = canvas_get_str_mask(&canvas->base, str, depth, &pos); + if (brush) { + pixman_image_set_clip_region32(canvas->image, &dest_region); + + pixman_image_composite32(PIXMAN_OP_OVER, + brush, + str_mask, + canvas->image, + 0, 0, + 0, 0, + pos.x, pos.y, + pixman_image_get_width(str_mask), + pixman_image_get_height(str_mask)); + if (canvas->base.format == SPICE_SURFACE_FMT_32_xRGB) { + clear_dest_alpha(canvas->image, pos.x, pos.y, + pixman_image_get_width(str_mask), + pixman_image_get_height(str_mask)); + } + pixman_image_unref(brush); + + pixman_image_set_clip_region32(canvas->image, NULL); + } + pixman_image_unref(str_mask); + pixman_region32_fini(&dest_region); +} + +static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, + int dest_stride, const SpiceRect *area) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + pixman_image_t* surface; + uint8_t *src; + int src_stride; + uint8_t *dest_end; + int bpp; + + ASSERT(canvas && area); + + surface = canvas->image; + + bpp = spice_pixman_image_get_bpp(surface) / 8; + + src_stride = pixman_image_get_stride(surface); + src = (uint8_t *)pixman_image_get_data(surface) + + area->top * src_stride + area->left * bpp; + dest_end = dest + (area->bottom - area->top) * dest_stride; + for (; dest != dest_end; dest += dest_stride, src += src_stride) { + memcpy(dest, src, (area->right - area->left) * bpp); + } +} + +static void canvas_clear(SpiceCanvas *spice_canvas) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + spice_pixman_fill_rect(canvas->image, + 0, 0, + pixman_image_get_width(canvas->image), + pixman_image_get_height(canvas->image), + 0); +} + +static void canvas_set_access_params(SpiceCanvas *spice_canvas, + unsigned long base, unsigned long max) +{ +#ifdef SW_CANVAS_ACCESS_TEST + SwCanvas *canvas = (SwCanvas *)spice_canvas; + __canvas_set_access_params(&canvas->base, base, max); +#endif +} + +static void canvas_destroy(SpiceCanvas *spice_canvas) +{ + SwCanvas *canvas = (SwCanvas *)spice_canvas; + if (!canvas) { + return; + } + pixman_image_unref(canvas->image); + canvas_base_destroy(&canvas->base); + if (canvas->private_data) { + free(canvas->private_data); + } + free(canvas); +} + +static int need_init = 1; +static SpiceCanvasOps sw_canvas_ops; + +static SpiceCanvas *canvas_create_common(pixman_image_t *image, + uint32_t format +#ifdef SW_CANVAS_CACHE + , SpiceImageCache *bits_cache + , SpicePaletteCache *palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , SpiceImageCache *bits_cache +#endif + , SpiceImageSurfaces *surfaces + , SpiceGlzDecoder *glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , SpiceVirtMapping *virt_mapping +#endif + ) +{ + SwCanvas *canvas; + int init_ok; + + if (need_init) { + return NULL; + } + spice_pixman_image_set_format(image, + spice_surface_format_to_pixman (format)); + + canvas = spice_new0(SwCanvas, 1); + init_ok = canvas_base_init(&canvas->base, &sw_canvas_ops, + pixman_image_get_width (image), + pixman_image_get_height (image), + format +#ifdef SW_CANVAS_CACHE + , bits_cache + , palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , bits_cache +#endif + , surfaces + , glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , virt_mapping +#endif + ); + canvas->private_data = NULL; + canvas->private_data_size = 0; + + canvas->image = image; + + return (SpiceCanvas *)canvas; +} + +SpiceCanvas *canvas_create(int width, int height, uint32_t format +#ifdef SW_CANVAS_CACHE + , SpiceImageCache *bits_cache + , SpicePaletteCache *palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , SpiceImageCache *bits_cache +#endif + , SpiceImageSurfaces *surfaces + , SpiceGlzDecoder *glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , SpiceVirtMapping *virt_mapping +#endif + ) +{ + pixman_image_t *image; + + image = pixman_image_create_bits(spice_surface_format_to_pixman (format), + width, height, NULL, 0); + + return canvas_create_common(image, format +#ifdef SW_CANVAS_CACHE + , bits_cache + , palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , bits_cache +#endif + , surfaces + , glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , virt_mapping +#endif + ); +} + +SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, + uint8_t *data, size_t stride +#ifdef SW_CANVAS_CACHE + , SpiceImageCache *bits_cache + , SpicePaletteCache *palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , SpiceImageCache *bits_cache +#endif + , SpiceImageSurfaces *surfaces + , SpiceGlzDecoder *glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , SpiceVirtMapping *virt_mapping +#endif + ) +{ + pixman_image_t *image; + + image = pixman_image_create_bits(spice_surface_format_to_pixman (format), + width, height, (uint32_t *)data, stride); + + return canvas_create_common(image, format +#ifdef SW_CANVAS_CACHE + , bits_cache + , palette_cache +#elif defined(SW_CANVAS_IMAGE_CACHE) + , bits_cache +#endif + , surfaces + , glz_decoder +#ifndef SW_CANVAS_NO_CHUNKS + , virt_mapping +#endif + ); +} + +void sw_canvas_init() //unsafe global function +{ + if (!need_init) { + return; + } + need_init = 0; + + canvas_base_init_ops(&sw_canvas_ops); + sw_canvas_ops.draw_text = canvas_draw_text; + sw_canvas_ops.put_image = canvas_put_image; + sw_canvas_ops.clear = canvas_clear; + sw_canvas_ops.read_bits = canvas_read_bits; + sw_canvas_ops.set_access_params = canvas_set_access_params; + sw_canvas_ops.destroy = canvas_destroy; + + sw_canvas_ops.fill_solid_spans = fill_solid_spans; + sw_canvas_ops.fill_solid_rects = fill_solid_rects; + sw_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop; + sw_canvas_ops.fill_tiled_rects = fill_tiled_rects; + sw_canvas_ops.fill_tiled_rects_from_surface = fill_tiled_rects_from_surface; + sw_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop; + sw_canvas_ops.fill_tiled_rects_rop_from_surface = fill_tiled_rects_rop_from_surface; + sw_canvas_ops.blit_image = blit_image; + sw_canvas_ops.blit_image_from_surface = blit_image_from_surface; + sw_canvas_ops.blit_image_rop = blit_image_rop; + sw_canvas_ops.blit_image_rop_from_surface = blit_image_rop_from_surface; + sw_canvas_ops.scale_image = scale_image; + sw_canvas_ops.scale_image_from_surface = scale_image_from_surface; + sw_canvas_ops.scale_image_rop = scale_image_rop; + sw_canvas_ops.scale_image_rop_from_surface = scale_image_rop_from_surface; + sw_canvas_ops.blend_image = blend_image; + sw_canvas_ops.blend_image_from_surface = blend_image_from_surface; + sw_canvas_ops.blend_scale_image = blend_scale_image; + sw_canvas_ops.blend_scale_image_from_surface = blend_scale_image_from_surface; + sw_canvas_ops.colorkey_image = colorkey_image; + sw_canvas_ops.colorkey_image_from_surface = colorkey_image_from_surface; + sw_canvas_ops.colorkey_scale_image = colorkey_scale_image; + sw_canvas_ops.colorkey_scale_image_from_surface = colorkey_scale_image_from_surface; + sw_canvas_ops.copy_region = copy_region; + sw_canvas_ops.get_image = get_image; + rop3_init(); +} |