mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-23 14:18:10 +02:00
Handling clip as part of the surface state, as opposed to being part of the operation state, is cumbersome and a hindrance to providing true proxy surface support. For example, the clip must be copied from the surface onto the fallback image, but this was forgotten causing undue hassle in each backend. Another example is the contortion the meta surface endures to ensure the clip is correctly recorded. By contrast passing the clip along with the operation is quite simple and enables us to write generic handlers for providing surface wrappers. (And in the future, we should be able to write more esoteric wrappers, e.g. automatic 2x FSAA, trivially.) In brief, instead of the surface automatically applying the clip before calling the backend, the backend can call into a generic helper to apply clipping. For raster surfaces, clip regions are handled automatically as part of the composite interface. For vector surfaces, a clip helper is introduced to replay and callback into an intersect_clip_path() function as necessary. Whilst this is not primarily a performance related change (the change should just move the computation of the clip from the moment it is applied by the user to the moment it is required by the backend), it is important to track any potential regression: ppc: Speedups ======== image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup ▌ image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup ▎ image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup ▏ image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup ▏ Slowdowns ========= image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown ▏
1335 lines
47 KiB
C
1335 lines
47 KiB
C
/* vim: set sw=4 sts=4 et cin: */
|
|
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright (c) 2005-2006 netlabs.org
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* The Original Code is the cairo graphics library.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Doodle <doodle@scenergy.dfmk.hu>
|
|
*
|
|
* Contributor(s):
|
|
* Peter Weilbacher <mozilla@Weilbacher.org>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-os2-private.h"
|
|
|
|
#if CAIRO_HAS_FC_FONT
|
|
#include <fontconfig/fontconfig.h>
|
|
#endif
|
|
|
|
#include <float.h>
|
|
#ifdef BUILD_CAIRO_DLL
|
|
# include "cairo-os2.h"
|
|
# ifndef __WATCOMC__
|
|
# include <emx/startup.h>
|
|
# endif
|
|
#endif
|
|
|
|
/*
|
|
* Here comes the extra API for the OS/2 platform. Currently it consists
|
|
* of two extra functions, the cairo_os2_init() and the
|
|
* cairo_os2_fini(). Both of them are called automatically if
|
|
* Cairo is compiled to be a DLL file, but you have to call them before
|
|
* using the Cairo API if you link to Cairo statically!
|
|
*
|
|
* You'll also find the code in here which deals with DLL initialization
|
|
* and termination, if the code is built to be a DLL.
|
|
* (if BUILD_CAIRO_DLL is defined)
|
|
*/
|
|
|
|
/* Initialization counter: */
|
|
static int cairo_os2_initialization_count = 0;
|
|
|
|
static void inline
|
|
DisableFPUException (void)
|
|
{
|
|
unsigned short usCW;
|
|
|
|
/* Some OS/2 PM API calls modify the FPU Control Word,
|
|
* but forget to restore it.
|
|
*
|
|
* This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
|
|
* so to be sure, we disable Invalid Opcode FPU exception
|
|
* before using FPU stuffs.
|
|
*/
|
|
usCW = _control87 (0, 0);
|
|
usCW = usCW | EM_INVALID | 0x80;
|
|
_control87 (usCW, MCW_EM | 0x80);
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_init:
|
|
*
|
|
* Initializes the Cairo library. This function is automatically called if
|
|
* Cairo was compiled to be a DLL (however it's not a problem if it's called
|
|
* multiple times). But if you link to Cairo statically, you have to call it
|
|
* once to set up Cairo's internal structures and mutexes.
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
cairo_public void
|
|
cairo_os2_init (void)
|
|
{
|
|
/* This may initialize some stuffs, like create mutex semaphores etc.. */
|
|
|
|
cairo_os2_initialization_count++;
|
|
if (cairo_os2_initialization_count > 1) return;
|
|
|
|
DisableFPUException ();
|
|
|
|
#if CAIRO_HAS_FC_FONT
|
|
/* Initialize FontConfig */
|
|
FcInit ();
|
|
#endif
|
|
|
|
CAIRO_MUTEX_INITIALIZE ();
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_fini:
|
|
*
|
|
* Uninitializes the Cairo library. This function is automatically called if
|
|
* Cairo was compiled to be a DLL (however it's not a problem if it's called
|
|
* multiple times). But if you link to Cairo statically, you have to call it
|
|
* once to shut down Cairo, to let it free all the resources it has allocated.
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
cairo_public void
|
|
cairo_os2_fini (void)
|
|
{
|
|
/* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */
|
|
|
|
if (cairo_os2_initialization_count <= 0) return;
|
|
cairo_os2_initialization_count--;
|
|
if (cairo_os2_initialization_count > 0) return;
|
|
|
|
DisableFPUException ();
|
|
|
|
cairo_debug_reset_static_data ();
|
|
|
|
#if CAIRO_HAS_FC_FONT
|
|
# if HAVE_FCFINI
|
|
/* Uninitialize FontConfig */
|
|
FcFini ();
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef __WATCOMC__
|
|
/* It can happen that the libraries we use have memory leaks,
|
|
* so there are still memory chunks allocated at this point.
|
|
* In these cases, Watcom might still have a bigger memory chunk,
|
|
* called "the heap" allocated from the OS.
|
|
* As we want to minimize the memory we lose from the point of
|
|
* view of the OS, we call this function to shrink that heap
|
|
* as much as possible.
|
|
*/
|
|
_heapshrink ();
|
|
#else
|
|
/* GCC has a heapmin function that approximately corresponds to
|
|
* what the Watcom function does
|
|
*/
|
|
_heapmin ();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* This function calls the allocation function depending on which
|
|
* method was compiled into the library: it can be native allocation
|
|
* (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free).
|
|
* Actually, for pixel buffers that we use this function for, cairo
|
|
* uses _cairo_malloc_abc, so we use that here, too. And use the
|
|
* change to check the size argument
|
|
*/
|
|
void *_buffer_alloc (size_t a, size_t b, const unsigned int size)
|
|
{
|
|
/* check length like in the _cairo_malloc_abc macro, but we can leave
|
|
* away the unsigned casts as our arguments are unsigned already
|
|
*/
|
|
size_t nbytes = b &&
|
|
a >= INT32_MAX / b ? 0 : size &&
|
|
a*b >= INT32_MAX / size ? 0 : a * b * size;
|
|
void *buffer = NULL;
|
|
#ifdef OS2_USE_PLATFORM_ALLOC
|
|
APIRET rc = NO_ERROR;
|
|
|
|
rc = DosAllocMem ((PPVOID)&buffer,
|
|
nbytes,
|
|
#ifdef OS2_HIGH_MEMORY /* only if compiled with high-memory support, */
|
|
OBJ_ANY | /* we can allocate anywhere! */
|
|
#endif
|
|
PAG_READ | PAG_WRITE | PAG_COMMIT);
|
|
if (rc != NO_ERROR) {
|
|
/* should there for some reason be another error, let's return
|
|
* a null surface and free the buffer again, because that's
|
|
* how a malloc failure would look like
|
|
*/
|
|
if (rc != ERROR_NOT_ENOUGH_MEMORY && buffer) {
|
|
DosFreeMem (buffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
#else
|
|
buffer = malloc (nbytes);
|
|
#endif
|
|
|
|
/* This does not seem to be needed, malloc'd space is usually
|
|
* already zero'd out!
|
|
*/
|
|
/*
|
|
* memset (buffer, 0x00, nbytes);
|
|
*/
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/*
|
|
* This function selects the free function depending on which
|
|
* allocation method was compiled into the library
|
|
*/
|
|
void _buffer_free (void *buffer)
|
|
{
|
|
#ifdef OS2_USE_PLATFORM_ALLOC
|
|
DosFreeMem (buffer);
|
|
#else
|
|
free (buffer);
|
|
#endif
|
|
}
|
|
|
|
/* XXX
|
|
* The cairo_os2_ini() and cairo_os2_fini() functions should be removed and
|
|
* the LibMain code moved to cairo-system.c. It should also call
|
|
* cairo_debug_reset_static_data() instead of duplicating its logic...
|
|
*/
|
|
|
|
#ifdef BUILD_CAIRO_DLL
|
|
/* The main DLL entry for DLL initialization and uninitialization */
|
|
/* Only include this code if we're about to build a DLL. */
|
|
|
|
#ifdef __WATCOMC__
|
|
unsigned _System
|
|
LibMain (unsigned hmod,
|
|
unsigned termination)
|
|
#else
|
|
unsigned long _System
|
|
_DLL_InitTerm (unsigned long hModule,
|
|
unsigned long termination)
|
|
#endif
|
|
{
|
|
if (termination) {
|
|
/* Unloading the DLL */
|
|
cairo_os2_fini ();
|
|
|
|
#ifndef __WATCOMC__
|
|
/* Uninitialize RTL of GCC */
|
|
__ctordtorTerm ();
|
|
_CRT_term ();
|
|
#endif
|
|
return 1;
|
|
} else {
|
|
/* Loading the DLL */
|
|
#ifndef __WATCOMC__
|
|
/* Initialize RTL of GCC */
|
|
if (_CRT_init () != 0)
|
|
return 0;
|
|
__ctordtorInit ();
|
|
#endif
|
|
|
|
cairo_os2_init ();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#endif /* BUILD_CAIRO_DLL */
|
|
|
|
/*
|
|
* The following part of the source file contains the code which might
|
|
* be called the "core" of the OS/2 backend support. This contains the
|
|
* OS/2 surface support functions and structures.
|
|
*/
|
|
|
|
/* Forward declaration */
|
|
static const cairo_surface_backend_t cairo_os2_surface_backend;
|
|
|
|
/* Unpublished API:
|
|
* GpiEnableYInversion = PMGPI.723
|
|
* GpiQueryYInversion = PMGPI.726
|
|
* BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
|
|
* LONG APIENTRY GpiQueryYInversion (HPS hps);
|
|
*/
|
|
BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
|
|
LONG APIENTRY GpiQueryYInversion (HPS hps);
|
|
|
|
#ifdef __WATCOMC__
|
|
/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */
|
|
LONG APIENTRY GpiDrawBits (HPS hps,
|
|
PVOID pBits,
|
|
PBITMAPINFO2 pbmiInfoTable,
|
|
LONG lCount,
|
|
PPOINTL aptlPoints,
|
|
LONG lRop,
|
|
ULONG flOptions);
|
|
#endif
|
|
|
|
static void
|
|
_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
|
|
HPS hps_begin_paint,
|
|
PRECTL prcl_begin_paint_rect)
|
|
{
|
|
POINTL aptlPoints[4];
|
|
LONG lOldYInversion, rc = GPI_OK;
|
|
|
|
/* Enable Y Inversion for the HPS, so the
|
|
* GpiDrawBits will work with upside-top image, not with upside-down image!
|
|
*/
|
|
lOldYInversion = GpiQueryYInversion (hps_begin_paint);
|
|
GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
|
|
|
|
/* Target coordinates (Noninclusive) */
|
|
aptlPoints[0].x = prcl_begin_paint_rect->xLeft;
|
|
aptlPoints[0].y = prcl_begin_paint_rect->yBottom;
|
|
|
|
aptlPoints[1].x = prcl_begin_paint_rect->xRight-1;
|
|
aptlPoints[1].y = prcl_begin_paint_rect->yTop-1;
|
|
|
|
/* Source coordinates (Inclusive) */
|
|
aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
|
|
aptlPoints[2].y = prcl_begin_paint_rect->yBottom;
|
|
|
|
aptlPoints[3].x = prcl_begin_paint_rect->xRight;
|
|
aptlPoints[3].y = (prcl_begin_paint_rect->yTop);
|
|
|
|
/* Some extra checking for limits
|
|
* (Dunno if really needed, but had some crashes sometimes without it,
|
|
* while developing the code...)
|
|
*/
|
|
{
|
|
int i;
|
|
for (i = 0; i < 4; i++) {
|
|
if (aptlPoints[i].x < 0)
|
|
aptlPoints[i].x = 0;
|
|
if (aptlPoints[i].y < 0)
|
|
aptlPoints[i].y = 0;
|
|
if (aptlPoints[i].x > (LONG) surface->bitmap_info.cx)
|
|
aptlPoints[i].x = (LONG) surface->bitmap_info.cx;
|
|
if (aptlPoints[i].y > (LONG) surface->bitmap_info.cy)
|
|
aptlPoints[i].y = (LONG) surface->bitmap_info.cy;
|
|
}
|
|
}
|
|
|
|
/* Debug code to draw rectangle limits */
|
|
#if 0
|
|
{
|
|
int x, y;
|
|
unsigned char *pixels;
|
|
|
|
pixels = surface->pixels;
|
|
for (x = 0; x < surface->bitmap_info.cx; x++) {
|
|
for (y = 0; y < surface->bitmap_info.cy; y++) {
|
|
if ((x == 0) ||
|
|
(y == 0) ||
|
|
(x == y) ||
|
|
(x >= surface->bitmap_info.cx-1) ||
|
|
(y >= surface->bitmap_info.cy-1))
|
|
{
|
|
pixels[y*surface->bitmap_info.cx*4+x*4] = 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
rc = GpiDrawBits (hps_begin_paint,
|
|
surface->pixels,
|
|
&(surface->bitmap_info),
|
|
4,
|
|
aptlPoints,
|
|
ROP_SRCCOPY,
|
|
BBO_IGNORE);
|
|
|
|
if (rc != GPI_OK) {
|
|
/* if GpiDrawBits () failed then this is most likely because the
|
|
* display driver could not handle a 32bit bitmap. So we need to
|
|
* - create a buffer that only contains 3 bytes per pixel
|
|
* - change the bitmap info header to contain 24bit
|
|
* - pass the new buffer to GpiDrawBits () again
|
|
* - clean up the new buffer
|
|
*/
|
|
BITMAPINFOHEADER2 bmpheader;
|
|
unsigned char *pchPixBuf, *pchPixSource;
|
|
void *pBufStart;
|
|
ULONG ulPixels;
|
|
|
|
/* allocate temporary pixel buffer */
|
|
pchPixBuf = (unsigned char *) _buffer_alloc (surface->bitmap_info.cy,
|
|
surface->bitmap_info.cx,
|
|
3);
|
|
pchPixSource = surface->pixels; /* start at beginning of pixel buffer */
|
|
pBufStart = pchPixBuf; /* remember beginning of the new pixel buffer */
|
|
|
|
/* copy the first three bytes for each pixel but skip over the fourth */
|
|
for (ulPixels = 0; ulPixels < surface->bitmap_info.cx * surface->bitmap_info.cy; ulPixels++)
|
|
{
|
|
/* copy BGR from source buffer */
|
|
*pchPixBuf++ = *pchPixSource++;
|
|
*pchPixBuf++ = *pchPixSource++;
|
|
*pchPixBuf++ = *pchPixSource++;
|
|
pchPixSource++; /* jump over alpha channel in source buffer */
|
|
}
|
|
|
|
/* jump back to start of the buffer for display and cleanup */
|
|
pchPixBuf = pBufStart;
|
|
|
|
/* set up the bitmap header, but this time with 24bit depth only */
|
|
memset (&bmpheader, 0, sizeof (bmpheader));
|
|
bmpheader.cbFix = sizeof (BITMAPINFOHEADER2);
|
|
bmpheader.cx = surface->bitmap_info.cx;
|
|
bmpheader.cy = surface->bitmap_info.cy;
|
|
bmpheader.cPlanes = surface->bitmap_info.cPlanes;
|
|
bmpheader.cBitCount = 24;
|
|
rc = GpiDrawBits (hps_begin_paint,
|
|
pchPixBuf,
|
|
(PBITMAPINFO2)&bmpheader,
|
|
4,
|
|
aptlPoints,
|
|
ROP_SRCCOPY,
|
|
BBO_IGNORE);
|
|
|
|
_buffer_free (pchPixBuf);
|
|
}
|
|
|
|
/* Restore Y inversion */
|
|
GpiEnableYInversion (hps_begin_paint, lOldYInversion);
|
|
}
|
|
|
|
static void
|
|
_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
|
|
HPS hps_begin_paint,
|
|
PRECTL prcl_begin_paint_rect)
|
|
{
|
|
HPS hps;
|
|
HDC hdc;
|
|
HAB hab;
|
|
SIZEL sizlTemp;
|
|
HBITMAP hbmpTemp;
|
|
BITMAPINFO2 bmi2Temp;
|
|
POINTL aptlPoints[4];
|
|
int y;
|
|
unsigned char *pchTemp;
|
|
|
|
/* To copy pixels from screen to our buffer, we do the following steps:
|
|
*
|
|
* - Blit pixels from screen to a HBITMAP:
|
|
* -- Create Memory Device Context
|
|
* -- Create a PS into it
|
|
* -- Create a HBITMAP
|
|
* -- Select HBITMAP into memory PS
|
|
* -- Blit dirty pixels from screen to HBITMAP
|
|
* - Copy HBITMAP lines (pixels) into our buffer
|
|
* - Free resources
|
|
*
|
|
* These steps will require an Anchor Block (HAB). However,
|
|
* WinQUeryAnchorBlock () documentation says that HAB is not
|
|
* used in current OS/2 implementations, OS/2 deduces all information
|
|
* it needs from the TID. Anyway, we'd be in trouble if we'd have to
|
|
* get a HAB where we only know a HPS...
|
|
* So, we'll simply use a fake HAB.
|
|
*/
|
|
|
|
hab = (HAB) 1; /* OS/2 doesn't really use HAB... */
|
|
|
|
/* Create a memory device context */
|
|
hdc = DevOpenDC (hab, OD_MEMORY,"*",0L, NULL, NULLHANDLE);
|
|
if (!hdc) {
|
|
return;
|
|
}
|
|
|
|
/* Create a memory PS */
|
|
sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft;
|
|
sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom;
|
|
hps = GpiCreatePS (hab,
|
|
hdc,
|
|
&sizlTemp,
|
|
PU_PELS | GPIT_NORMAL | GPIA_ASSOC);
|
|
if (!hps) {
|
|
DevCloseDC (hdc);
|
|
return;
|
|
}
|
|
|
|
/* Create an uninitialized bitmap. */
|
|
/* Prepare BITMAPINFO2 structure for our buffer */
|
|
memset (&bmi2Temp, 0, sizeof (bmi2Temp));
|
|
bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2);
|
|
bmi2Temp.cx = sizlTemp.cx;
|
|
bmi2Temp.cy = sizlTemp.cy;
|
|
bmi2Temp.cPlanes = 1;
|
|
bmi2Temp.cBitCount = 32;
|
|
|
|
hbmpTemp = GpiCreateBitmap (hps,
|
|
(PBITMAPINFOHEADER2) &bmi2Temp,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!hbmpTemp) {
|
|
GpiDestroyPS (hps);
|
|
DevCloseDC (hdc);
|
|
return;
|
|
}
|
|
|
|
/* Select the bitmap into the memory device context. */
|
|
GpiSetBitmap (hps, hbmpTemp);
|
|
|
|
/* Target coordinates (Noninclusive) */
|
|
aptlPoints[0].x = 0;
|
|
aptlPoints[0].y = 0;
|
|
|
|
aptlPoints[1].x = sizlTemp.cx;
|
|
aptlPoints[1].y = sizlTemp.cy;
|
|
|
|
/* Source coordinates (Inclusive) */
|
|
aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
|
|
aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
|
|
|
|
aptlPoints[3].x = prcl_begin_paint_rect->xRight;
|
|
aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop;
|
|
|
|
/* Blit pixels from screen to bitmap */
|
|
GpiBitBlt (hps,
|
|
hps_begin_paint,
|
|
4,
|
|
aptlPoints,
|
|
ROP_SRCCOPY,
|
|
BBO_IGNORE);
|
|
|
|
/* Now we have to extract the pixels from the bitmap. */
|
|
pchTemp =
|
|
surface->pixels +
|
|
(prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 +
|
|
prcl_begin_paint_rect->xLeft*4;
|
|
for (y = 0; y < sizlTemp.cy; y++) {
|
|
/* Get one line of pixels */
|
|
GpiQueryBitmapBits (hps,
|
|
sizlTemp.cy - y - 1, /* lScanStart */
|
|
1, /* lScans */
|
|
pchTemp,
|
|
&bmi2Temp);
|
|
|
|
/* Go for next line */
|
|
pchTemp += surface->bitmap_info.cx*4;
|
|
}
|
|
|
|
/* Clean up resources */
|
|
GpiSetBitmap (hps, (HBITMAP) NULL);
|
|
GpiDeleteBitmap (hbmpTemp);
|
|
GpiDestroyPS (hps);
|
|
DevCloseDC (hdc);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_os2_surface_acquire_source_image (void *abstract_surface,
|
|
cairo_image_surface_t **image_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
|
|
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
/* Increase lend counter */
|
|
local_os2_surface->pixel_array_lend_count++;
|
|
|
|
*image_out = local_os2_surface->image_surface;
|
|
*image_extra = NULL;
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_os2_surface_release_source_image (void *abstract_surface,
|
|
cairo_image_surface_t *image,
|
|
void *image_extra)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return;
|
|
}
|
|
|
|
/* Decrease Lend counter! */
|
|
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
if (local_os2_surface->pixel_array_lend_count > 0)
|
|
local_os2_surface->pixel_array_lend_count--;
|
|
DosPostEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
return;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_os2_surface_acquire_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_rectangle_int_t *image_rect,
|
|
void **image_extra)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
|
|
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
/* Increase lend counter */
|
|
local_os2_surface->pixel_array_lend_count++;
|
|
|
|
*image_out = local_os2_surface->image_surface;
|
|
*image_extra = NULL;
|
|
|
|
image_rect->x = 0;
|
|
image_rect->y = 0;
|
|
image_rect->width = local_os2_surface->bitmap_info.cx;
|
|
image_rect->height = local_os2_surface->bitmap_info.cy;
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_os2_surface_release_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t *image,
|
|
cairo_rectangle_int_t *image_rect,
|
|
void *image_extra)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
RECTL rclToBlit;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return;
|
|
}
|
|
|
|
/* So, we got back the image, and if all goes well, then
|
|
* something has been changed inside the interest_rect.
|
|
* So, we blit it to the screen!
|
|
*/
|
|
|
|
if (local_os2_surface->blit_as_changes) {
|
|
/* Get mutex, we'll work with the pixel array! */
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
|
|
/* Could not get mutex! */
|
|
return;
|
|
}
|
|
|
|
if (local_os2_surface->hwnd_client_window) {
|
|
/* We know the HWND, so let's invalidate the window region,
|
|
* so the application will redraw itself, using the
|
|
* cairo_os2_surface_refresh_window () API from its own PM thread.
|
|
*
|
|
* This is the safe method, which should be preferred every time.
|
|
*/
|
|
rclToBlit.xLeft = interest_rect->x;
|
|
rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */
|
|
rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (interest_rect->y);
|
|
rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (interest_rect->y+interest_rect->height); /* Noninclusive */
|
|
|
|
WinInvalidateRect (local_os2_surface->hwnd_client_window,
|
|
&rclToBlit,
|
|
FALSE);
|
|
} else {
|
|
/* We don't know the HWND, so try to blit the pixels from here!
|
|
* Please note that it can be problematic if this is not the PM thread!
|
|
*
|
|
* It can cause internal PM stuffs to be scewed up, for some reason.
|
|
* Please always tell the HWND to the surface using the
|
|
* cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
|
|
* from your WM_PAINT, if it's possible!
|
|
*/
|
|
rclToBlit.xLeft = interest_rect->x;
|
|
rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */
|
|
rclToBlit.yBottom = interest_rect->y;
|
|
rclToBlit.yTop = interest_rect->y+interest_rect->height; /* Noninclusive */
|
|
/* Now blit there the stuffs! */
|
|
_cairo_os2_surface_blit_pixels (local_os2_surface,
|
|
local_os2_surface->hps_client_window,
|
|
&rclToBlit);
|
|
}
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
}
|
|
/* Also decrease Lend counter! */
|
|
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
if (local_os2_surface->pixel_array_lend_count > 0)
|
|
local_os2_surface->pixel_array_lend_count--;
|
|
DosPostEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_os2_surface_get_extents (void *abstract_surface,
|
|
cairo_rectangle_int_t *rectangle)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
rectangle->x = 0;
|
|
rectangle->y = 0;
|
|
rectangle->width = local_os2_surface->bitmap_info.cx;
|
|
rectangle->height = local_os2_surface->bitmap_info.cy;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_create:
|
|
* @hps_client_window: the presentation handle to bind the surface to
|
|
* @width: the width of the surface
|
|
* @height: the height of the surface
|
|
*
|
|
* Create a Cairo surface which is bound to a given presentation space (HPS).
|
|
* The surface will be created to have the given size.
|
|
* By default every change to the surface will be made visible immediately by
|
|
* blitting it into the window. This can be changed with
|
|
* cairo_os2_surface_set_manual_window_refresh().
|
|
* Note that the surface will contain garbage when created, so the pixels have
|
|
* to be initialized by hand first. You can use the Cairo functions to fill it
|
|
* with black, or use cairo_surface_mark_dirty() to fill the surface with pixels
|
|
* from the window/HPS.
|
|
*
|
|
* Return value: the newly created surface
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_os2_surface_create (HPS hps_client_window,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
cairo_status_t status;
|
|
int rc;
|
|
|
|
/* Check the size of the window */
|
|
if ((width <= 0) ||
|
|
(height <= 0))
|
|
{
|
|
/* Invalid window size! */
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
|
|
}
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
|
|
if (!local_os2_surface) {
|
|
/* Not enough memory! */
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
}
|
|
|
|
/* Initialize the OS/2 specific parts of the surface! */
|
|
|
|
/* Create mutex semaphore */
|
|
rc = DosCreateMutexSem (NULL,
|
|
&(local_os2_surface->hmtx_use_private_fields),
|
|
0,
|
|
FALSE);
|
|
if (rc != NO_ERROR) {
|
|
/* Could not create mutex semaphore! */
|
|
free (local_os2_surface);
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
}
|
|
|
|
/* Save PS handle */
|
|
local_os2_surface->hps_client_window = hps_client_window;
|
|
|
|
/* Defaults */
|
|
local_os2_surface->hwnd_client_window = NULLHANDLE;
|
|
local_os2_surface->blit_as_changes = TRUE;
|
|
local_os2_surface->pixel_array_lend_count = 0;
|
|
rc = DosCreateEventSem (NULL,
|
|
&(local_os2_surface->hev_pixel_array_came_back),
|
|
0,
|
|
FALSE);
|
|
|
|
if (rc != NO_ERROR) {
|
|
/* Could not create event semaphore! */
|
|
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
free (local_os2_surface);
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
}
|
|
|
|
/* Prepare BITMAPINFO2 structure for our buffer */
|
|
memset (&(local_os2_surface->bitmap_info), 0, sizeof (local_os2_surface->bitmap_info));
|
|
local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2);
|
|
local_os2_surface->bitmap_info.cx = width;
|
|
local_os2_surface->bitmap_info.cy = height;
|
|
local_os2_surface->bitmap_info.cPlanes = 1;
|
|
local_os2_surface->bitmap_info.cBitCount = 32;
|
|
|
|
/* Allocate memory for pixels */
|
|
local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
|
|
if (!(local_os2_surface->pixels)) {
|
|
/* Not enough memory for the pixels! */
|
|
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
free (local_os2_surface);
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
}
|
|
|
|
/* Create image surface from pixel array */
|
|
local_os2_surface->image_surface = (cairo_image_surface_t *)
|
|
cairo_image_surface_create_for_data (local_os2_surface->pixels,
|
|
CAIRO_FORMAT_ARGB32,
|
|
width, /* Width */
|
|
height, /* Height */
|
|
width * 4); /* Rowstride */
|
|
|
|
status = local_os2_surface->image_surface->base.status;
|
|
if (status) {
|
|
/* Could not create image surface! */
|
|
_buffer_free (local_os2_surface->pixels);
|
|
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
free (local_os2_surface);
|
|
return _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
/* Initialize base surface */
|
|
_cairo_surface_init (&local_os2_surface->base,
|
|
&cairo_os2_surface_backend,
|
|
_cairo_content_from_format (CAIRO_FORMAT_ARGB32));
|
|
|
|
return (cairo_surface_t *)local_os2_surface;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_set_size:
|
|
* @surface: the cairo surface to resize
|
|
* @new_width: the new width of the surface
|
|
* @new_height: the new height of the surface
|
|
* @timeout: timeout value in milliseconds
|
|
*
|
|
* When the client window is resized, call this API to set the new size in the
|
|
* underlying surface accordingly. This function will reallocate everything,
|
|
* so you'll have to redraw everything in the surface after this call.
|
|
* The surface will contain garbage after the resizing. So the notes of
|
|
* cairo_os2_surface_create() apply here, too.
|
|
*
|
|
* The timeout value specifies how long the function should wait on other parts
|
|
* of the program to release the buffers. It is necessary, because it can happen
|
|
* that Cairo is just drawing something into the surface while we want to
|
|
* destroy and recreate it.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
|
|
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
|
|
* %CAIRO_STATUS_INVALID_SIZE for invalid sizes
|
|
* %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
|
|
* timeout happened before all the buffers were released
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
int
|
|
cairo_os2_surface_set_size (cairo_surface_t *surface,
|
|
int new_width,
|
|
int new_height,
|
|
int timeout)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
unsigned char *pchNewPixels;
|
|
int rc;
|
|
cairo_image_surface_t *pNewImageSurface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
|
|
if ((new_width <= 0) ||
|
|
(new_height <= 0))
|
|
{
|
|
/* Invalid size! */
|
|
return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
|
|
}
|
|
|
|
/* Allocate memory for new stuffs */
|
|
pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
|
|
if (!pchNewPixels) {
|
|
/* Not enough memory for the pixels!
|
|
* Everything remains the same!
|
|
*/
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* Create image surface from new pixel array */
|
|
pNewImageSurface = (cairo_image_surface_t *)
|
|
cairo_image_surface_create_for_data (pchNewPixels,
|
|
CAIRO_FORMAT_ARGB32,
|
|
new_width, /* Width */
|
|
new_height, /* Height */
|
|
new_width * 4); /* Rowstride */
|
|
|
|
if (pNewImageSurface->base.status) {
|
|
/* Could not create image surface!
|
|
* Everything remains the same!
|
|
*/
|
|
_buffer_free (pchNewPixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* Okay, new memory allocated, so it's time to swap old buffers
|
|
* to new ones!
|
|
*/
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
|
|
/* Could not get mutex!
|
|
* Everything remains the same!
|
|
*/
|
|
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
|
|
_buffer_free (pchNewPixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* We have to make sure that we won't destroy a surface which
|
|
* is lent to some other code (Cairo is drawing into it)!
|
|
*/
|
|
while (local_os2_surface->pixel_array_lend_count > 0) {
|
|
ULONG ulPostCount;
|
|
DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
/* Wait for somebody to return the pixels! */
|
|
rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
|
|
if (rc != NO_ERROR) {
|
|
/* Either timeout or something wrong... Exit. */
|
|
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
|
|
_buffer_free (pchNewPixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
/* Okay, grab mutex and check counter again! */
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
|
|
!= NO_ERROR)
|
|
{
|
|
/* Could not get mutex!
|
|
* Everything remains the same!
|
|
*/
|
|
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
|
|
_buffer_free (pchNewPixels);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
}
|
|
|
|
/* Destroy old image surface */
|
|
cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
|
|
/* Destroy old pixel buffer */
|
|
_buffer_free (local_os2_surface->pixels);
|
|
/* Set new image surface */
|
|
local_os2_surface->image_surface = pNewImageSurface;
|
|
/* Set new pixel buffer */
|
|
local_os2_surface->pixels = pchNewPixels;
|
|
/* Change bitmap2 structure */
|
|
local_os2_surface->bitmap_info.cx = new_width;
|
|
local_os2_surface->bitmap_info.cy = new_height;
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_refresh_window:
|
|
* @surface: the cairo surface to refresh
|
|
* @hps_begin_paint: the presentation handle of the window to refresh
|
|
* @prcl_begin_paint_rect: the rectangle to redraw
|
|
*
|
|
* This function can be used to force a repaint of a given area of the client
|
|
* window. It should usually be called from the WM_PAINT processing of the
|
|
* window procedure. However, it can be called any time a given part of the
|
|
* window has to be updated.
|
|
*
|
|
* The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
|
|
* of the window procedure, but you can also get the HPS using WinGetPS, and you
|
|
* can assemble your own update rectangle by hand.
|
|
* If hps_begin_paint is %NULL, the function will use the HPS passed into
|
|
* cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
|
|
* will query the current window size and repaint the whole window.
|
|
*
|
|
* Cairo assumes that if you set the HWND to the surface using
|
|
* cairo_os2_surface_set_hwnd(), this function will be called by the application
|
|
* every time it gets a WM_PAINT for that HWND. If the HWND is set in the
|
|
* surface, Cairo uses this function to handle dirty areas too.
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
void
|
|
cairo_os2_surface_refresh_window (cairo_surface_t *surface,
|
|
HPS hps_begin_paint,
|
|
PRECTL prcl_begin_paint_rect)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
RECTL rclTemp;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return;
|
|
}
|
|
|
|
/* Manage defaults (NULLs) */
|
|
if (!hps_begin_paint)
|
|
hps_begin_paint = local_os2_surface->hps_client_window;
|
|
|
|
if (prcl_begin_paint_rect == NULL) {
|
|
/* Update the whole window! */
|
|
rclTemp.xLeft = 0;
|
|
rclTemp.xRight = local_os2_surface->bitmap_info.cx;
|
|
rclTemp.yTop = local_os2_surface->bitmap_info.cy;
|
|
rclTemp.yBottom = 0;
|
|
} else {
|
|
/* Use the rectangle we got passed as parameter! */
|
|
rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
|
|
rclTemp.xRight = prcl_begin_paint_rect->xRight;
|
|
rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
|
|
rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
|
|
}
|
|
|
|
/* Get mutex, we'll work with the pixel array! */
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
|
|
!= NO_ERROR)
|
|
{
|
|
/* Could not get mutex! */
|
|
return;
|
|
}
|
|
|
|
if ((local_os2_surface->dirty_area_present) &&
|
|
(local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
|
|
(local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
|
|
(local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
|
|
(local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
|
|
{
|
|
/* Aha, this call was because of a dirty area, so in this case we
|
|
* have to blit the pixels from the screen to the surface!
|
|
*/
|
|
local_os2_surface->dirty_area_present = FALSE;
|
|
_cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
|
|
hps_begin_paint,
|
|
&rclTemp);
|
|
} else {
|
|
/* Okay, we have the surface, have the HPS
|
|
* (might be from WinBeginPaint () or from WinGetPS () )
|
|
* Now blit there the stuffs!
|
|
*/
|
|
_cairo_os2_surface_blit_pixels (local_os2_surface,
|
|
hps_begin_paint,
|
|
&rclTemp);
|
|
}
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_os2_surface_finish (void *abstract_surface)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
|
|
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
/* Destroy old image surface */
|
|
cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
|
|
/* Destroy old pixel buffer */
|
|
_buffer_free (local_os2_surface->pixels);
|
|
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
|
|
/* The memory itself will be free'd by the cairo_surface_destroy ()
|
|
* who called us.
|
|
*/
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_set_hwnd:
|
|
* @surface: the cairo surface to associate with the window handle
|
|
* @hwnd_client_window: the window handle of the client window
|
|
*
|
|
* Sets window handle for surface. If Cairo wants to blit into the window
|
|
* because it is set to blit as the surface changes (see
|
|
* cairo_os2_surface_set_manual_window_refresh()), then there are two ways it
|
|
* can choose:
|
|
* If it knows the HWND of the surface, then it invalidates that area, so the
|
|
* application will get a WM_PAINT message and it can call
|
|
* cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
|
|
* will use the HPS it got at surface creation time, and blit the pixels itself.
|
|
* It's also a solution, but experience shows that if this happens from a non-PM
|
|
* thread, then it can screw up PM internals.
|
|
*
|
|
* So, best solution is to set the HWND for the surface after the surface
|
|
* creation, so every blit will be done from application's message processing
|
|
* loop, which is the safest way to do.
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
void
|
|
cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
|
|
HWND hwnd_client_window)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return;
|
|
}
|
|
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
|
|
!= NO_ERROR)
|
|
{
|
|
/* Could not get mutex! */
|
|
return;
|
|
}
|
|
|
|
local_os2_surface->hwnd_client_window = hwnd_client_window;
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_set_manual_window_refresh:
|
|
* @surface: the cairo surface to set the refresh mode for
|
|
* @manual_refresh: the switch for manual surface refresh
|
|
*
|
|
* This API can tell Cairo if it should show every change to this surface
|
|
* immediately in the window or if it should be cached and will only be visible
|
|
* once the user calls cairo_os2_surface_refresh_window() explicitly. If the
|
|
* HWND was not set in the cairo surface, then the HPS will be used to blit the
|
|
* graphics. Otherwise it will invalidate the given window region so the user
|
|
* will get the WM_PAINT message to redraw that area of the window.
|
|
*
|
|
* So, if you're only interested in displaying the final result after several
|
|
* drawing operations, you might get better performance if you put the surface
|
|
* into manual refresh mode by passing a true value to this function. Then call
|
|
* cairo_os2_surface_refresh() whenever desired.
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
void
|
|
cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
|
|
cairo_bool_t manual_refresh)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return;
|
|
}
|
|
|
|
local_os2_surface->blit_as_changes = !manual_refresh;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_get_manual_window_refresh:
|
|
* @surface: the cairo surface to query the refresh mode from
|
|
*
|
|
* This space left intentionally blank.
|
|
*
|
|
* Return value: current refresh mode of the surface (true by default)
|
|
*
|
|
* Since: 1.4
|
|
**/
|
|
cairo_bool_t
|
|
cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return FALSE;
|
|
}
|
|
|
|
return !(local_os2_surface->blit_as_changes);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_os2_surface_mark_dirty_rectangle (void *surface,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
RECTL rclToBlit;
|
|
|
|
local_os2_surface = (cairo_os2_surface_t *) surface;
|
|
if ((!local_os2_surface) ||
|
|
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
|
|
{
|
|
/* Invalid parameter (wrong surface)! */
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
|
|
/* Get mutex, we'll work with the pixel array! */
|
|
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
|
|
!= NO_ERROR)
|
|
{
|
|
/* Could not get mutex! */
|
|
return CAIRO_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Check for defaults */
|
|
if (width < 0)
|
|
width = local_os2_surface->bitmap_info.cx;
|
|
if (height < 0)
|
|
height = local_os2_surface->bitmap_info.cy;
|
|
|
|
if (local_os2_surface->hwnd_client_window) {
|
|
/* We know the HWND, so let's invalidate the window region,
|
|
* so the application will redraw itself, using the
|
|
* cairo_os2_surface_refresh_window () API from its own PM thread.
|
|
* From that function we'll note that it's not a redraw but a
|
|
* dirty-rectangle deal stuff, so we'll handle the things from
|
|
* there.
|
|
*
|
|
* This is the safe method, which should be preferred every time.
|
|
*/
|
|
rclToBlit.xLeft = x;
|
|
rclToBlit.xRight = x + width; /* Noninclusive */
|
|
rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
|
|
rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
|
|
|
|
#if 0
|
|
if (local_os2_surface->dirty_area_present) {
|
|
/* Yikes, there is already a dirty area which should be
|
|
* cleaned up, but we'll overwrite it. Sorry.
|
|
* TODO: Something clever should be done here.
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
/* Set up dirty area reminder stuff */
|
|
memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
|
|
local_os2_surface->dirty_area_present = TRUE;
|
|
|
|
/* Invalidate window area */
|
|
WinInvalidateRect (local_os2_surface->hwnd_client_window,
|
|
&rclToBlit,
|
|
FALSE);
|
|
} else {
|
|
/* We don't know the HWND, so try to blit the pixels from here!
|
|
* Please note that it can be problematic if this is not the PM thread!
|
|
*
|
|
* It can cause internal PM stuffs to be scewed up, for some reason.
|
|
* Please always tell the HWND to the surface using the
|
|
* cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
|
|
* from your WM_PAINT, if it's possible!
|
|
*/
|
|
|
|
rclToBlit.xLeft = x;
|
|
rclToBlit.xRight = x + width; /* Noninclusive */
|
|
rclToBlit.yBottom = y;
|
|
rclToBlit.yTop = y + height; /* Noninclusive */
|
|
/* Now get the pixels from the screen! */
|
|
_cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
|
|
local_os2_surface->hps_client_window,
|
|
&rclToBlit);
|
|
}
|
|
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const cairo_surface_backend_t cairo_os2_surface_backend = {
|
|
CAIRO_SURFACE_TYPE_OS2,
|
|
NULL, /* create_similar */
|
|
_cairo_os2_surface_finish,
|
|
_cairo_os2_surface_acquire_source_image,
|
|
_cairo_os2_surface_release_source_image,
|
|
_cairo_os2_surface_acquire_dest_image,
|
|
_cairo_os2_surface_release_dest_image,
|
|
NULL, /* clone_similar */
|
|
NULL, /* composite */
|
|
NULL, /* fill_rectangles */
|
|
NULL, /* composite_trapezoids */
|
|
NULL, /* create_span_renderer */
|
|
NULL, /* check_span_renderer */
|
|
NULL, /* copy_page */
|
|
NULL, /* show_page */
|
|
_cairo_os2_surface_get_extents,
|
|
NULL, /* old_show_glyphs */
|
|
NULL, /* get_font_options */
|
|
NULL, /* flush */
|
|
_cairo_os2_surface_mark_dirty_rectangle,
|
|
NULL, /* scaled_font_fini */
|
|
NULL, /* scaled_glyph_fini */
|
|
NULL, /* paint */
|
|
NULL, /* mask */
|
|
NULL, /* stroke */
|
|
NULL, /* fill */
|
|
NULL, /* show_glyphs */
|
|
NULL /* snapshot */
|
|
};
|