spice/common/gdi_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

1866 lines
59 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 <windows.h>
#include <wingdi.h>
#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, SpicePath *s)
{
unsigned int i;
for (i = 0; i < s->num_segments; i++) {
SpicePathSeg* seg = s->segments[0];
SpicePointFix* point = seg->points;
SpicePointFix* end_point = point + seg->count;
if (seg->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 (seg->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 (seg->flags & SPICE_PATH_END) {
if (seg->flags & SPICE_PATH_CLOSE) {
if (!CloseFigure(canvas->dc)) {
CANVAS_ERROR("CloseFigure failed");
}
}
if (!EndPath(canvas->dc)) {
CANVAS_ERROR("EndPath failed");
}
}
}
}
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 = clip->rects->num_rects;
SpiceRect *now = clip->rects->rects;
SpiceRect *end = now + n;
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;
}
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 &= ((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 = NULL;
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_descriptor, fill->brush.type != SPICE_BRUSH_TYPE_NONE);
} else {
gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
fill->rop_descriptor, 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, &copy->mask);
set_scale_mode(canvas, copy->scale_mode);
set_clip(canvas, clip);
gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, gdi_surface->dc,
&bitmapmask, copy->rop_descriptor, 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, &copy->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, &copy->src_area, bbox, dc,
&bitmapmask, copy->rop_descriptor, 0);
SelectObject(dc, prev_bitmap);
DeleteObject(dc);
ReleaseMutex(pixman_data->mutex);
} else {
gdi_draw_image(canvas->dc, &copy->src_area, bbox, surface, &bitmapmask,
copy->rop_descriptor, 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, SpiceAlphaBlend* 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_descriptor);
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_descriptor, 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_descriptor, 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_descriptor, 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 uint32_t *gdi_get_userstyle(GdiCanvas *canvas, uint8_t nseg, SPICE_FIXED28_4* style, int start_is_gap)
{
double offset = 0;
uint32_t *local_style;
int i;
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;
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 (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
user_style = gdi_get_userstyle(canvas, stroke->attr.style_nseg,
stroke->attr.style,
!!(stroke->attr.flags & SPICE_LINE_FLAGS_START_WITH_GAP));
hpen = ExtCreatePen(PS_COSMETIC | PS_USERSTYLE,
1,
&logbrush, stroke->attr.style_nseg, (DWORD *)user_style);
} else {
hpen = ExtCreatePen(PS_COSMETIC,
1,
&logbrush, 0, NULL);
}
prev_hpen = (HPEN)SelectObject(canvas->dc, hpen);
set_path(canvas, 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_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
, SpiceZlibDecoder *zlib_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
, zlib_decoder);
canvas->dc = dc;
canvas->lock = lock;
return (SpiceCanvas *)canvas;
}
void gdi_canvas_init(void) //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.destroy = gdi_canvas_destroy;
rop3_init();
}