mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 22:48:19 +00:00
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
1866 lines
59 KiB
C
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, ©->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_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, ©->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_descriptor, 0);
|
|
SelectObject(dc, prev_bitmap);
|
|
DeleteObject(dc);
|
|
ReleaseMutex(pixman_data->mutex);
|
|
} else {
|
|
gdi_draw_image(canvas->dc, ©->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();
|
|
}
|
|
|