summaryrefslogtreecommitdiffstats
path: root/common/sw_canvas.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/sw_canvas.c')
-rw-r--r--common/sw_canvas.c1332
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();
+}