spice/common/cairo_canvas.c
Alexander Larsson 6664c12278 Fix up empty region checks in canvas operations
We rely on not passing on empty rects to the drawing operations by
checking for empty regions and exiting early. However the checks
were wrongly using pixman_region32_n_rects(region) == 0, whereas
we should be using pixman_region32_not_empty().
2010-03-17 10:06:40 +01:00

873 lines
31 KiB
C

/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include "cairo_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 CairoCanvas CairoCanvas;
struct CairoCanvas {
CanvasBase base;
uint32_t *private_data;
int private_data_size;
pixman_image_t *image;
};
static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *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: {
pixman_image_t* surface;
pixman_transform_t t;
surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
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 void copy_region(SpiceCanvas *spice_canvas,
pixman_region32_t *dest_region,
int dx, int dy)
{
CairoCanvas *canvas = (CairoCanvas *)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)
{
CairoCanvas *canvas = (CairoCanvas *)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)
{
CairoCanvas *canvas = (CairoCanvas *)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)
{
CairoCanvas *canvas = (CairoCanvas *)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)
{
CairoCanvas *canvas = (CairoCanvas *)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_rop(SpiceCanvas *spice_canvas,
pixman_box32_t *rects,
int n_rects,
pixman_image_t *tile,
int offset_x, int offset_y,
SpiceROP rop)
{
CairoCanvas *canvas = (CairoCanvas *)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 blit_image(SpiceCanvas *spice_canvas,
pixman_region32_t *region,
pixman_image_t *src_image,
int offset_x, int offset_y)
{
CairoCanvas *canvas = (CairoCanvas *)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_rop(SpiceCanvas *spice_canvas,
pixman_region32_t *region,
pixman_image_t *src_image,
int offset_x, int offset_y,
SpiceROP rop)
{
CairoCanvas *canvas = (CairoCanvas *)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 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)
{
CairoCanvas *canvas = (CairoCanvas *)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_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)
{
CairoCanvas *canvas = (CairoCanvas *)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(PIXMAN_x8r8g8b8,
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 blend_image(SpiceCanvas *spice_canvas,
pixman_region32_t *region,
pixman_image_t *src,
int src_x, int src_y,
int dest_x, int dest_y,
int width, int height,
int overall_alpha)
{
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
pixman_image_t *mask;
pixman_image_set_clip_region32(canvas->image, 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, canvas->image,
src_x, src_y, /* src */
0, 0, /* mask */
dest_x, dest_y, /* dst */
width,
height);
if (mask) {
pixman_image_unref(mask);
}
pixman_image_set_clip_region32(canvas->image, NULL);
}
static void blend_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,
int overall_alpha)
{
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
pixman_transform_t transform;
pixman_image_t *mask;
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));
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, 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);
if (mask) {
pixman_image_unref(mask);
}
pixman_image_set_clip_region32(canvas->image, NULL);
}
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)
{
CairoCanvas *canvas = (CairoCanvas *)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_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)
{
CairoCanvas *canvas = (CairoCanvas *)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(PIXMAN_x8r8g8b8,
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 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)
{
CairoCanvas *canvas = (CairoCanvas *)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)
{
CairoCanvas *canvas = (CairoCanvas *)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));
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)
{
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
pixman_image_t* surface;
uint8_t *src;
int src_stride;
uint8_t *dest_end;
ASSERT(canvas && area);
surface = canvas->image;
src_stride = pixman_image_get_stride(surface);
src = (uint8_t *)pixman_image_get_data(surface) +
area->top * src_stride + area->left * sizeof(uint32_t);
dest_end = dest + (area->bottom - area->top) * dest_stride;
for (; dest != dest_end; dest += dest_stride, src += src_stride) {
memcpy(dest, src, dest_stride);
}
}
static void canvas_clear(SpiceCanvas *spice_canvas)
{
CairoCanvas *canvas = (CairoCanvas *)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 CAIRO_CANVAS_ACCESS_TEST
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
__canvas_set_access_params(&canvas->base, base, max);
#endif
}
static void canvas_destroy(SpiceCanvas *spice_canvas)
{
CairoCanvas *canvas = (CairoCanvas *)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 cairo_canvas_ops;
SpiceCanvas *canvas_create(pixman_image_t *image, int bits
#ifdef CAIRO_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(CAIRO_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
#endif
, SpiceGlzDecoder *glz_decoder
#ifndef CAIRO_CANVAS_NO_CHUNKS
, SpiceVirtMapping *virt_mapping
#endif
)
{
CairoCanvas *canvas;
int init_ok;
if (need_init) {
return NULL;
}
canvas = spice_new0(CairoCanvas, 1);
init_ok = canvas_base_init(&canvas->base, &cairo_canvas_ops,
pixman_image_get_width (image),
pixman_image_get_height (image),
bits
#ifdef CAIRO_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(CAIRO_CANVAS_IMAGE_CACHE)
, bits_cache
#endif
, glz_decoder
#ifndef CAIRO_CANVAS_NO_CHUNKS
, virt_mapping
#endif
);
canvas->private_data = NULL;
canvas->private_data_size = 0;
canvas->image = pixman_image_ref(image);
return (SpiceCanvas *)canvas;
}
void cairo_canvas_init() //unsafe global function
{
if (!need_init) {
return;
}
need_init = 0;
canvas_base_init_ops(&cairo_canvas_ops);
cairo_canvas_ops.draw_text = canvas_draw_text;
cairo_canvas_ops.put_image = canvas_put_image;
cairo_canvas_ops.clear = canvas_clear;
cairo_canvas_ops.read_bits = canvas_read_bits;
cairo_canvas_ops.set_access_params = canvas_set_access_params;
cairo_canvas_ops.destroy = canvas_destroy;
cairo_canvas_ops.fill_solid_spans = fill_solid_spans;
cairo_canvas_ops.fill_solid_rects = fill_solid_rects;
cairo_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop;
cairo_canvas_ops.fill_tiled_rects = fill_tiled_rects;
cairo_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop;
cairo_canvas_ops.blit_image = blit_image;
cairo_canvas_ops.blit_image_rop = blit_image_rop;
cairo_canvas_ops.scale_image = scale_image;
cairo_canvas_ops.scale_image_rop = scale_image_rop;
cairo_canvas_ops.blend_image = blend_image;
cairo_canvas_ops.blend_scale_image = blend_scale_image;
cairo_canvas_ops.colorkey_image = colorkey_image;
cairo_canvas_ops.colorkey_scale_image = colorkey_scale_image;
cairo_canvas_ops.copy_region = copy_region;
rop3_init();
}