spice/common/gl_canvas.c
Christophe Fergeau 78c1465ed3 add #include <config.h> to all source files
When using config.h, it must be the very first include in all source
files since it contains #define that may change the compilation process
(eg libc structure layout changes when it's used to enable large file
support on 32 bit x86 archs). This commit adds it at the beginning
of all .c and .cpp files
2011-05-03 14:44:10 +02:00

909 lines
29 KiB
C

/* -*- 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/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef SPICE_CANVAS_INTERNAL
#error "This file shouldn't be compiled directly"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "quic.h"
#include "rop3.h"
#include "region.h"
#define GL_CANVAS
#include "canvas_base.c"
typedef struct GLCanvas GLCanvas;
struct GLCanvas {
CanvasBase base;
GLCCtx glc;
void *private_data;
int private_data_size;
int textures_lost;
};
static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int stride, int height)
{
uint8_t *ret_data = (uint8_t *)data;
uint8_t *dest;
uint8_t *src;
int i;
if (!canvas->private_data) {
canvas->private_data = spice_malloc_n(height, stride);
if (!canvas->private_data) {
return ret_data;
}
canvas->private_data_size = stride * height;
}
if (canvas->private_data_size < (stride * height)) {
free(canvas->private_data);
canvas->private_data = spice_malloc_n(height, stride);
if (!canvas->private_data) {
return ret_data;
}
canvas->private_data_size = stride * height;
}
dest = (uint8_t *)canvas->private_data;
src = (uint8_t *)data + (height - 1) * stride;
for (i = 0; i < height; ++i) {
memcpy(dest, src, stride);
dest += stride;
src -= stride;
}
return (uint8_t *)canvas->private_data;
}
static pixman_image_t *canvas_surf_to_trans_surf(GLCImage *image,
uint32_t trans_color)
{
int width = image->width;
int height = image->height;
uint8_t *src_line;
uint8_t *end_src_line;
int src_stride;
uint8_t *dest_line;
int dest_stride;
pixman_image_t *ret;
int i;
ret = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, 0);
if (ret == NULL) {
CANVAS_ERROR("create surface failed");
}
src_line = image->pixels;
src_stride = image->stride;
end_src_line = src_line + src_stride * height;
dest_line = (uint8_t *)pixman_image_get_data(ret);
dest_stride = pixman_image_get_stride(ret);
for (; src_line < end_src_line; src_line += src_stride, dest_line += dest_stride) {
for (i = 0; i < width; i++) {
if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
((uint32_t*)dest_line)[i] = 0;
} else {
((uint32_t*)dest_line)[i] = (((uint32_t*)src_line)[i]) | 0xff000000;
}
}
}
return ret;
}
static GLCPath get_path(GLCanvas *canvas, SpicePath *s)
{
GLCPath path = glc_path_create(canvas->glc);
int i;
for (i = 0; i < s->num_segments; i++) {
SpicePathSeg* seg = s->segments[i];
SpicePointFix* point = seg->points;
SpicePointFix* end_point = point + seg->count;
if (seg->flags & SPICE_PATH_BEGIN) {
glc_path_move_to(path, fix_to_double(point->x), fix_to_double(point->y));
point++;
}
if (seg->flags & SPICE_PATH_BEZIER) {
ASSERT((point - end_point) % 3 == 0);
for (; point + 2 < end_point; point += 3) {
glc_path_curve_to(path,
fix_to_double(point[0].x), fix_to_double(point[0].y),
fix_to_double(point[1].x), fix_to_double(point[1].y),
fix_to_double(point[2].x), fix_to_double(point[2].y));
}
} else {
for (; point < end_point; point++) {
glc_path_line_to(path, fix_to_double(point->x), fix_to_double(point->y));
}
}
if (seg->flags & SPICE_PATH_END) {
if (seg->flags & SPICE_PATH_CLOSE) {
glc_path_close(path);
}
}
}
return path;
}
#define SET_GLC_RECT(dest, src) { \
(dest)->x = (src)->left; \
(dest)->y = (src)->top; \
(dest)->width = (src)->right - (src)->left; \
(dest)->height = (src)->bottom - (src)->top; \
}
#define SET_GLC_BOX(dest, src) { \
(dest)->x = (src)->x1; \
(dest)->y = (src)->y1; \
(dest)->width = (src)->x2 - (src)->x1; \
(dest)->height = (src)->y2 - (src)->y1; \
}
static void set_clip(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip)
{
GLCRect rect;
glc_clip_reset(canvas->glc);
switch (clip->type) {
case SPICE_CLIP_TYPE_NONE:
break;
case SPICE_CLIP_TYPE_RECTS: {
uint32_t n = clip->rects->num_rects;
SpiceRect *now = clip->rects->rects;
SpiceRect *end = now + n;
if (n == 0) {
rect.x = rect.y = 0;
rect.width = rect.height = 0;
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
break;
} else {
SET_GLC_RECT(&rect, now);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
}
for (now++; now < end; now++) {
SET_GLC_RECT(&rect, now);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
}
break;
}
default:
CANVAS_ERROR("invalid clip type");
}
}
static void set_mask(GLCanvas *canvas, SpiceQMask *mask, int x, int y)
{
pixman_image_t *image;
if (!(image = canvas_get_mask(&canvas->base, mask, NULL))) {
glc_clear_mask(canvas->glc, GLC_MASK_A);
return;
}
glc_set_mask(canvas->glc, x - mask->pos.x, y - mask->pos.y,
pixman_image_get_width(image),
pixman_image_get_height(image),
pixman_image_get_stride(image),
(uint8_t *)pixman_image_get_data(image), GLC_MASK_A);
}
static inline void surface_to_image(GLCanvas *canvas, pixman_image_t *surface, GLCImage *image,
int ignore_stride)
{
int depth = pixman_image_get_depth(surface);
ASSERT(depth == 32 || depth == 24);
image->format = (depth == 24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
image->width = pixman_image_get_width(surface);
image->height = pixman_image_get_height(surface);
image->stride = pixman_image_get_stride(surface);
image->pixels = (uint8_t *)pixman_image_get_data(surface);
image->pallet = NULL;
if (ignore_stride) {
return;
}
if (image->stride < 0) {
image->stride = -image->stride;
image->pixels = image->pixels - (image->height - 1) * image->stride;
} else {
image->pixels = copy_opposite_image(canvas, image->pixels, image->stride, image->height);
}
}
static void set_brush(GLCanvas *canvas, SpiceBrush *brush)
{
switch (brush->type) {
case SPICE_BRUSH_TYPE_SOLID: {
uint32_t color = brush->u.color;
double r, g, b;
b = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
color >>= canvas->base.color_shift;
g = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
color >>= canvas->base.color_shift;
r = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
glc_set_rgb(canvas->glc, r, g, b);
break;
}
case SPICE_BRUSH_TYPE_PATTERN: {
GLCImage image;
GLCPattern pattern;
pixman_image_t *surface;
surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
surface_to_image(canvas, surface, &image, 0);
pattern = glc_pattern_create(canvas->glc, -brush->u.pattern.pos.x,
-brush->u.pattern.pos.y, &image);
glc_set_pattern(canvas->glc, pattern);
glc_pattern_destroy(pattern);
pixman_image_unref (surface);
}
case SPICE_BRUSH_TYPE_NONE:
return;
default:
CANVAS_ERROR("invalid brush type");
}
}
static void set_op(GLCanvas *canvas, uint16_t rop_decriptor)
{
GLCOp op;
switch (rop_decriptor) {
case SPICE_ROPD_OP_PUT:
op = GLC_OP_COPY;
break;
case SPICE_ROPD_OP_XOR:
op = GLC_OP_XOR;
break;
case SPICE_ROPD_OP_BLACKNESS:
op = GLC_OP_CLEAR;
break;
case SPICE_ROPD_OP_WHITENESS:
op = GLC_OP_SET;
break;
case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_COPY_INVERTED;
break;
case SPICE_ROPD_OP_INVERS:
op = GLC_OP_INVERT;
break;
case SPICE_ROPD_OP_AND:
op = GLC_OP_AND;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES:
op = GLC_OP_NAND;
break;
case SPICE_ROPD_OP_OR:
op = GLC_OP_OR;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_RES:
op = GLC_OP_NOR;
break;
case SPICE_ROPD_OP_XOR | SPICE_ROPD_INVERS_RES:
op = GLC_OP_EQUIV;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_DEST:
op = GLC_OP_AND_REVERSE;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_AND_INVERTED;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_DEST:
op = GLC_OP_OR_REVERSE;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_OR_INVERTED;
break;
default:
WARN("GLC_OP_NOOP");
op = GLC_OP_NOOP;
}
glc_set_op(canvas->glc, op);
}
static void gl_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect rect;
set_clip(canvas, bbox, clip);
set_mask(canvas, &fill->mask, bbox->left, bbox->top);
set_brush(canvas, &fill->brush);
set_op(canvas, fill->rop_descriptor);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &copy->mask, bbox->left, bbox->top);
set_op(canvas, copy->rop_descriptor);
//todo: optimize get_image (use ogl conversion + remove unnecessary copy of 32bpp)
surface = canvas_get_image(&canvas->base, copy->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &copy->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCRect fill_rect;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &opaque->mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, (opaque->rop_descriptor & SPICE_ROPD_INVERS_SRC) ? GLC_OP_COPY_INVERTED :
GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, opaque->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &opaque->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
set_brush(canvas, &opaque->brush);
set_op(canvas, opaque->rop_descriptor & ~SPICE_ROPD_INVERS_SRC);
SET_GLC_RECT(&fill_rect, bbox);
glc_fill_rect(canvas->glc, &fill_rect);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend *alpha_blend)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &alpha_blend->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, (double)alpha_blend->alpha / 0xff);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &blend->mask, bbox->left, bbox->top);
set_op(canvas, blend->rop_descriptor);
surface = canvas_get_image(&canvas->base, blend->src_bitmap, FALSE);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &blend->src_area);
surface_to_image(canvas, surface, &image, 0);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
pixman_image_t *trans_surf;
GLCImage image;
GLCRecti src;
GLCRecti dest;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, transparent->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
trans_surf = canvas_surf_to_trans_surf(&image, transparent->true_color);
pixman_image_unref(surface);
surface_to_image(canvas, trans_surf, &image, 1);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &transparent->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(trans_surf);
glc_flush(canvas->glc);
}
static inline void fill_common(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceQMask * mask, GLCOp op)
{
GLCRect rect;
set_clip(canvas, bbox, clip);
set_mask(canvas, mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, op);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
}
static void gl_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &whiteness->mask, GLC_OP_SET);
}
static void gl_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &blackness->mask, GLC_OP_CLEAR);
}
static void gl_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &invers->mask, GLC_OP_INVERT);
}
static void gl_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *d;
pixman_image_t *s;
GLCImage image;
SpicePoint src_pos;
uint8_t *data_opp;
int src_stride;
set_clip(canvas, bbox, clip);
set_mask(canvas, &rop3->mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, GLC_OP_COPY);
image.format = GLC_IMAGE_RGB32;
image.width = bbox->right - bbox->left;
image.height = bbox->bottom - bbox->top;
image.pallet = NULL;
d = pixman_image_create_bits(PIXMAN_x8r8g8b8, image.width, image.height, NULL, 0);
if (d == NULL) {
CANVAS_ERROR("create surface failed");
}
image.pixels = (uint8_t *)pixman_image_get_data(d);
image.stride = pixman_image_get_stride(d);
glc_read_pixels(canvas->glc, bbox->left, bbox->top, &image);
data_opp = copy_opposite_image(canvas, image.pixels,
image.stride,
pixman_image_get_height(d));
memcpy(image.pixels, data_opp,
image.stride * pixman_image_get_height(d));
s = canvas_get_image(&canvas->base, rop3->src_bitmap, FALSE);
src_stride = pixman_image_get_stride(s);
if (src_stride > 0) {
data_opp = copy_opposite_image(canvas, (uint8_t *)pixman_image_get_data(s),
src_stride, pixman_image_get_height(s));
memcpy((uint8_t *)pixman_image_get_data(s), data_opp,
src_stride * pixman_image_get_height(s));
}
if (!rect_is_same_size(bbox, &rop3->src_area)) {
pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
image.height, 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 < image.width ||
pixman_image_get_height(s) - src_pos.y < image.height) {
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, FALSE);
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);
//for now (bottom-top)
if (pat_pos.y < 0) {
pat_pos.y = pixman_image_get_height(p) + pat_pos.y;
}
pat_pos.y = (image.height + pat_pos.y) % pixman_image_get_height(p);
pat_pos.y = pixman_image_get_height(p) - pat_pos.y;
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);
GLCRecti dest;
GLCRecti src;
dest.x = bbox->left;
dest.y = bbox->top;
image.pixels = copy_opposite_image(canvas, image.pixels, pixman_image_get_stride(d),
pixman_image_get_height(d));
src.x = src.y = 0;
dest.width = src.width = image.width;
dest.height = src.height = image.height;
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(d);
}
static void gl_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCPath path;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
set_op(canvas, stroke->fore_mode);
set_brush(canvas, &stroke->brush);
if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
WARN("SPICE_LINE_FLAGS_STYLED");
}
glc_set_line_width(canvas->glc, 1.0);
path = get_path(canvas, stroke->path);
glc_stroke_path(canvas->glc, path);
glc_path_destroy(path);
}
static void gl_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect rect;
SpiceString *str;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
if (!rect_is_empty(&text->back_area)) {
set_brush(canvas, &text->back_brush);
set_op(canvas, text->back_mode);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
}
str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
set_brush(canvas, &text->fore_brush);
set_op(canvas, text->fore_mode);
if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
_glc_fill_mask(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
glc_fill_alpha(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
WARN("untested path A8 glyphs, doing nothing");
if (0) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
glc_fill_alpha(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
}
} else {
WARN("untested path vector glyphs, doing nothing");
if (0) {
//draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
}
}
glc_flush(canvas->glc);
}
static void gl_canvas_clear(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
glc_clear(canvas->glc);
glc_flush(canvas->glc);
}
static void gl_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
glc_copy_pixels(canvas->glc, bbox->left, bbox->top, src_pos->x, src_pos->y,
bbox->right - bbox->left, bbox->bottom - bbox->top);
}
static void gl_canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCImage image;
ASSERT(dest_stride > 0);
image.format = GLC_IMAGE_RGB32;
image.height = area->bottom - area->top;
image.width = area->right - area->left;
image.pixels = dest;
image.stride = dest_stride;
glc_read_pixels(canvas->glc, area->left, area->top, &image);
}
static void gl_canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect *glc_rects;
GLCRect *now, *end;
int num_rect;
pixman_box32_t *rects;
canvas_base_group_start(spice_canvas, region);
rects = pixman_region32_rectangles(region, &num_rect);
glc_rects = spice_new(GLCRect, num_rect);
now = glc_rects;
end = glc_rects + num_rect;
for (; now < end; now++, rects++) {
SET_GLC_BOX(now, rects);
}
glc_mask_rects(canvas->glc, num_rect, glc_rects, GLC_MASK_B);
free(glc_rects);
}
static void gl_canvas_put_image(SpiceCanvas *spice_canvas, const SpiceRect *dest, const uint8_t *src_data,
uint32_t src_width, uint32_t src_height, int src_stride,
const QRegion *clip)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRecti src;
GLCRecti gldest;
GLCImage image;
uint32_t i;
ASSERT(src_stride <= 0)
glc_clip_reset(canvas->glc);
if (clip) {
int num_rects;
pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
&num_rects);
GLCRect rect;
if (num_rects == 0) {
rect.x = rect.y = rect.width = rect.height = 0;
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
} else {
SET_GLC_BOX(&rect, rects);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
for (i = 1; i < num_rects; i++) {
SET_GLC_BOX(&rect, rects + i);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
}
}
}
SET_GLC_RECT(&gldest, dest);
src.x = src.y = 0;
src.width = src_width;
src.height = src_height;
image.format = GLC_IMAGE_RGB32;
image.width = src_width;
image.height = src_height;
src_stride = -src_stride;
image.stride = src_stride;
image.pixels = (uint8_t *)src_data - (src_height - 1) * src_stride;
image.pallet = NULL;
glc_draw_image(canvas->glc, &gldest, &src, &image, 0, 1);
glc_flush(canvas->glc);
}
static void gl_canvas_group_end(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
canvas_base_group_end(spice_canvas);
glc_clear_mask(canvas->glc, GLC_MASK_B);
}
static int need_init = 1;
static SpiceCanvasOps gl_canvas_ops;
SpiceCanvas *gl_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
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
)
{
GLCanvas *canvas;
int init_ok;
if (need_init) {
return NULL;
}
canvas = spice_new0(GLCanvas, 1);
if (!(canvas->glc = glc_create(width, height))) {
goto error_1;
}
canvas->private_data = NULL;
init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops,
width, height, format
#ifdef SW_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, bits_cache
#endif
, surfaces
, glz_decoder
, jpeg_decoder
, zlib_decoder
);
if (!init_ok) {
goto error_2;
}
return (SpiceCanvas *)canvas;
error_2:
glc_destroy(canvas->glc, 0);
error_1:
free(canvas);
return NULL;
}
void gl_canvas_set_textures_lost(SpiceCanvas *spice_canvas,
int textures_lost)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
canvas->textures_lost = textures_lost;
}
static void gl_canvas_destroy(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
if (!canvas) {
return;
}
canvas_base_destroy(&canvas->base);
glc_destroy(canvas->glc, canvas->textures_lost);
if (canvas->private_data) {
free(canvas->private_data);
}
free(canvas);
}
void gl_canvas_init(void) //unsafe global function
{
if (!need_init) {
return;
}
need_init = 0;
canvas_base_init_ops(&gl_canvas_ops);
gl_canvas_ops.draw_fill = gl_canvas_draw_fill;
gl_canvas_ops.draw_copy = gl_canvas_draw_copy;
gl_canvas_ops.draw_opaque = gl_canvas_draw_opaque;
gl_canvas_ops.copy_bits = gl_canvas_copy_bits;
gl_canvas_ops.draw_text = gl_canvas_draw_text;
gl_canvas_ops.draw_stroke = gl_canvas_draw_stroke;
gl_canvas_ops.draw_rop3 = gl_canvas_draw_rop3;
gl_canvas_ops.draw_blend = gl_canvas_draw_blend;
gl_canvas_ops.draw_blackness = gl_canvas_draw_blackness;
gl_canvas_ops.draw_whiteness = gl_canvas_draw_whiteness;
gl_canvas_ops.draw_invers = gl_canvas_draw_invers;
gl_canvas_ops.draw_transparent = gl_canvas_draw_transparent;
gl_canvas_ops.draw_alpha_blend = gl_canvas_draw_alpha_blend;
gl_canvas_ops.put_image = gl_canvas_put_image;
gl_canvas_ops.clear = gl_canvas_clear;
gl_canvas_ops.read_bits = gl_canvas_read_bits;
gl_canvas_ops.group_start = gl_canvas_group_start;
gl_canvas_ops.group_end = gl_canvas_group_end;
gl_canvas_ops.destroy = gl_canvas_destroy;
rop3_init();
}