mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2025-12-29 08:28:08 +00:00
This adds a set of virtual methods for low-level operations. A subclass can choose to implement those and let the default CanvasBase implementations handle the highlevel stuff.
1424 lines
49 KiB
C
1424 lines
49 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 "rop3.h"
|
|
#include "rect.h"
|
|
#include "region.h"
|
|
#include "lines.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_surface_from_self(CairoCanvas *canvas,
|
|
int x, int y,
|
|
int32_t width, int32_t heigth)
|
|
{
|
|
pixman_image_t *surface;
|
|
pixman_image_t *src_surface;
|
|
uint8_t *dest;
|
|
int dest_stride;
|
|
uint8_t *src;
|
|
int src_stride;
|
|
int i;
|
|
|
|
surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, heigth, NULL, 0);
|
|
if (surface == NULL) {
|
|
CANVAS_ERROR("create surface failed");
|
|
}
|
|
|
|
dest = (uint8_t *)pixman_image_get_data(surface);
|
|
dest_stride = pixman_image_get_stride(surface);
|
|
|
|
src_surface = canvas->image;
|
|
src = (uint8_t *)pixman_image_get_data(src_surface);
|
|
src_stride = pixman_image_get_stride(src_surface);
|
|
src += y * src_stride + (x << 2);
|
|
for (i = 0; i < heigth; i++, dest += dest_stride, src += src_stride) {
|
|
memcpy(dest, src, width << 2);
|
|
}
|
|
return surface;
|
|
}
|
|
|
|
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_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
|
|
{
|
|
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
|
|
pixman_region32_t dest_region;
|
|
pixman_image_t *d;
|
|
pixman_image_t *s;
|
|
SpicePoint src_pos;
|
|
int width;
|
|
int heigth;
|
|
|
|
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);
|
|
canvas_mask_pixman(&canvas->base, &dest_region, &rop3->mask,
|
|
bbox->left, bbox->top);
|
|
|
|
width = bbox->right - bbox->left;
|
|
heigth = bbox->bottom - bbox->top;
|
|
|
|
d = canvas_surface_from_self(canvas, bbox->left, bbox->top, width, heigth);
|
|
s = canvas_get_image(&canvas->base, rop3->src_bitmap);
|
|
|
|
if (!rect_is_same_size(bbox, &rop3->src_area)) {
|
|
pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
|
|
rop3->scale_mode);
|
|
pixman_image_unref(s);
|
|
s = scaled_s;
|
|
src_pos.x = 0;
|
|
src_pos.y = 0;
|
|
} else {
|
|
src_pos.x = rop3->src_area.left;
|
|
src_pos.y = rop3->src_area.top;
|
|
}
|
|
if (pixman_image_get_width(s) - src_pos.x < width ||
|
|
pixman_image_get_height(s) - src_pos.y < heigth) {
|
|
CANVAS_ERROR("bad src bitmap size");
|
|
}
|
|
if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
|
|
pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat);
|
|
SpicePoint pat_pos;
|
|
|
|
pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
|
|
pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
|
|
do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
|
|
pixman_image_unref(p);
|
|
} else {
|
|
uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
|
|
canvas_16bpp_to_32bpp(rop3->brush.u.color);
|
|
do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
|
|
}
|
|
pixman_image_unref(s);
|
|
|
|
blit_image(spice_canvas, &dest_region, d,
|
|
bbox->left,
|
|
bbox->top);
|
|
|
|
pixman_image_unref(d);
|
|
|
|
pixman_region32_fini(&dest_region);
|
|
}
|
|
|
|
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_n_rects(&dest_region) == 0) {
|
|
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);
|
|
}
|
|
|
|
typedef struct {
|
|
lineGC base;
|
|
pixman_image_t *dest;
|
|
pixman_region32_t dest_region;
|
|
SpiceROP fore_rop;
|
|
SpiceROP back_rop;
|
|
int solid;
|
|
uint32_t color;
|
|
pixman_image_t *tile;
|
|
int tile_offset_x;
|
|
int tile_offset_y;
|
|
} StrokeGC;
|
|
|
|
static void stroke_fill_spans(lineGC * pGC,
|
|
int num_spans,
|
|
SpicePoint *points,
|
|
int *widths,
|
|
int sorted,
|
|
int foreground)
|
|
{
|
|
StrokeGC *strokeGC;
|
|
int i;
|
|
pixman_image_t *dest;
|
|
SpiceROP rop;
|
|
|
|
strokeGC = (StrokeGC *)pGC;
|
|
dest = strokeGC->dest;
|
|
|
|
num_spans = spice_canvas_clip_spans(&strokeGC->dest_region,
|
|
points, widths, num_spans,
|
|
points, widths, sorted);
|
|
|
|
if (foreground) {
|
|
rop = strokeGC->fore_rop;
|
|
} else {
|
|
rop = strokeGC->back_rop;
|
|
}
|
|
|
|
for (i = 0; i < num_spans; i++) {
|
|
if (strokeGC->solid) {
|
|
if (rop == SPICE_ROP_COPY) {
|
|
spice_pixman_fill_rect(dest, points[i].x, points[i].y, widths[i], 1,
|
|
strokeGC->color);
|
|
} else {
|
|
spice_pixman_fill_rect_rop(dest, points[i].x, points[i].y, widths[i], 1,
|
|
strokeGC->color, rop);
|
|
}
|
|
} else {
|
|
if (rop == SPICE_ROP_COPY) {
|
|
spice_pixman_tile_rect(dest,
|
|
points[i].x, points[i].y,
|
|
widths[i], 1,
|
|
strokeGC->tile,
|
|
strokeGC->tile_offset_x,
|
|
strokeGC->tile_offset_y);
|
|
} else {
|
|
spice_pixman_tile_rect_rop(dest,
|
|
points[i].x, points[i].y,
|
|
widths[i], 1,
|
|
strokeGC->tile,
|
|
strokeGC->tile_offset_x,
|
|
strokeGC->tile_offset_y,
|
|
rop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stroke_fill_rects(lineGC * pGC,
|
|
int num_rects,
|
|
pixman_rectangle32_t *rects,
|
|
int foreground)
|
|
{
|
|
pixman_region32_t area;
|
|
pixman_box32_t *boxes;
|
|
StrokeGC *strokeGC;
|
|
pixman_image_t *dest;
|
|
SpiceROP rop;
|
|
int i;
|
|
pixman_box32_t *area_rects;
|
|
int n_area_rects;
|
|
|
|
strokeGC = (StrokeGC *)pGC;
|
|
dest = strokeGC->dest;
|
|
|
|
if (foreground) {
|
|
rop = strokeGC->fore_rop;
|
|
} else {
|
|
rop = strokeGC->back_rop;
|
|
}
|
|
|
|
/* TODO: We can optimize this for more common cases where
|
|
dest is one rect */
|
|
|
|
boxes = (pixman_box32_t *)malloc(num_rects * sizeof(pixman_box32_t));
|
|
for (i = 0; i < num_rects; i++) {
|
|
boxes[i].x1 = rects[i].x;
|
|
boxes[i].y1 = rects[i].y;
|
|
boxes[i].x2 = rects[i].x + rects[i].width;
|
|
boxes[i].y2 = rects[i].y + rects[i].height;
|
|
}
|
|
pixman_region32_init_rects(&area, boxes, num_rects);
|
|
pixman_region32_intersect(&area, &area, &strokeGC->dest_region);
|
|
free(boxes);
|
|
|
|
area_rects = pixman_region32_rectangles(&area, &n_area_rects);
|
|
|
|
for (i = 0; i < n_area_rects; i++) {
|
|
if (strokeGC->solid) {
|
|
if (rop == SPICE_ROP_COPY) {
|
|
spice_pixman_fill_rect(dest,
|
|
area_rects[i].x1,
|
|
area_rects[i].y1,
|
|
area_rects[i].x2 - area_rects[i].x1,
|
|
area_rects[i].y2 - area_rects[i].y1,
|
|
strokeGC->color);
|
|
} else {
|
|
spice_pixman_fill_rect_rop(dest,
|
|
area_rects[i].x1,
|
|
area_rects[i].y1,
|
|
area_rects[i].x2 - area_rects[i].x1,
|
|
area_rects[i].y2 - area_rects[i].y1,
|
|
strokeGC->color, rop);
|
|
}
|
|
} else {
|
|
if (rop == SPICE_ROP_COPY) {
|
|
spice_pixman_tile_rect(dest,
|
|
area_rects[i].x1,
|
|
area_rects[i].y1,
|
|
area_rects[i].x2 - area_rects[i].x1,
|
|
area_rects[i].y2 - area_rects[i].y1,
|
|
strokeGC->tile,
|
|
strokeGC->tile_offset_x,
|
|
strokeGC->tile_offset_y);
|
|
} else {
|
|
spice_pixman_tile_rect_rop(dest,
|
|
area_rects[i].x1,
|
|
area_rects[i].y1,
|
|
area_rects[i].x2 - area_rects[i].x1,
|
|
area_rects[i].y2 - area_rects[i].y1,
|
|
strokeGC->tile,
|
|
strokeGC->tile_offset_x,
|
|
strokeGC->tile_offset_y,
|
|
rop);
|
|
}
|
|
}
|
|
}
|
|
pixman_region32_fini(&area);
|
|
}
|
|
|
|
typedef struct {
|
|
SpicePoint *points;
|
|
int num_points;
|
|
int size;
|
|
} StrokeLines;
|
|
|
|
static void stroke_lines_init(StrokeLines *lines)
|
|
{
|
|
lines->points = (SpicePoint *)malloc(10*sizeof(SpicePoint));
|
|
lines->size = 10;
|
|
lines->num_points = 0;
|
|
}
|
|
|
|
static void stroke_lines_free(StrokeLines *lines)
|
|
{
|
|
free(lines->points);
|
|
}
|
|
|
|
static void stroke_lines_append(StrokeLines *lines,
|
|
int x, int y)
|
|
{
|
|
if (lines->num_points == lines->size) {
|
|
lines->size *= 2;
|
|
lines->points = (SpicePoint *)realloc(lines->points,
|
|
lines->size * sizeof(SpicePoint));
|
|
}
|
|
lines->points[lines->num_points].x = x;
|
|
lines->points[lines->num_points].y = y;
|
|
lines->num_points++;
|
|
}
|
|
|
|
static void stroke_lines_append_fix(StrokeLines *lines,
|
|
SpicePointFix *point)
|
|
{
|
|
stroke_lines_append(lines,
|
|
fix_to_int(point->x),
|
|
fix_to_int(point->y));
|
|
}
|
|
|
|
static inline int64_t dot(SPICE_FIXED28_4 x1,
|
|
SPICE_FIXED28_4 y1,
|
|
SPICE_FIXED28_4 x2,
|
|
SPICE_FIXED28_4 y2)
|
|
{
|
|
return (((int64_t)x1) *((int64_t)x2) +
|
|
((int64_t)y1) *((int64_t)y2)) >> 4;
|
|
}
|
|
|
|
static inline int64_t dot2(SPICE_FIXED28_4 x,
|
|
SPICE_FIXED28_4 y)
|
|
{
|
|
return (((int64_t)x) *((int64_t)x) +
|
|
((int64_t)y) *((int64_t)y)) >> 4;
|
|
}
|
|
|
|
static void subdivide_bezier(StrokeLines *lines,
|
|
SpicePointFix point0,
|
|
SpicePointFix point1,
|
|
SpicePointFix point2,
|
|
SpicePointFix point3)
|
|
{
|
|
int64_t A2, B2, C2, AB, CB, h1, h2;
|
|
|
|
A2 = dot2(point1.x - point0.x,
|
|
point1.y - point0.y);
|
|
B2 = dot2(point3.x - point0.x,
|
|
point3.y - point0.y);
|
|
C2 = dot2(point2.x - point3.x,
|
|
point2.y - point3.y);
|
|
|
|
AB = dot(point1.x - point0.x,
|
|
point1.y - point0.y,
|
|
point3.x - point0.x,
|
|
point3.y - point0.y);
|
|
|
|
CB = dot(point2.x - point3.x,
|
|
point2.y - point3.y,
|
|
point0.x - point3.x,
|
|
point0.y - point3.y);
|
|
|
|
h1 = (A2*B2 - AB*AB) >> 3;
|
|
h2 = (C2*B2 - CB*CB) >> 3;
|
|
|
|
if (h1 < B2 && h2 < B2) {
|
|
/* deviation squared less than half a pixel, use straight line */
|
|
stroke_lines_append_fix(lines, &point3);
|
|
} else {
|
|
SpicePointFix point01, point23, point12, point012, point123, point0123;
|
|
|
|
point01.x = (point0.x + point1.x) / 2;
|
|
point01.y = (point0.y + point1.y) / 2;
|
|
point12.x = (point1.x + point2.x) / 2;
|
|
point12.y = (point1.y + point2.y) / 2;
|
|
point23.x = (point2.x + point3.x) / 2;
|
|
point23.y = (point2.y + point3.y) / 2;
|
|
point012.x = (point01.x + point12.x) / 2;
|
|
point012.y = (point01.y + point12.y) / 2;
|
|
point123.x = (point12.x + point23.x) / 2;
|
|
point123.y = (point12.y + point23.y) / 2;
|
|
point0123.x = (point012.x + point123.x) / 2;
|
|
point0123.y = (point012.y + point123.y) / 2;
|
|
|
|
subdivide_bezier(lines, point0, point01, point012, point0123);
|
|
subdivide_bezier(lines, point0123, point123, point23, point3);
|
|
}
|
|
}
|
|
|
|
static void stroke_lines_append_bezier(StrokeLines *lines,
|
|
SpicePointFix *point1,
|
|
SpicePointFix *point2,
|
|
SpicePointFix *point3)
|
|
{
|
|
SpicePointFix point0;
|
|
|
|
point0.x = int_to_fix(lines->points[lines->num_points-1].x);
|
|
point0.y = int_to_fix(lines->points[lines->num_points-1].y);
|
|
|
|
subdivide_bezier(lines, point0, *point1, *point2, *point3);
|
|
}
|
|
|
|
static void stroke_lines_draw(StrokeLines *lines,
|
|
lineGC *gc,
|
|
int dashed)
|
|
{
|
|
if (lines->num_points != 0) {
|
|
if (dashed) {
|
|
spice_canvas_zero_dash_line(gc, CoordModeOrigin,
|
|
lines->num_points, lines->points);
|
|
} else {
|
|
spice_canvas_zero_line(gc, CoordModeOrigin,
|
|
lines->num_points, lines->points);
|
|
}
|
|
lines->num_points = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
|
|
{
|
|
CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
|
|
StrokeGC gc = { {0} };
|
|
lineGCOps ops = {
|
|
stroke_fill_spans,
|
|
stroke_fill_rects
|
|
};
|
|
uint32_t *data_size;
|
|
uint32_t more;
|
|
SpicePathSeg *seg;
|
|
StrokeLines lines;
|
|
int i;
|
|
int dashed;
|
|
|
|
pixman_region32_init_rect(&gc.dest_region,
|
|
bbox->left, bbox->top,
|
|
bbox->right - bbox->left,
|
|
bbox->bottom - bbox->top);
|
|
|
|
canvas_clip_pixman(&canvas->base, &gc.dest_region, clip);
|
|
|
|
if (pixman_region32_n_rects(&gc.dest_region) == 0) {
|
|
touch_brush(&canvas->base, &stroke->brush);
|
|
pixman_region32_fini(&gc.dest_region);
|
|
return;
|
|
}
|
|
|
|
gc.fore_rop = ropd_descriptor_to_rop(stroke->fore_mode,
|
|
ROP_INPUT_BRUSH,
|
|
ROP_INPUT_DEST);
|
|
gc.back_rop = ropd_descriptor_to_rop(stroke->back_mode,
|
|
ROP_INPUT_BRUSH,
|
|
ROP_INPUT_DEST);
|
|
|
|
gc.dest = canvas->image;
|
|
gc.base.width = pixman_image_get_width(gc.dest);
|
|
gc.base.height = pixman_image_get_height(gc.dest);
|
|
gc.base.alu = gc.fore_rop;
|
|
gc.base.lineWidth = 0;
|
|
|
|
/* dash */
|
|
gc.base.dashOffset = 0;
|
|
gc.base.dash = NULL;
|
|
gc.base.numInDashList = 0;
|
|
gc.base.lineStyle = LineSolid;
|
|
/* win32 cosmetic lines are endpoint-exclusive, so use CapNotLast */
|
|
gc.base.capStyle = CapNotLast;
|
|
gc.base.joinStyle = JoinMiter;
|
|
gc.base.ops = &ops;
|
|
|
|
dashed = 0;
|
|
if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
|
|
SPICE_FIXED28_4 *style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(stroke->attr.style);
|
|
int nseg;
|
|
|
|
dashed = 1;
|
|
|
|
nseg = stroke->attr.style_nseg;
|
|
|
|
/* To truly handle back_mode we should use LineDoubleDash here
|
|
and treat !foreground as back_rop using the same brush.
|
|
However, using the same brush for that seems wrong.
|
|
The old cairo backend was stroking the non-dashed line with
|
|
rop_mode before enabling dashes for the foreground which is
|
|
not right either. The gl an gdi backend don't use back_mode
|
|
at all */
|
|
gc.base.lineStyle = LineOnOffDash;
|
|
gc.base.dash = (unsigned char *)malloc(nseg);
|
|
gc.base.numInDashList = nseg;
|
|
access_test(&canvas->base, style, nseg * sizeof(*style));
|
|
|
|
if (stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP) {
|
|
gc.base.dash[stroke->attr.style_nseg - 1] = fix_to_int(style[0]);
|
|
for (i = 0; i < stroke->attr.style_nseg - 1; i++) {
|
|
gc.base.dash[i] = fix_to_int(style[i+1]);
|
|
}
|
|
gc.base.dashOffset = gc.base.dash[0];
|
|
} else {
|
|
for (i = 0; i < stroke->attr.style_nseg; i++) {
|
|
gc.base.dash[i] = fix_to_int(style[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (stroke->brush.type) {
|
|
case SPICE_BRUSH_TYPE_NONE:
|
|
gc.solid = TRUE;
|
|
gc.color = 0;
|
|
break;
|
|
case SPICE_BRUSH_TYPE_SOLID:
|
|
gc.solid = TRUE;
|
|
gc.color = stroke->brush.u.color;
|
|
break;
|
|
case SPICE_BRUSH_TYPE_PATTERN:
|
|
gc.solid = FALSE;
|
|
gc.tile = canvas_get_image(&canvas->base,
|
|
stroke->brush.u.pattern.pat);
|
|
gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
|
|
gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
|
|
break;
|
|
default:
|
|
CANVAS_ERROR("invalid brush type");
|
|
}
|
|
|
|
data_size = (uint32_t*)SPICE_GET_ADDRESS(stroke->path);
|
|
access_test(&canvas->base, data_size, sizeof(uint32_t));
|
|
more = *data_size;
|
|
seg = (SpicePathSeg*)(data_size + 1);
|
|
|
|
stroke_lines_init(&lines);
|
|
|
|
do {
|
|
access_test(&canvas->base, seg, sizeof(SpicePathSeg));
|
|
|
|
uint32_t flags = seg->flags;
|
|
SpicePointFix* point = (SpicePointFix*)seg->data;
|
|
SpicePointFix* end_point = point + seg->count;
|
|
access_test(&canvas->base, point, (unsigned long)end_point - (unsigned long)point);
|
|
ASSERT(point < end_point);
|
|
more -= ((unsigned long)end_point - (unsigned long)seg);
|
|
seg = (SpicePathSeg*)end_point;
|
|
|
|
if (flags & SPICE_PATH_BEGIN) {
|
|
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
|
stroke_lines_append_fix(&lines, point);
|
|
point++;
|
|
}
|
|
|
|
if (flags & SPICE_PATH_BEZIER) {
|
|
ASSERT((point - end_point) % 3 == 0);
|
|
for (; point + 2 < end_point; point += 3) {
|
|
stroke_lines_append_bezier(&lines,
|
|
&point[0],
|
|
&point[1],
|
|
&point[2]);
|
|
}
|
|
} else
|
|
{
|
|
for (; point < end_point; point++) {
|
|
stroke_lines_append_fix(&lines, point);
|
|
}
|
|
}
|
|
if (flags & SPICE_PATH_END) {
|
|
if (flags & SPICE_PATH_CLOSE) {
|
|
stroke_lines_append(&lines,
|
|
lines.points[0].x, lines.points[0].y);
|
|
}
|
|
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
|
}
|
|
} while (more);
|
|
|
|
stroke_lines_draw(&lines, (lineGC *)&gc, dashed);
|
|
|
|
if (gc.base.dash) {
|
|
free(gc.base.dash);
|
|
}
|
|
stroke_lines_free(&lines);
|
|
|
|
if (!gc.solid && gc.tile) {
|
|
pixman_image_unref(gc.tile);
|
|
}
|
|
|
|
pixman_region32_fini(&gc.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 || !(canvas = (CairoCanvas *)malloc(sizeof(CairoCanvas)))) {
|
|
return NULL;
|
|
}
|
|
memset(canvas, 0, sizeof(CairoCanvas));
|
|
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.draw_stroke = canvas_draw_stroke;
|
|
cairo_canvas_ops.draw_rop3 = canvas_draw_rop3;
|
|
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();
|
|
}
|