/* -*- 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 .
*/
#include
#include
#include "gdi_canvas.h"
#define GDI_CANVAS
#include "canvas_base.c"
#include "rop3.h"
#include "rect.h"
#include "region.h"
#include "threads.h"
typedef struct GdiCanvas GdiCanvas;
struct GdiCanvas {
CanvasBase base;
HDC dc;
RecurciveMutex* lock;
};
struct BitmapData {
HBITMAP hbitmap;
HBITMAP prev_hbitmap;
SpicePoint pos;
uint8_t flags;
HDC dc;
int cache;
int from_surface;
};
#define _rop3_brush 0xf0
#define _rop3_src 0xcc
#define _rop3_dest 0xaa
uint32_t raster_ops[] = {
0x00000042,
0x00010289,
0x00020C89,
0x000300AA,
0x00040C88,
0x000500A9,
0x00060865,
0x000702C5,
0x00080F08,
0x00090245,
0x000A0329,
0x000B0B2A,
0x000C0324,
0x000D0B25,
0x000E08A5,
0x000F0001,
0x00100C85,
0x001100A6,
0x00120868,
0x001302C8,
0x00140869,
0x001502C9,
0x00165CCA,
0x00171D54,
0x00180D59,
0x00191CC8,
0x001A06C5,
0x001B0768,
0x001C06CA,
0x001D0766,
0x001E01A5,
0x001F0385,
0x00200F09,
0x00210248,
0x00220326,
0x00230B24,
0x00240D55,
0x00251CC5,
0x002606C8,
0x00271868,
0x00280369,
0x002916CA,
0x002A0CC9,
0x002B1D58,
0x002C0784,
0x002D060A,
0x002E064A,
0x002F0E2A,
0x0030032A,
0x00310B28,
0x00320688,
0x00330008,
0x003406C4,
0x00351864,
0x003601A8,
0x00370388,
0x0038078A, // PSDPoax
0x00390604, // SPDnox
0x003A0644, // SPDSxox
0x003B0E24, // SPDnoan
0x003C004A, // PSx
0x003D18A4, // SPDSonox
0x003E1B24, // SPDSnaox
0x003F00EA, // PSan
0x00400F0A, // PSDnaa
0x00410249, // DPSxon
0x00420D5D, // SDxPDxa
0x00431CC4, // SPDSanaxn
0x00440328, // SDna SRCERASE
0x00450B29, // DPSnaon
0x004606C6, // DSPDaox
0x0047076A, // PSDPxaxn
0x00480368, // SDPxa
0x004916C5, // PDSPDaoxxn
0x004A0789, // DPSDoax
0x004B0605, // PDSnox
0x004C0CC8, // SDPana
0x004D1954, // SSPxDSxoxn
0x004E0645, // PDSPxox
0x004F0E25, // PDSnoan
0x00500325, // PDna
0x00510B26, // DSPnaon
0x005206C9, // DPSDaox
0x00530764, // SPDSxaxn
0x005408A9, // DPSonon
0x00550009, // Dn DSTINVERT
0x005601A9, // DPSox
0x00570389, // DPSoan
0x00580785, // PDSPoax
0x00590609, // DPSnox
0x005A0049, // DPx PATINVERT
0x005B18A9, // DPSDonox
0x005C0649, // DPSDxox
0x005D0E29, // DPSnoan
0x005E1B29, // DPSDnaox
0x005F00E9, // DPan
0x00600365, // PDSxa
0x006116C6, // DSPDSaoxxn
0x00620786, // DSPDoax
0x00630608, // SDPnox
0x00640788, // SDPSoax
0x00650606, // DSPnox
0x00660046, // DSx SRCINVERT
0x006718A8, // SDPSonox
0x006858A6, // DSPDSonoxxn
0x00690145, // PDSxxn
0x006A01E9, // DPSax
0x006B178A, // PSDPSoaxxn
0x006C01E8, // SDPax
0x006D1785, // PDSPDoaxxn
0x006E1E28, // SDPSnoax
0x006F0C65, // PDSxnan
0x00700CC5, // PDSana
0x00711D5C, // SSDxPDxaxn
0x00720648, // SDPSxox
0x00730E28, // SDPnoan
0x00740646, // DSPDxox
0x00750E26, // DSPnoan
0x00761B28, // SDPSnaox
0x007700E6, // DSan
0x007801E5, // PDSax
0x00791786, // DSPDSoaxxn
0x007A1E29, // DPSDnoax
0x007B0C68, // SDPxnan
0x007C1E24, // SPDSnoax
0x007D0C69, // DPSxnan
0x007E0955, // SPxDSxo
0x007F03C9, // DPSaan
0x008003E9, // DPSaa
0x00810975, // SPxDSxon
0x00820C49, // DPSxna
0x00831E04, // SPDSnoaxn
0x00840C48, // SDPxna
0x00851E05, // PDSPnoaxn
0x008617A6, // DSPDSoaxx
0x008701C5, // PDSaxn
0x008800C6, // DSa SRCAND
0x00891B08, // SDPSnaoxn
0x008A0E06, // DSPnoa
0x008B0666, // DSPDxoxn
0x008C0E08, // SDPnoa
0x008D0668, // SDPSxoxn
0x008E1D7C, // SSDxPDxax
0x008F0CE5, // PDSanan
0x00900C45, // PDSxna
0x00911E08, // SDPSnoaxn
0x009217A9, // DPSDPoaxx
0x009301C4, // SPDaxn
0x009417AA, // PSDPSoaxx
0x009501C9, // DPSaxn
0x00960169, // DPSxx
0x0097588A, // PSDPSonoxx
0x00981888, // SDPSonoxn
0x00990066, // DSxn
0x009A0709, // DPSnax
0x009B07A8, // SDPSoaxn
0x009C0704, // SPDnax
0x009D07A6, // DSPDoaxn
0x009E16E6, // DSPDSaoxx
0x009F0345, // PDSxan
0x00A000C9, // DPa
0x00A11B05, // PDSPnaoxn
0x00A20E09, // DPSnoa
0x00A30669, // DPSDxoxn
0x00A41885, // PDSPonoxn
0x00A50065, // PDxn
0x00A60706, // DSPnax
0x00A707A5, // PDSPoaxn
0x00A803A9, // DPSoa
0x00A90189, // DPSoxn
0x00AA0029, // D
0x00AB0889, // DPSono
0x00AC0744, // SPDSxax
0x00AD06E9, // DPSDaoxn
0x00AE0B06, // DSPnao
0x00AF0229, // DPno
0x00B00E05, // PDSnoa
0x00B10665, // PDSPxoxn
0x00B21974, // SSPxDSxox
0x00B30CE8, // SDPanan
0x00B4070A, // PSDnax
0x00B507A9, // DPSDoaxn
0x00B616E9, // DPSDPaoxx
0x00B70348, // SDPxan
0x00B8074A, // PSDPxax
0x00B906E6, // DSPDaoxn
0x00BA0B09, // DPSnao
0x00BB0226, // DSno MERGEPAINT
0x00BC1CE4, // SPDSanax
0x00BD0D7D, // SDxPDxan
0x00BE0269, // DPSxo
0x00BF08C9, // DPSano
0x00C000CA, // PSa MERGECOPY
0x00C11B04, // SPDSnaoxn
0x00C21884, // SPDSonoxn
0x00C3006A, // PSxn
0x00C40E04, // SPDnoa
0x00C50664, // SPDSxoxn
0x00C60708, // SDPnax
0x00C707AA, // PSDPoaxn
0x00C803A8, // SDPoa
0x00C90184, // SPDoxn
0x00CA0749, // DPSDxax
0x00CB06E4, // SPDSaoxn
0x00CC0020, // S SRCCOPY
0x00CD0888, // SDPono
0x00CE0B08, // SDPnao
0x00CF0224, // SPno
0x00D00E0A, // PSDnoa
0x00D1066A, // PSDPxoxn
0x00D20705, // PDSnax
0x00D307A4, // SPDSoaxn
0x00D41D78, // SSPxPDxax
0x00D50CE9, // DPSanan
0x00D616EA, // PSDPSaoxx
0x00D70349, // DPSxan
0x00D80745, // PDSPxax
0x00D906E8, // SDPSaoxn
0x00DA1CE9, // DPSDanax
0x00DB0D75, // SPxDSxan
0x00DC0B04, // SPDnao
0x00DD0228, // SDno
0x00DE0268, // SDPxo
0x00DF08C8, // SDPano
0x00E003A5, // PDSoa
0x00E10185, // PDSoxn
0x00E20746, // DSPDxax
0x00E306EA, // PSDPaoxn
0x00E40748, // SDPSxax
0x00E506E5, // PDSPaoxn
0x00E61CE8, // SDPSanax
0x00E70D79, // SPxPDxan
0x00E81D74, // SSPxDSxax
0x00E95CE6, // DSPDSanaxxn
0x00EA02E9, // DPSao
0x00EB0849, // DPSxno
0x00EC02E8, // SDPao
0x00ED0848, // SDPxno
0x00EE0086, // DSo SRCPAINT
0x00EF0A08, // SDPnoo
0x00F00021, // P PATCOPY
0x00F10885, // PDSono
0x00F20B05, // PDSnao
0x00F3022A, // PSno
0x00F40B0A, // PSDnao
0x00F50225, // PDno
0x00F60265, // PDSxo
0x00F708C5, // PDSano
0x00F802E5, // PDSao
0x00F90845, // PDSxno
0x00FA0089, // DPo
0x00FB0A09, // DPSnoo PATPAINT
0x00FC008A, // PSo
0x00FD0A0A, // PSDnoo
0x00FE02A9, // DPSoo
0x00FF0062 // 1 WHITENESS
};
static void set_path(GdiCanvas *canvas, void *addr)
{
uint32_t* data_size = (uint32_t*)addr;
access_test(&canvas->base, data_size, sizeof(uint32_t));
uint32_t more = *data_size;
SpicePathSeg* seg = (SpicePathSeg*)(data_size + 1);
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) {
BeginPath(canvas->dc);
if (!MoveToEx(canvas->dc, (int)fix_to_double(point->x), (int)fix_to_double(point->y),
NULL)) {
CANVAS_ERROR("MoveToEx failed");
return;
}
point++;
}
if (flags & SPICE_PATH_BEZIER) {
ASSERT((point - end_point) % 3 == 0);
for (; point + 2 < end_point; point += 3) {
POINT points[3];
points[0].x = (int)fix_to_double(point[0].x);
points[0].y = (int)fix_to_double(point[0].y);
points[1].x = (int)fix_to_double(point[1].x);
points[1].y = (int)fix_to_double(point[1].y);
points[2].x = (int)fix_to_double(point[2].x);
points[2].y = (int)fix_to_double(point[2].y);
if (!PolyBezierTo(canvas->dc, points, 3)) {
CANVAS_ERROR("PolyBezierTo failed");
return;
}
}
} else {
for (; point < end_point; point++) {
if (!LineTo(canvas->dc, (int)fix_to_double(point->x),
(int)fix_to_double(point->y))) {
CANVAS_ERROR("LineTo failed");
}
}
}
if (flags & SPICE_PATH_END) {
if (flags & SPICE_PATH_CLOSE) {
if (!CloseFigure(canvas->dc)) {
CANVAS_ERROR("CloseFigure failed");
}
}
if (!EndPath(canvas->dc)) {
CANVAS_ERROR("EndPath failed");
}
}
} while (more);
}
static void set_scale_mode(GdiCanvas *canvas, uint8_t scale_mode)
{
if (scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE) {
SetStretchBltMode(canvas->dc, HALFTONE);
} else if (scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) {
SetStretchBltMode(canvas->dc, COLORONCOLOR);
} else {
CANVAS_ERROR("Unknown ScaleMode");
}
}
static void set_clip(GdiCanvas *canvas, SpiceClip *clip)
{
switch (clip->type) {
case SPICE_CLIP_TYPE_NONE:
if (SelectClipRgn(canvas->dc, NULL) == ERROR) {
CANVAS_ERROR("SelectClipRgn failed");
}
break;
case SPICE_CLIP_TYPE_RECTS: {
uint32_t *n = (uint32_t *)SPICE_GET_ADDRESS(clip->data);
access_test(&canvas->base, n, sizeof(uint32_t));
SpiceRect *now = (SpiceRect *)(n + 1);
SpiceRect *end = now + *n;
access_test(&canvas->base, now, (unsigned long)end - (unsigned long)now);
if (now < end) {
HRGN main_hrgn;
main_hrgn = CreateRectRgn(now->left, now->top, now->right, now->bottom);
if (!main_hrgn) {
return;
}
now++;
for (; now < end; now++) {
HRGN combaine_hrgn;
combaine_hrgn = CreateRectRgn(now->left, now->top, now->right,
now->bottom);
if (!combaine_hrgn) {
CANVAS_ERROR("Unable to CreateRectRgn");
DeleteObject(main_hrgn);
return;
}
if (CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR) == ERROR) {
CANVAS_ERROR("Unable to CombineRgn");
DeleteObject(combaine_hrgn);
return;
}
DeleteObject(combaine_hrgn);
}
if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
CANVAS_ERROR("Unable to SelectClipRgn");
}
DeleteObject(main_hrgn);
}
break;
}
case SPICE_CLIP_TYPE_PATH:
set_path(canvas, SPICE_GET_ADDRESS(clip->data));
if (SelectClipPath(canvas->dc, RGN_COPY) == ERROR) {
CANVAS_ERROR("Unable to SelectClipPath");
}
break;
default:
CANVAS_ERROR("invalid clip type");
}
}
static void copy_bitmap(const uint8_t *src_image, int height, int src_stride,
uint8_t *dest_bitmap, int dest_stride)
{
int copy_width = MIN(dest_stride, src_stride);
int y = 0;
ASSERT(dest_stride >= 0 && src_stride >= 0);
while (y < height) {
memcpy(dest_bitmap, src_image, copy_width);
src_image += src_stride;
dest_bitmap += dest_stride;
y++;
}
}
static void copy_bitmap_alpha(const uint8_t *src_alpha, int height, int width, int src_stride,
uint8_t *dest_bitmap, int dest_stride, int alpha_bits_size)
{
int y = 0;
uint8_t i_offset;
int i_count = 0;
int i = 0;
int width_div_stride;
width_div_stride = width / src_stride;
if (alpha_bits_size == 1) {
i_offset = 1;
} else {
i_offset = 8;
}
while (y < height) {
int x;
for (x = 0; x < width; ++x) {
uint8_t alphaval;
double alpha;
alphaval = src_alpha[i];
alphaval = alphaval >> (i_count * i_offset);
alphaval = alphaval &= ((uint8_t)0xff >> (8 - i_offset));
alphaval = ((255 * alphaval) / ((uint8_t)0xff >> (8 - i_offset)));
dest_bitmap[x * 4 + 3] = alphaval;
alpha = (double)alphaval / 0xff;
dest_bitmap[x * 4 + 2] = (uint8_t)(alpha * dest_bitmap[x * 4 + 2]);
dest_bitmap[x * 4 + 1] = (uint8_t)(alpha * dest_bitmap[x * 4 + 1]);
dest_bitmap[x * 4] = (uint8_t)(alpha * dest_bitmap[x * 4]);
i_count++;
if (i_count == (8 / i_offset)) {
i++;
i_count = 0;
}
}
dest_bitmap += width * 4;
i = 0;
src_alpha += src_stride;
i_count = 0;
y++;
}
}
static uint8_t *create_bitmap(HBITMAP *bitmap, HBITMAP *prev_bitmap, HDC *dc,
const uint8_t *bitmap_data, int width, int height,
int stride, int bits, int rotate)
{
uint8_t *data;
const uint8_t *src_data;
uint32_t nstride;
struct {
BITMAPINFO inf;
RGBQUAD palette[255];
} bitmap_info;
memset(&bitmap_info, 0, sizeof(bitmap_info));
bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
bitmap_info.inf.bmiHeader.biWidth = width;
if (stride < 0) {
bitmap_info.inf.bmiHeader.biHeight = height;
} else {
bitmap_info.inf.bmiHeader.biHeight = -height;
}
if (rotate) {
bitmap_info.inf.bmiHeader.biHeight = -bitmap_info.inf.bmiHeader.biHeight;
}
bitmap_info.inf.bmiHeader.biPlanes = 1;
bitmap_info.inf.bmiHeader.biBitCount = bits;
bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
*dc = create_compatible_dc();
if (!*dc) {
CANVAS_ERROR("create_compatible_dc() failed");
return NULL;
}
*bitmap = CreateDIBSection(*dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
if (!*bitmap) {
CANVAS_ERROR("Unable to CreateDIBSection");
DeleteDC(*dc);
return NULL;
}
*prev_bitmap = (HBITMAP)SelectObject(*dc, *bitmap);
if (stride < 0) {
src_data = bitmap_data - (height - 1) * -stride;
} else {
src_data = bitmap_data;
}
switch (bits) {
case 1:
nstride = SPICE_ALIGN(width, 32) / 8;
break;
case 8:
nstride = SPICE_ALIGN(width, 4);
break;
case 16:
nstride = SPICE_ALIGN(width * 2, 4);
break;
case 32:
nstride = width * 4;
break;
default:
CANVAS_ERROR("invalid bitmap bits size");
}
if (bitmap_data) {
if (stride < 0) {
copy_bitmap(src_data, height, -stride, data, nstride);
} else {
copy_bitmap(src_data, height, stride, data, nstride);
}
}
return data;
}
static uint8_t *create_bitmap_from_pixman(HBITMAP *bitmap, HBITMAP *prev_bitmap, HDC *dc,
pixman_image_t *surface, int rotate)
{
return create_bitmap(bitmap, prev_bitmap, dc,
(uint8_t*)pixman_image_get_data(surface),
pixman_image_get_width(surface),
pixman_image_get_height(surface),
pixman_image_get_stride(surface),
spice_pixman_image_get_bpp(surface),
rotate);
}
static void release_bitmap(HDC dc, HBITMAP bitmap, HBITMAP prev_bitmap, int cache)
{
bitmap = (HBITMAP)SelectObject(dc, prev_bitmap);
if (!cache) {
DeleteObject(bitmap);
}
DeleteDC(dc);
}
static inline uint8_t get_converted_color(uint8_t color)
{
uint8_t msb;
msb = color & 0xE0;
msb = msb >> 5;
color |= msb;
return color;
}
static inline COLORREF get_color_ref(GdiCanvas *canvas, uint32_t color)
{
int shift = canvas->base.color_shift == 8 ? 0 : 3;
uint8_t r, g, b;
b = (color & canvas->base.color_mask);
color >>= canvas->base.color_shift;
g = (color & canvas->base.color_mask);
color >>= canvas->base.color_shift;
r = (color & canvas->base.color_mask);
if (shift) {
r = get_converted_color(r << shift);
g = get_converted_color(g << shift);
b = get_converted_color(b << shift);
}
return RGB(r, g, b);
}
static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush, RecurciveMutex **brush_lock)
{
HBRUSH hbrush;
*brush_lock = NULL;
switch (brush->type) {
case SPICE_BRUSH_TYPE_SOLID:
if (!(hbrush = CreateSolidBrush(get_color_ref(canvas, brush->u.color)))) {
CANVAS_ERROR("CreateSolidBrush failed");
}
return hbrush;
case SPICE_BRUSH_TYPE_PATTERN: {
GdiCanvas *gdi_surface = NULL;
HBRUSH hbrush;
pixman_image_t *surface;
HDC dc;
HBITMAP bitmap;
HBITMAP prev_bitmap;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
if (gdi_surface) {
bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
if (!bitmap) {
CANVAS_ERROR("GetCurrentObject failed");
}
*brush_lock = gdi_surface->lock;
} else {
surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
if (!create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, surface, 0)) {
CANVAS_ERROR("create_bitmap failed");
return NULL;
}
}
if (!(hbrush = CreatePatternBrush(bitmap))) {
CANVAS_ERROR("CreatePatternBrush failed");
}
if (!gdi_surface) {
release_bitmap(dc, bitmap, prev_bitmap, 0);
pixman_image_unref(surface);
}
return hbrush;
}
case SPICE_BRUSH_TYPE_NONE:
return NULL;
default:
CANVAS_ERROR("invalid brush type");
return NULL;
}
}
static HBRUSH set_brush(HDC dc, HBRUSH hbrush, SpiceBrush *brush)
{
switch (brush->type) {
case SPICE_BRUSH_TYPE_SOLID: {
return (HBRUSH)SelectObject(dc, hbrush);
}
case SPICE_BRUSH_TYPE_PATTERN: {
HBRUSH prev_hbrush;
prev_hbrush = (HBRUSH)SelectObject(dc, hbrush);
if (!SetBrushOrgEx(dc, brush->u.pattern.pos.x, brush->u.pattern.pos.y, NULL)) {
CANVAS_ERROR("SetBrushOrgEx failed");
}
return prev_hbrush;
}
default:
CANVAS_ERROR("invalid brush type");
return NULL;
}
}
static void unset_brush(HDC dc, HBRUSH prev_hbrush)
{
if (!prev_hbrush) {
return;
}
prev_hbrush = (HBRUSH)SelectObject(dc, prev_hbrush);
DeleteObject(prev_hbrush);
}
uint8_t calc_rop3(uint16_t rop3_bits, int brush)
{
uint8_t rop3 = 0;
uint8_t rop3_src = _rop3_src;
uint8_t rop3_dest = _rop3_dest;
uint8_t rop3_brush = _rop3_brush;
uint8_t rop3_src_brush;
if (rop3_bits & SPICE_ROPD_INVERS_SRC) {
rop3_src = ~rop3_src;
}
if (rop3_bits & SPICE_ROPD_INVERS_BRUSH) {
rop3_brush = ~rop3_brush;
}
if (rop3_bits & SPICE_ROPD_INVERS_DEST) {
rop3_dest = ~rop3_dest;
}
if (brush) {
rop3_src_brush = rop3_brush;
} else {
rop3_src_brush = rop3_src;
}
if (rop3_bits & SPICE_ROPD_OP_PUT) {
rop3 = rop3_src_brush;
}
if (rop3_bits & SPICE_ROPD_OP_OR) {
rop3 = rop3_src_brush | rop3_dest;
}
if (rop3_bits & SPICE_ROPD_OP_AND) {
rop3 = rop3_src_brush & rop3_dest;
}
if (rop3_bits & SPICE_ROPD_OP_XOR) {
rop3 = rop3_src_brush ^ rop3_dest;
}
if (rop3_bits & SPICE_ROPD_INVERS_RES) {
rop3 = ~rop3_dest;
}
if (rop3_bits & SPICE_ROPD_OP_BLACKNESS || rop3_bits & SPICE_ROPD_OP_WHITENESS ||
rop3_bits & SPICE_ROPD_OP_INVERS) {
CANVAS_ERROR("invalid rop3 type");
}
return rop3;
}
uint8_t calc_rop3_src_brush(uint16_t rop3_bits)
{
uint8_t rop3 = 0;
uint8_t rop3_src = _rop3_src;
uint8_t rop3_brush = _rop3_brush;
if (rop3_bits & SPICE_ROPD_INVERS_SRC) {
rop3_src = ~rop3_src;
}
if (rop3_bits & SPICE_ROPD_INVERS_BRUSH) {
rop3_brush = ~rop3_brush;
}
if (rop3_bits & SPICE_ROPD_OP_OR) {
rop3 = rop3_src | rop3_brush;
}
if (rop3_bits & SPICE_ROPD_OP_AND) {
rop3 = rop3_src & rop3_brush;
}
if (rop3_bits & SPICE_ROPD_OP_XOR) {
rop3 = rop3_src ^ rop3_brush;
}
return rop3;
}
static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct SpiceQMask *mask)
{
GdiCanvas *gdi_surface;
pixman_image_t *surface;
struct BitmapData bitmap;
PixmanData *pixman_data;
bitmap.hbitmap = NULL;
if (!mask->bitmap) {
return bitmap;
}
gdi_surface = (GdiCanvas *)canvas_get_surface_mask(&canvas->base, mask->bitmap);
if (gdi_surface) {
HBITMAP _bitmap;
_bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
if (!_bitmap) {
CANVAS_ERROR ("GetCurrentObject failed");
}
bitmap.dc = gdi_surface->dc;
bitmap.hbitmap = _bitmap;
bitmap.prev_hbitmap = (HBITMAP)0;
bitmap.cache = 0;
bitmap.from_surface = 1;
} else {
if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
return bitmap;
}
pixman_data = (PixmanData *)pixman_image_get_destroy_data (surface);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
bitmap.dc = create_compatible_dc();
bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, pixman_data->bitmap);
bitmap.hbitmap = pixman_data->bitmap;
ReleaseMutex(pixman_data->mutex);
bitmap.cache = 1;
} else if (!create_bitmap_from_pixman(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
surface, 0)) {
bitmap.hbitmap = NULL;
} else {
bitmap.cache = 0;
}
bitmap.from_surface = 0;
}
bitmap.flags = mask->flags;
bitmap.pos = mask->pos;
return bitmap;
}
static void gdi_draw_bitmap(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
HDC src_dc, struct BitmapData *bitmapmask, uint32_t rop3_val)
{
uint32_t rast_oper;
rast_oper = raster_ops[rop3_val];
if (!bitmapmask || !bitmapmask->hbitmap) {
if ((dest->right - dest->left) == (src->right - src->left) &&
(dest->bottom - dest->top) == (src->bottom - src->top)) {
if (!BitBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, src_dc, src->left, src->top, rast_oper)) {
CANVAS_ERROR("BitBlt failed");
}
} else {
if (!StretchBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, src_dc, src->left, src->top,
src->right - src->left, src->bottom - src->top, rast_oper)) {
CANVAS_ERROR("StretchBlt failed");
}
}
} else {
rast_oper = MAKEROP4(rast_oper, raster_ops[_rop3_dest]);
if (!MaskBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, src_dc, src->left, src->top,
bitmapmask->hbitmap, bitmapmask->pos.x, bitmapmask->pos.y,
rast_oper)) {
CANVAS_ERROR("MaskBlt failed");
}
}
}
static void gdi_draw_bitmap_redrop(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
HDC src_dc, struct BitmapData *bitmapmask,
uint16_t rop, int brush)
{
uint32_t rop3_val;
rop3_val = calc_rop3(rop, brush);
gdi_draw_bitmap(dest_dc, src, dest, src_dc, bitmapmask, rop3_val);
}
static void free_mask(struct BitmapData *bitmap)
{
if (bitmap->hbitmap) {
if (!bitmap->from_surface) {
release_bitmap(bitmap->dc, bitmap->hbitmap, bitmap->prev_hbitmap, bitmap->cache);
}
}
}
static void draw_str_mask_bitmap(struct GdiCanvas *canvas,
SpiceString *str, int n, SpiceRect *dest,
SpiceRect *src, SpiceBrush *brush)
{
pixman_image_t *surface;
struct BitmapData bitmap;
SpicePoint pos;
int dest_stride;
uint8_t *bitmap_data;
HBRUSH prev_hbrush;
HBRUSH hbrush;
RecurciveMutex *brush_lock;
bitmap.hbitmap = (HBITMAP)1;
if (!(surface = canvas_get_str_mask(&canvas->base, str, n, &pos))) {
CANVAS_ERROR("unable to canvas_get_str_mask");
return;
}
bitmap.from_surface = 0;
bitmap.cache = 0;
bitmap_data = create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap,
&bitmap.dc, NULL,
pixman_image_get_width(surface),
pixman_image_get_height(surface),
pixman_image_get_stride(surface), 32, 0);
if (!bitmap_data) {
return;
}
bitmap.flags = 0;
bitmap.pos.x = 0;
bitmap.pos.y = 0;
dest->left = pos.x;
dest->top = pos.y;
dest->right = pos.x + pixman_image_get_width(surface);
dest->bottom = pos.y + pixman_image_get_height(surface);
src->left = 0;
src->top = 0;
src->right = pixman_image_get_width(surface);
src->bottom = pixman_image_get_height(surface);
dest_stride = pixman_image_get_width(surface);
switch (n) {
case 1:
dest_stride = dest_stride / 8;
break;
case 4:
dest_stride = dest_stride / 2;
break;
case 32:
dest_stride = dest_stride * 4;
break;
default:
CANVAS_ERROR("unsupported bitmap bits size");
}
dest_stride = dest_stride + 3;
dest_stride = dest_stride & ~3;
hbrush = get_brush(canvas, brush, &brush_lock);
prev_hbrush = set_brush(bitmap.dc, hbrush, brush);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap(bitmap.dc, src, src, bitmap.dc, NULL, _rop3_brush);
} else {
gdi_draw_bitmap(bitmap.dc, src, src, bitmap.dc, NULL, _rop3_brush);
}
unset_brush(bitmap.dc, prev_hbrush);
copy_bitmap_alpha((uint8_t *)pixman_image_get_data(surface),
pixman_image_get_height(surface),
pixman_image_get_width(surface),
pixman_image_get_stride(surface),
bitmap_data, dest_stride, n);
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
RecurciveLock lock(*canvas->lock);
AlphaBlend(canvas->dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, bitmap.dc, src->left, src->top,
src->right - src->left, src->bottom - src->top, bf);
free_mask(&bitmap);
}
static void gdi_draw_image(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
pixman_image_t *image, struct BitmapData *bitmapmask, uint16_t rop,
int rotate)
{
HDC dc;
HBITMAP bitmap;
HBITMAP prev_bitmap;
create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
gdi_draw_bitmap_redrop(dest_dc, src, dest, dc, bitmapmask, rop, 0);
release_bitmap(dc, bitmap, prev_bitmap, 0);
}
static void gdi_draw_image_rop3(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
pixman_image_t *image, struct BitmapData *bitmapmask, uint8_t rop3,
int rotate)
{
HDC dc;
HBITMAP bitmap;
HBITMAP prev_bitmap;
create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
gdi_draw_bitmap(dest_dc, src, dest, dc, bitmapmask, rop3);
release_bitmap(dc, bitmap, prev_bitmap, 0);
}
static void gdi_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
HBRUSH prev_hbrush;
HBRUSH brush;
struct BitmapData bitmapmask;
RecurciveMutex *brush_lock;
RecurciveLock lock(*canvas->lock);
if (!(brush = get_brush(canvas, &fill->brush, &brush_lock))) {
CANVAS_ERROR("no braash");
return;
}
bitmapmask = get_mask_bitmap(canvas, &fill->mask);
set_clip(canvas, clip);
prev_hbrush = set_brush(canvas->dc, brush, &fill->brush);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
fill->rop_decriptor, fill->brush.type != SPICE_BRUSH_TYPE_NONE);
} else {
gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
fill->rop_decriptor, fill->brush.type != SPICE_BRUSH_TYPE_NONE);
}
free_mask(&bitmapmask);
unset_brush(canvas->dc, prev_hbrush);
}
static void gdi_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
GdiCanvas *gdi_surface;
pixman_image_t *surface;
struct BitmapData bitmapmask;
PixmanData *pixman_data;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, copy->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
bitmapmask = get_mask_bitmap(canvas, ©->mask);
set_scale_mode(canvas, copy->scale_mode);
set_clip(canvas, clip);
gdi_draw_bitmap_redrop(canvas->dc, ©->src_area, bbox, gdi_surface->dc,
&bitmapmask, copy->rop_decriptor, 0);
} else {
surface = canvas_get_image(&canvas->base, copy->src_bitmap, FALSE);
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, ©->mask);
set_scale_mode(canvas, copy->scale_mode);
set_clip(canvas, clip);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
gdi_draw_bitmap_redrop(canvas->dc, ©->src_area, bbox, dc,
&bitmapmask, copy->rop_decriptor, 0);
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
gdi_draw_image(canvas->dc, ©->src_area, bbox, surface, &bitmapmask,
copy->rop_decriptor, 0);
}
pixman_image_unref(surface);
}
free_mask(&bitmapmask);
}
static void gdi_canvas_put_image(SpiceCanvas *spice_canvas, HDC dc, const SpiceRect *dest, const uint8_t *src_data,
uint32_t src_width, uint32_t src_height, int src_stride,
const QRegion *clip)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
SpiceRect src;
src.top = 0;
src.bottom = src_height;
src.left = 0;
src.right = src_width;
int num_rects;
pixman_box32_t *rects;
RecurciveLock lock(*canvas->lock);
set_scale_mode(canvas, SPICE_IMAGE_SCALE_MODE_NEAREST);
if (clip) {
rects = pixman_region32_rectangles((pixman_region32_t*)clip, &num_rects);
if (num_rects == 0) {
return;
} else {
HRGN main_hrgn;
int i;
main_hrgn = CreateRectRgn(rects[0].x1, rects[0].y1, rects[0].x2,
rects[0].y2);
if (!main_hrgn) {
return;
}
for (i = 1; i < num_rects; i++) {
HRGN combaine_hrgn;
combaine_hrgn = CreateRectRgn(rects[i].x1, rects[i].y1,
rects[i].x2,
rects[i].y2);
if (!combaine_hrgn) {
CANVAS_ERROR("CreateRectRgn failed");
DeleteObject(main_hrgn);
return;
}
if (!CombineRgn(main_hrgn, main_hrgn, combaine_hrgn, RGN_OR)) {
CANVAS_ERROR("CombineRgn failed in put_image");
return;
}
DeleteObject(combaine_hrgn);
}
if (SelectClipRgn(canvas->dc, main_hrgn) == ERROR) {
CANVAS_ERROR("SelectClipRgn failed in put_image");
DeleteObject(main_hrgn);
return;
}
DeleteObject(main_hrgn);
}
} else {
SelectClipRgn(canvas->dc, NULL);
}
if (dc) {
gdi_draw_bitmap_redrop(canvas->dc, &src, dest, dc,
NULL, SPICE_ROPD_OP_PUT, 0);
} else {
pixman_image_t *image = pixman_image_create_bits(PIXMAN_a8r8g8b8, src_width, src_height,
(uint32_t *)src_data, src_stride);
gdi_draw_image(canvas->dc, &src, dest, image, NULL, SPICE_ROPD_OP_PUT, 0);
pixman_image_unref(image);
}
}
static void gdi_draw_bitmap_transparent(GdiCanvas *canvas, HDC dest_dc, const SpiceRect *src,
const SpiceRect *dest, HDC src_dc, uint32_t color)
{
TransparentBlt(dest_dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, src_dc, src->left, src->top,
src->right - src->left, src->bottom - src->top,
RGB(((uint8_t*)&color)[2], ((uint8_t*)&color)[1], ((uint8_t*)&color)[0]));
}
static void gdi_draw_image_transparent(GdiCanvas *canvas, HDC dest_dc, const SpiceRect *src,
const SpiceRect *dest, pixman_image_t *image,
uint32_t color, int rotate)
{
HDC dc;
HBITMAP bitmap;
HBITMAP prev_bitmap;
create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
gdi_draw_bitmap_transparent(canvas, dest_dc, src, dest, dc, color);
release_bitmap(dc, bitmap, prev_bitmap, 0);
}
static void gdi_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip,
SpiceTransparent* transparent)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
GdiCanvas *gdi_surface;
pixman_image_t *surface;
PixmanData *pixman_data;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, transparent->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
set_clip(canvas, clip);
gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox,
gdi_surface->dc, transparent->true_color);
} else {
surface = canvas_get_image(&canvas->base, transparent->src_bitmap, FALSE);
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
set_clip(canvas, clip);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
transparent->true_color);
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, surface,
transparent->true_color, 0);
}
pixman_image_unref(surface);
}
}
static void gdi_draw_bitmap_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
HDC src_dc, uint8_t alpha, int use_bitmap_alpha)
{
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = alpha;
if (use_bitmap_alpha) {
bf.AlphaFormat = AC_SRC_ALPHA;
} else {
bf.AlphaFormat = 0;
}
if (!AlphaBlend(dest_dc, dest->left, dest->top, dest->right - dest->left,
dest->bottom - dest->top, src_dc, src->left, src->top,
src->right - src->left, src->bottom - src->top, bf)) {
CANVAS_ERROR("AlphaBlend failed");
}
}
static void gdi_draw_image_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
pixman_image_t *image, uint8_t alpha,
int rotate, int use_bitmap_alpha)
{
HDC dc;
HBITMAP bitmap;
HBITMAP prev_bitmap;
create_bitmap_from_pixman(&bitmap, &prev_bitmap, &dc, image, rotate);
gdi_draw_bitmap_alpha(dest_dc, src, dest, dc, alpha, use_bitmap_alpha);
release_bitmap(dc, bitmap, prev_bitmap, 0);
}
static void gdi_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
GdiCanvas *gdi_surface;
pixman_image_t *surface;
PixmanData *pixman_data;
int use_bitmap_alpha;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, alpha_blend->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
set_clip(canvas, clip);
use_bitmap_alpha = alpha_blend->alpha_flags & SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA;
gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, gdi_surface->dc,
alpha_blend->alpha, use_bitmap_alpha);
} else {
surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap, TRUE);
use_bitmap_alpha = pixman_image_get_depth(surface) == 32;
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
set_clip(canvas, clip);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha,
use_bitmap_alpha);
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, surface,
alpha_blend->alpha, 0, use_bitmap_alpha);
}
pixman_image_unref(surface);
}
}
static void gdi_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
{
GdiCanvas *gdi_surface;
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
pixman_image_t *surface;
struct BitmapData bitmapmask;
PixmanData *pixman_data;
HBRUSH prev_hbrush;
HBRUSH hbrush;
uint8_t rop3;
RecurciveMutex *brush_lock;
rop3 = calc_rop3_src_brush(opaque->rop_decriptor);
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, opaque->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
hbrush = get_brush(canvas, &opaque->brush, &brush_lock);
set_scale_mode(canvas, opaque->scale_mode);
set_clip(canvas, clip);
prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, gdi_surface->dc, &bitmapmask, rop3);
} else {
gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, gdi_surface->dc, &bitmapmask, rop3);
}
unset_brush(canvas->dc, prev_hbrush);
} else {
surface = canvas_get_image(&canvas->base, opaque->src_bitmap, FALSE);
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
hbrush = get_brush(canvas, &opaque->brush, &brush_lock);
set_scale_mode(canvas, opaque->scale_mode);
set_clip(canvas, clip);
prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
} else {
gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
}
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, surface, &bitmapmask, rop3, 0);
} else {
gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, surface, &bitmapmask, rop3, 0);
}
}
unset_brush(canvas->dc, prev_hbrush);
pixman_image_unref(surface);
}
free_mask(&bitmapmask);
}
static void gdi_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
{
GdiCanvas *gdi_surface;
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
pixman_image_t *surface;
struct BitmapData bitmapmask;
PixmanData *pixman_data;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, blend->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
bitmapmask = get_mask_bitmap(canvas, &blend->mask);
set_scale_mode(canvas, blend->scale_mode);
set_clip(canvas, clip);
gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, gdi_surface->dc,
&bitmapmask, blend->rop_decriptor, 0);
} else {
surface = canvas_get_image(&canvas->base, blend->src_bitmap, FALSE);
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, &blend->mask);
set_scale_mode(canvas, blend->scale_mode);
set_clip(canvas, clip);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
&bitmapmask, blend->rop_decriptor, 0);
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
gdi_draw_image(canvas->dc, &blend->src_area, bbox, surface,
&bitmapmask, blend->rop_decriptor, 0);
}
pixman_image_unref(surface);
}
free_mask(&bitmapmask);
}
static void gdi_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
struct BitmapData bitmapmask;
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
set_clip(canvas, clip);
gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x0);
free_mask(&bitmapmask);
}
static void gdi_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
struct BitmapData bitmapmask;
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, &invers->mask);
set_clip(canvas, clip);
gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x55);
free_mask(&bitmapmask);
}
static void gdi_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
struct BitmapData bitmapmask;
RecurciveLock lock(*canvas->lock);
bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
set_clip(canvas, clip);
gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0xff);
free_mask(&bitmapmask);
}
static void gdi_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
{
GdiCanvas *gdi_surface;
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
pixman_image_t *surface;
struct BitmapData bitmapmask;
HBRUSH prev_hbrush;
HBRUSH hbrush;
PixmanData *pixman_data;
RecurciveMutex *brush_lock;
gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, rop3->src_bitmap);
if (gdi_surface) {
RecurciveLock lock(*canvas->lock);
RecurciveLock s_lock(*gdi_surface->lock);
hbrush = get_brush(canvas, &rop3->brush, &brush_lock);
bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
set_scale_mode(canvas, rop3->scale_mode);
set_clip(canvas, clip);
prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, gdi_surface->dc,
&bitmapmask, rop3->rop3);
} else {
gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, gdi_surface->dc,
&bitmapmask, rop3->rop3);
}
unset_brush(canvas->dc, prev_hbrush);
} else {
surface = canvas_get_image(&canvas->base, rop3->src_bitmap, FALSE);
pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
RecurciveLock lock(*canvas->lock);
hbrush = get_brush(canvas, &rop3->brush, &brush_lock);
bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
set_scale_mode(canvas, rop3->scale_mode);
set_clip(canvas, clip);
prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
HDC dc;
HBITMAP prev_bitmap;
dc = create_compatible_dc();
prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
&bitmapmask, rop3->rop3);
} else {
gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
&bitmapmask, rop3->rop3);
}
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, surface, &bitmapmask, rop3->rop3, 0);
} else {
gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, surface, &bitmapmask, rop3->rop3, 0);
}
}
unset_brush(canvas->dc, prev_hbrush);
pixman_image_unref(surface);
}
free_mask(&bitmapmask);
}
static void gdi_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
RecurciveLock lock(*canvas->lock);
set_clip(canvas, clip);
BitBlt(canvas->dc, bbox->left, bbox->top, bbox->right - bbox->left,
bbox->bottom - bbox->top, canvas->dc, src_pos->x, src_pos->y, SRCCOPY);
}
static void gdi_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
SpiceString *str;
RecurciveMutex *brush_lock;
RecurciveLock lock(*canvas->lock);
set_clip(canvas, clip);
lock.unlock();
if (!rect_is_empty(&text->back_area)) {
HBRUSH prev_hbrush;
HBRUSH hbrush;
RecurciveLock lock(*canvas->lock);
hbrush = get_brush(canvas, &text->back_brush, &brush_lock);
prev_hbrush = set_brush(canvas->dc, hbrush, &text->back_brush);
if (brush_lock) {
RecurciveLock b_lock(*brush_lock);
gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
text->back_mode, 1);
} else {
gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
text->back_mode, 1);
}
unset_brush(canvas->dc, prev_hbrush);
}
str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
SpiceRect dest;
SpiceRect src;
draw_str_mask_bitmap(canvas, str, 1, &dest, &src, &text->fore_brush);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
SpiceRect dest;
SpiceRect src;
draw_str_mask_bitmap(canvas, str, 4, &dest, &src, &text->fore_brush);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
WARN("untested path A8 glyphs, doing nothing");
if (0) {
SpiceRect dest;
SpiceRect src;
draw_str_mask_bitmap(canvas, str, 8, &dest, &src, &text->fore_brush);
}
} else {
WARN("untested path vector glyphs, doing nothing");
if (0) {
}
}
}
static int get_join_style(uint8_t join_style)
{
switch (join_style) {
case SPICE_LINE_JOIN_ROUND:
return PS_JOIN_ROUND;
case SPICE_LINE_JOIN_BEVEL:
return PS_JOIN_BEVEL;
case SPICE_LINE_JOIN_MITER:
return PS_JOIN_MITER;
default:
CANVAS_ERROR("bad join style %d", join_style);
}
}
static int get_cap(int end_style)
{
switch (end_style) {
case SPICE_LINE_CAP_ROUND:
return PS_ENDCAP_ROUND;
case SPICE_LINE_CAP_SQUARE:
return PS_ENDCAP_SQUARE;
case SPICE_LINE_CAP_BUTT:
return PS_ENDCAP_FLAT;
default:
CANVAS_ERROR("bad end style %d", end_style);
}
}
static uint32_t *gdi_get_userstyle(GdiCanvas *canvas, uint8_t nseg, SPICE_ADDRESS addr, int start_is_gap)
{
SPICE_FIXED28_4* style = (SPICE_FIXED28_4*)SPICE_GET_ADDRESS(addr);
double offset = 0;
uint32_t *local_style;
int i;
access_test(&canvas->base, style, nseg * sizeof(*style));
if (nseg == 0) {
CANVAS_ERROR("bad nseg");
}
local_style = spice_new(uint32_t , nseg);
if (start_is_gap) {
offset = (uint32_t)fix_to_double(*style);
local_style[nseg - 1] = (uint32_t)fix_to_double(*style);
style++;
for (i = 0; i < nseg - 1; i++, style++) {
local_style[i] = (uint32_t)fix_to_double(*style);
}
} else {
for (i = 0; i < nseg; i++, style++) {
local_style[i] = (uint32_t)fix_to_double(*style);
}
}
return local_style;
}
static void gdi_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
HPEN hpen;
HPEN prev_hpen;
LOGBRUSH logbrush;
int ps_join = 0;
int line_cap = 0;
uint32_t *user_style = NULL;
pixman_image_t *surface = NULL;
if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
surface = canvas_get_image(&canvas->base, stroke->brush.u.pattern.pat, FALSE);
}
RecurciveLock lock(*canvas->lock);
set_clip(canvas, clip);
switch (stroke->fore_mode) {
case SPICE_ROPD_OP_WHITENESS:
SetROP2(canvas->dc, R2_WHITE); //0
break;
case SPICE_ROPD_OP_BLACKNESS:
SetROP2(canvas->dc, R2_BLACK); //1
break;
case SPICE_ROPD_OP_INVERS:
SetROP2(canvas->dc, R2_NOT); //Dn
break;
case SPICE_ROPD_OP_PUT:
SetROP2(canvas->dc, R2_COPYPEN); //P
break;
case SPICE_ROPD_OP_OR:
SetROP2(canvas->dc, R2_MERGEPEN); //DPo
break;
case SPICE_ROPD_OP_XOR:
SetROP2(canvas->dc, R2_XORPEN); //DPx
break;
case SPICE_ROPD_OP_AND:
SetROP2(canvas->dc, R2_MASKPEN); //DPa
break;
case SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_PUT: //Pn
SetROP2(canvas->dc, R2_NOTCOPYPEN);
break;
case SPICE_ROPD_OP_XOR | SPICE_ROPD_INVERS_RES:
SetROP2(canvas->dc, R2_NOTXORPEN); //DPxn
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_RES:
SetROP2(canvas->dc, R2_NOTMERGEPEN); //DPon
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES:
SetROP2(canvas->dc, R2_NOTMASKPEN); //DPan
break;
case SPICE_ROPD_INVERS_DEST | SPICE_ROPD_OP_AND:
SetROP2(canvas->dc, R2_MASKPENNOT); //PDna
break;
case SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_AND:
SetROP2(canvas->dc, R2_MASKNOTPEN); //DPna
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_BRUSH:
SetROP2(canvas->dc, R2_MERGENOTPEN); //DPno
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_DEST:
SetROP2(canvas->dc, R2_MERGEPENNOT); //PDno
break;
default:
SetROP2(canvas->dc, R2_NOP); //D
}
if (stroke->brush.type == SPICE_BRUSH_TYPE_SOLID) {
logbrush.lbStyle = BS_SOLID | DIB_RGB_COLORS;
logbrush.lbHatch = 0;
logbrush.lbColor = get_color_ref(canvas, stroke->brush.u.color);
} else if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
#if 0
struct {
BITMAPINFO inf;
RGBQUAD palette[255];
} bitmap_info;
GdiImage image;
#endif
//CANVAS_ERROR("untested path stroke brush with pattern");
#if 0
ASSERT(surface)
surface_to_image(surface, &image);
memset(&bitmap_info, 0, sizeof(bitmap_info));
bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
bitmap_info.inf.bmiHeader.biWidth = image.width;
if (image.stride < 0) {
bitmap_info.inf.bmiHeader.biHeight = image.height;
} else {
bitmap_info.inf.bmiHeader.biHeight = -image.height;
}
bitmap_info.inf.bmiHeader.biPlanes = 1;
bitmap_info.inf.bmiHeader.biBitCount = 32;
bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
if (image.stride < 0) {
logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
image.height * -image.stride + sizeof(BITMAPINFO));
if (!logbrush.lbHatch) {
CANVAS_ERROR("GlobalAlloc failed");
}
copy_bitmap(image.pixels - (image.height - 1) * -image.stride,
image.height, -image.stride,
(uint8_t *)logbrush.lbHatch, image.width);
} else {
logbrush.lbHatch = (LONG)GlobalAlloc(GMEM_MOVEABLE,
image.height * image.stride + sizeof(BITMAPINFO));
if (!logbrush.lbHatch) {
CANVAS_ERROR("GlobalAlloc failed");
}
copy_bitmap(image.pixels, image.height, image.stride,
(uint8_t *)logbrush.lbHatch, image.width);
}
memcpy((void *)logbrush.lbHatch, &bitmap_info.inf, sizeof(BITMAPINFO));
logbrush.lbStyle = BS_DIBPATTERN | DIB_RGB_COLORS;
logbrush.lbColor = 0;
#endif
pixman_image_unref(surface);
}
#if 0
ps_join = get_join_style(stroke->attr.join_style);
line_cap = get_cap(stroke->attr.end_style);
SetMiterLimit(canvas->dc, (FLOAT)fix_to_double(stroke->attr.miter_limit), &old_miter);
#endif
if (stroke->attr.flags & SPICE_LINE_ATTR_STYLED) {
user_style = gdi_get_userstyle(canvas, stroke->attr.style_nseg,
stroke->attr.style,
!!(stroke->attr.flags & SPICE_LINE_ATTR_STARTGAP));
hpen = ExtCreatePen(PS_GEOMETRIC | ps_join | line_cap | PS_USERSTYLE,
(uint32_t)fix_to_double(stroke->attr.width),
&logbrush, stroke->attr.style_nseg, (DWORD *)user_style);
} else {
hpen = ExtCreatePen(PS_GEOMETRIC | ps_join | line_cap,
(uint32_t)fix_to_double(stroke->attr.width),
&logbrush, 0, NULL);
}
prev_hpen = (HPEN)SelectObject(canvas->dc, hpen);
set_path(canvas, SPICE_GET_ADDRESS(stroke->path));
StrokePath(canvas->dc);
SelectObject(canvas->dc, prev_hpen);
DeleteObject(hpen);
#if 0
if (stroke->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
GlobalFree((HGLOBAL)logbrush.lbHatch);
}
#endif
if (user_style) {
free(user_style);
}
}
static void gdi_canvas_clear(SpiceCanvas *spice_canvas)
{
}
static void gdi_canvas_set_access_params(SpiceCanvas *spice_canvas, unsigned long base, unsigned long max)
{
#ifdef SW_CANVAS_ACCESS_TEST
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
__canvas_set_access_params(&canvas->base, base, max);
#endif
}
static void gdi_canvas_destroy(SpiceCanvas *spice_canvas)
{
GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
if (!canvas) {
return;
}
canvas_base_destroy(&canvas->base);
free(canvas);
}
static int need_init = 1;
static SpiceCanvasOps gdi_canvas_ops;
SpiceCanvas *gdi_canvas_create(int width, int height,
HDC dc, RecurciveMutex* lock, 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
)
{
GdiCanvas *canvas;
int init_ok;
if (need_init) {
return NULL;
}
canvas = spice_new0(GdiCanvas, 1);
init_ok = canvas_base_init(&canvas->base, &gdi_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);
canvas->dc = dc;
canvas->lock = lock;
return (SpiceCanvas *)canvas;
}
void gdi_canvas_init() //unsafe global function
{
if (!need_init) {
return;
}
need_init = 0;
canvas_base_init_ops(&gdi_canvas_ops);
gdi_canvas_ops.draw_fill = gdi_canvas_draw_fill;
gdi_canvas_ops.draw_copy = gdi_canvas_draw_copy;
gdi_canvas_ops.draw_opaque = gdi_canvas_draw_opaque;
gdi_canvas_ops.copy_bits = gdi_canvas_copy_bits;
gdi_canvas_ops.draw_text = gdi_canvas_draw_text;
gdi_canvas_ops.draw_stroke = gdi_canvas_draw_stroke;
gdi_canvas_ops.draw_rop3 = gdi_canvas_draw_rop3;
gdi_canvas_ops.draw_blend = gdi_canvas_draw_blend;
gdi_canvas_ops.draw_blackness = gdi_canvas_draw_blackness;
gdi_canvas_ops.draw_whiteness = gdi_canvas_draw_whiteness;
gdi_canvas_ops.draw_invers = gdi_canvas_draw_invers;
gdi_canvas_ops.draw_transparent = gdi_canvas_draw_transparent;
gdi_canvas_ops.draw_alpha_blend = gdi_canvas_draw_alpha_blend;
gdi_canvas_ops.put_image = gdi_canvas_put_image;
gdi_canvas_ops.clear = gdi_canvas_clear;
gdi_canvas_ops.set_access_params = gdi_canvas_set_access_params;
gdi_canvas_ops.destroy = gdi_canvas_destroy;
rop3_init();
}