forked from KolibriOS/kolibrios
754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
1416 lines
49 KiB
C
1416 lines
49 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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>
|
|
* Rich Walsh <dragtext@e-vertise.com>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-os2-private.h"
|
|
#include "cairo-default-context-private.h"
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-surface-fallback-private.h"
|
|
#include "cairo-image-surface-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 inline void
|
|
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)
|
|
{
|
|
size_t nbytes;
|
|
void *buffer = NULL;
|
|
|
|
if (!a || !b || !size ||
|
|
a >= INT32_MAX / b || a*b >= INT32_MAX / size) {
|
|
return NULL;
|
|
}
|
|
nbytes = a * b * size;
|
|
|
|
#ifdef OS2_USE_PLATFORM_ALLOC
|
|
/* Using OBJ_ANY on a machine that isn't configured for hi-mem
|
|
* will cause ERROR_INVALID_PARAMETER. If this occurs, or this
|
|
* build doesn't have hi-mem enabled, fall back to using lo-mem.
|
|
*/
|
|
#ifdef OS2_HIGH_MEMORY
|
|
if (!DosAllocMem (&buffer, nbytes,
|
|
OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT))
|
|
return buffer;
|
|
#endif
|
|
if (DosAllocMem (&buffer, nbytes,
|
|
PAG_READ | PAG_WRITE | PAG_COMMIT))
|
|
return NULL;
|
|
#else
|
|
/* Clear the malloc'd buffer the way DosAllocMem() does. */
|
|
buffer = malloc (nbytes);
|
|
if (buffer) {
|
|
memset (buffer, 0, nbytes);
|
|
}
|
|
#endif
|
|
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;
|
|
LONG rc = GPI_OK;
|
|
|
|
/* Check the limits (may not be necessary) */
|
|
if (prcl_begin_paint_rect->xLeft < 0)
|
|
prcl_begin_paint_rect->xLeft = 0;
|
|
if (prcl_begin_paint_rect->yBottom < 0)
|
|
prcl_begin_paint_rect->yBottom = 0;
|
|
if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx)
|
|
prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx;
|
|
if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy)
|
|
prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy;
|
|
|
|
/* Exit if the rectangle is empty */
|
|
if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight ||
|
|
prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop)
|
|
return;
|
|
|
|
/* Set the Target & Source coordinates */
|
|
*((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect;
|
|
*((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect;
|
|
|
|
/* Make the Target coordinates non-inclusive */
|
|
aptlPoints[1].x -= 1;
|
|
aptlPoints[1].y -= 1;
|
|
|
|
/* Enable Y Inversion for the HPS, so 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);
|
|
|
|
/* 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
|
|
if (!surface->use_24bpp) {
|
|
rc = GpiDrawBits (hps_begin_paint,
|
|
surface->pixels,
|
|
&(surface->bitmap_info),
|
|
4,
|
|
aptlPoints,
|
|
ROP_SRCCOPY,
|
|
BBO_IGNORE);
|
|
if (rc != GPI_OK)
|
|
surface->use_24bpp = TRUE;
|
|
}
|
|
|
|
if (surface->use_24bpp) {
|
|
/* 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
|
|
*/
|
|
BITMAPINFO2 bmpinfo;
|
|
unsigned char *pchPixBuf;
|
|
unsigned char *pchTarget;
|
|
ULONG *pulSource;
|
|
ULONG ulX;
|
|
ULONG ulY;
|
|
ULONG ulPad;
|
|
|
|
/* Set up the bitmap header, but this time for 24bit depth. */
|
|
bmpinfo = surface->bitmap_info;
|
|
bmpinfo.cBitCount = 24;
|
|
|
|
/* The start of each row has to be DWORD aligned. Calculate the
|
|
* of number aligned bytes per row, the total size of the bitmap,
|
|
* and the number of padding bytes at the end of each row.
|
|
*/
|
|
ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4;
|
|
bmpinfo.cbImage = ulX * bmpinfo.cy;
|
|
ulPad = ulX - bmpinfo.cx * 3;
|
|
|
|
/* Allocate temporary pixel buffer. If the rows don't need
|
|
* padding, it has to be 1 byte larger than the size of the
|
|
* bitmap or else the high-order byte from the last source
|
|
* row will end up in unallocated memory.
|
|
*/
|
|
pchPixBuf = (unsigned char *)_buffer_alloc (1, 1,
|
|
bmpinfo.cbImage + (ulPad ? 0 : 1));
|
|
|
|
if (pchPixBuf) {
|
|
/* Copy 4 bytes from the source but advance the target ptr only
|
|
* 3 bytes, so the high-order alpha byte will be overwritten by
|
|
* the next copy. At the end of each row, skip over the padding.
|
|
*/
|
|
pchTarget = pchPixBuf;
|
|
pulSource = (ULONG*)surface->pixels;
|
|
for (ulY = bmpinfo.cy; ulY; ulY--) {
|
|
for (ulX = bmpinfo.cx; ulX; ulX--) {
|
|
*((ULONG*)pchTarget) = *pulSource++;
|
|
pchTarget += 3;
|
|
}
|
|
pchTarget += ulPad;
|
|
}
|
|
|
|
rc = GpiDrawBits (hps_begin_paint,
|
|
pchPixBuf,
|
|
&bmpinfo,
|
|
4,
|
|
aptlPoints,
|
|
ROP_SRCCOPY,
|
|
BBO_IGNORE);
|
|
if (rc != GPI_OK)
|
|
surface->use_24bpp = FALSE;
|
|
|
|
_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;
|
|
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
|
|
*/
|
|
|
|
/* Create a memory device context */
|
|
hdc = DevOpenDC (0, 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 (0,
|
|
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 */
|
|
(PBYTE)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 *surface = (cairo_os2_surface_t *) abstract_surface;
|
|
|
|
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
/* Increase lend counter */
|
|
surface->pixel_array_lend_count++;
|
|
|
|
*image_out = surface->image_surface;
|
|
*image_extra = NULL;
|
|
|
|
DosReleaseMutexSem (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 *surface = (cairo_os2_surface_t *) abstract_surface;
|
|
|
|
/* Decrease Lend counter! */
|
|
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
if (surface->pixel_array_lend_count > 0)
|
|
surface->pixel_array_lend_count--;
|
|
DosPostEventSem (surface->hev_pixel_array_came_back);
|
|
|
|
DosReleaseMutexSem (surface->hmtx_use_private_fields);
|
|
}
|
|
|
|
static cairo_image_surface_t *
|
|
_cairo_os2_surface_map_to_image (void *abstract_surface,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
|
|
|
|
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
/* Increase lend counter */
|
|
surface->pixel_array_lend_count++;
|
|
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
|
|
/* XXX: BROKEN! */
|
|
*image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface,
|
|
extents);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_os2_surface_unmap_image (void *abstract_surface,
|
|
cairo_image_surface_t *image)
|
|
{
|
|
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
|
|
|
|
/* 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 (surface->blit_as_changes) {
|
|
RECTL rclToBlit;
|
|
|
|
/* Get mutex, we'll work with the pixel array! */
|
|
if (DosRequestMutexSem (surface->hmtx_use_private_fields,
|
|
SEM_INDEFINITE_WAIT) != NO_ERROR)
|
|
{
|
|
/* Could not get mutex! */
|
|
return;
|
|
}
|
|
|
|
rclToBlit.xLeft = image->base.device_transform_inverse.x0;
|
|
rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */
|
|
rclToBlit.yTop = image->base.device_transform_inverse.y0;
|
|
rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */
|
|
|
|
if (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.yTop = surface->bitmap_info.cy - rclToBlit.yTop;
|
|
rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop;
|
|
WinInvalidateRect (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 screwed 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!
|
|
*/
|
|
_cairo_os2_surface_blit_pixels (surface,
|
|
surface->hps_client_window,
|
|
&rclToBlit);
|
|
}
|
|
|
|
DosReleaseMutexSem (surface->hmtx_use_private_fields);
|
|
}
|
|
/* Also decrease Lend counter! */
|
|
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
|
|
|
|
if (surface->pixel_array_lend_count > 0)
|
|
surface->pixel_array_lend_count--;
|
|
DosPostEventSem (surface->hev_pixel_array_came_back);
|
|
|
|
DosReleaseMutexSem (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 *surface = (cairo_os2_surface_t *) abstract_surface;
|
|
|
|
rectangle->x = 0;
|
|
rectangle->y = 0;
|
|
rectangle->width = surface->bitmap_info.cx;
|
|
rectangle->height = 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 caller retains ownership of the HPS and must dispose of it after the
|
|
* the surface has been destroyed. 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 = 0;
|
|
cairo_status_t status;
|
|
int rc;
|
|
|
|
/* Check the size of the window */
|
|
if ((width <= 0) || (height <= 0)) {
|
|
status = _cairo_error (CAIRO_STATUS_INVALID_SIZE);
|
|
goto error_exit;
|
|
}
|
|
|
|
/* Allocate an OS/2 surface structure. */
|
|
local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
|
|
if (!local_os2_surface) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto error_exit;
|
|
}
|
|
|
|
memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t));
|
|
|
|
/* Allocate resources: mutex & event semaphores and the pixel buffer */
|
|
if (DosCreateMutexSem (NULL,
|
|
&(local_os2_surface->hmtx_use_private_fields),
|
|
0,
|
|
FALSE))
|
|
{
|
|
status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
|
|
goto error_exit;
|
|
}
|
|
|
|
if (DosCreateEventSem (NULL,
|
|
&(local_os2_surface->hev_pixel_array_came_back),
|
|
0,
|
|
FALSE))
|
|
{
|
|
status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
|
|
goto error_exit;
|
|
}
|
|
|
|
local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
|
|
if (!local_os2_surface->pixels) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto error_exit;
|
|
}
|
|
|
|
/* 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)
|
|
goto error_exit;
|
|
|
|
/* Set values for OS/2-specific data that aren't zero/NULL/FALSE.
|
|
* Note: hps_client_window may be null if this was called by
|
|
* cairo_os2_surface_create_for_window().
|
|
*/
|
|
local_os2_surface->hps_client_window = hps_client_window;
|
|
local_os2_surface->blit_as_changes = TRUE;
|
|
|
|
/* Prepare BITMAPINFO2 structure for our buffer */
|
|
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;
|
|
|
|
/* Initialize base surface */
|
|
_cairo_surface_init (&local_os2_surface->base,
|
|
&cairo_os2_surface_backend,
|
|
NULL, /* device */
|
|
_cairo_content_from_format (CAIRO_FORMAT_ARGB32));
|
|
|
|
/* Successful exit */
|
|
return (cairo_surface_t *)local_os2_surface;
|
|
|
|
error_exit:
|
|
|
|
/* This point will only be reached if an error occurred */
|
|
|
|
if (local_os2_surface) {
|
|
if (local_os2_surface->pixels)
|
|
_buffer_free (local_os2_surface->pixels);
|
|
if (local_os2_surface->hev_pixel_array_came_back)
|
|
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
|
|
if (local_os2_surface->hmtx_use_private_fields)
|
|
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
|
|
free (local_os2_surface);
|
|
}
|
|
|
|
return _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_create_for_window:
|
|
* @hwnd_client_window: the window 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 window; the caller retains
|
|
* ownership of the window. This is a convenience function for use with
|
|
* windows that will only be updated when cairo_os2_surface_refresh_window()
|
|
* is called (usually in response to a WM_PAINT message). It avoids the need
|
|
* to create a persistent HPS for every window and assumes that one will be
|
|
* supplied by the caller when a cairo function needs one. If it isn't, an
|
|
* HPS will be created on-the-fly and released before the function which needs
|
|
* it returns.
|
|
*
|
|
* Return value: the newly created surface
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_os2_surface_create_for_window (HWND hwnd_client_window,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_os2_surface_t *local_os2_surface;
|
|
|
|
/* A window handle must be provided. */
|
|
if (!hwnd_client_window) {
|
|
return _cairo_surface_create_in_error (
|
|
_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
}
|
|
|
|
/* Create the surface. */
|
|
local_os2_surface = (cairo_os2_surface_t *)
|
|
cairo_os2_surface_create (0, width, height);
|
|
|
|
/* If successful, save the hwnd & turn off automatic repainting. */
|
|
if (!local_os2_surface->image_surface->base.status) {
|
|
local_os2_surface->hwnd_client_window = hwnd_client_window;
|
|
local_os2_surface->blit_as_changes = FALSE;
|
|
}
|
|
|
|
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;
|
|
HPS hpsTemp = 0;
|
|
|
|
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 an HPS wasn't provided, see if we can get one. */
|
|
if (!hps_begin_paint) {
|
|
hps_begin_paint = local_os2_surface->hps_client_window;
|
|
if (!hps_begin_paint) {
|
|
if (local_os2_surface->hwnd_client_window) {
|
|
hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
|
|
hps_begin_paint = hpsTemp;
|
|
}
|
|
/* No HPS & no way to get one, so exit */
|
|
if (!hps_begin_paint)
|
|
return;
|
|
}
|
|
}
|
|
|
|
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! */
|
|
if (hpsTemp)
|
|
WinReleasePS(hpsTemp);
|
|
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);
|
|
|
|
if (hpsTemp)
|
|
WinReleasePS(hpsTemp);
|
|
}
|
|
|
|
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; the caller retains ownership of the window.
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_get_hps:
|
|
* @surface: the cairo surface to be querued
|
|
* @hps: HPS currently associated with the surface (if any)
|
|
*
|
|
* This API retrieves the HPS associated with the surface.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
|
|
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
|
|
* %CAIRO_STATUS_NULL_POINTER if the hps argument is null
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
cairo_status_t
|
|
cairo_os2_surface_get_hps (cairo_surface_t *surface,
|
|
HPS *hps)
|
|
{
|
|
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 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
if (!hps)
|
|
{
|
|
return _cairo_error (CAIRO_STATUS_NULL_POINTER);
|
|
}
|
|
*hps = local_os2_surface->hps_client_window;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* cairo_os2_surface_set_hps:
|
|
* @surface: the cairo surface to associate with the HPS
|
|
* @hps: new HPS to be associated with the surface (the HPS may be null)
|
|
*
|
|
* This API replaces the HPS associated with the surface with a new one.
|
|
* The caller retains ownership of the HPS and must dispose of it after
|
|
* the surface has been destroyed or it has been replaced by another
|
|
* call to this function.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
|
|
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
cairo_status_t
|
|
cairo_os2_surface_set_hps (cairo_surface_t *surface,
|
|
HPS hps)
|
|
{
|
|
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 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
}
|
|
local_os2_surface->hps_client_window = hps;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
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,
|
|
_cairo_os2_surface_finish,
|
|
_cairo_default_context_create,
|
|
|
|
NULL, /* create_similar */
|
|
NULL, /* create_similar_image */
|
|
_cairo_os2_surface_map_to_image,
|
|
_cairo_os2_surface_unmap_image,
|
|
|
|
_cairo_surface_default_source,
|
|
_cairo_os2_surface_acquire_source_image,
|
|
_cairo_os2_surface_release_source_image,
|
|
NULL, /* snapshot */
|
|
|
|
_cairo_os2_surface_get_extents,
|
|
NULL, /* get_font_options */
|
|
|
|
NULL, /* flush */
|
|
_cairo_os2_surface_mark_dirty_rectangle,
|
|
|
|
_cairo_surface_fallback_paint,
|
|
_cairo_surface_fallback_mask,
|
|
_cairo_surface_fallback_fill,
|
|
_cairo_surface_fallback_stroke,
|
|
NULL, /* fill/stroke */
|
|
_cairo_surface_fallback_glyphs,
|
|
};
|