/* 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 * * Contributor(s): * Peter Weilbacher * Rich Walsh */ #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 #endif #include #ifdef BUILD_CAIRO_DLL # include "cairo-os2.h" # ifndef __WATCOMC__ # include # 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, };