diff options
author | Peter Weilbacher <pmw@schnurps.(none)> | 2006-09-15 11:18:14 +0200 |
---|---|---|
committer | Peter Weilbacher <pmw@schnurps.(none)> | 2006-09-15 11:18:14 +0200 |
commit | 8c6baacefacbfb9e826d05ae253ead60d4e8e146 (patch) | |
tree | 039a32da5e1736a78bca2187a2a16574ae4c8397 /src/cairo-os2-surface.c | |
parent | edfceea853dd6ae189843f138478c7d43fb98367 (diff) |
OS/2 backend files
Diffstat (limited to 'src/cairo-os2-surface.c')
-rw-r--r-- | src/cairo-os2-surface.c | 1135 |
1 files changed, 1135 insertions, 0 deletions
diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c new file mode 100644 index 00000000..59718bb3 --- /dev/null +++ b/src/cairo-os2-surface.c @@ -0,0 +1,1135 @@ +/* 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 <stdlib.h> +#include <stdio.h> +#include <float.h> +#ifdef BUILD_CAIRO_DLL +# define INCL_WIN +# define INCL_GPI +# define INCL_DOS +# define INCL_DOSERRORS +# include <os2emx.h> +# include "cairo-os2.h" +# ifndef __WATCOMC__ +# include <emx/startup.h> +# endif +#endif +#include "cairoint.h" +#include "cairo-os2-private.h" +#include "fontconfig/fontconfig.h" + +/* + * 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; + +/* The mutex semaphores Cairo uses all around: */ +HMTX cairo_toy_font_face_hash_table_mutex = 0; +HMTX cairo_scaled_font_map_mutex = 0; +HMTX _global_image_glyph_cache_mutex = 0; +#ifdef CAIRO_HAS_FT_FONT +HMTX cairo_ft_unscaled_font_map_mutex = 0; +#endif + +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_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 (); + + /* Create the mutex semaphores we'll use! */ + + /* cairo-font.c: */ + DosCreateMutexSem (NULL, &cairo_toy_font_face_hash_table_mutex, 0, FALSE); + DosCreateMutexSem (NULL, &cairo_scaled_font_map_mutex, 0, FALSE); + DosCreateMutexSem (NULL, &_global_image_glyph_cache_mutex, 0, FALSE); + +#ifdef CAIRO_HAS_FT_FONT + /* cairo-ft-font.c: */ + DosCreateMutexSem (NULL, &cairo_ft_unscaled_font_map_mutex, 0, FALSE); +#endif + + /* Initialize FontConfig */ + FcInit (); +} + +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 (); + + /* Free allocated memories! */ + /* (Check cairo_debug_reset_static_date () for an example of this!) */ + _cairo_font_reset_static_data (); +#ifdef CAIRO_HAS_FT_FONT + _cairo_ft_font_reset_static_data (); +#endif + + /* Destroy the mutex semaphores we've created! */ + /* cairo-font.c: */ + if (cairo_toy_font_face_hash_table_mutex) { + DosCloseMutexSem (cairo_toy_font_face_hash_table_mutex); + cairo_toy_font_face_hash_table_mutex = 0; + } + if (cairo_scaled_font_map_mutex) { + DosCloseMutexSem (cairo_scaled_font_map_mutex); + cairo_scaled_font_map_mutex = 0; + } + if (_global_image_glyph_cache_mutex) { + DosCloseMutexSem (_global_image_glyph_cache_mutex); + _global_image_glyph_cache_mutex = 0; + } + +#ifdef CAIRO_HAS_FT_FONT + /* cairo-ft-font.c: */ + if (cairo_ft_unscaled_font_map_mutex) { + DosCloseMutexSem (cairo_ft_unscaled_font_map_mutex); + cairo_ft_unscaled_font_map_mutex = 0; + } +#endif + + /* Uninitialize FontConfig */ + FcFini (); + +#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 (); +#endif +} + +#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; + + /* 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 + GpiDrawBits (hps_begin_paint, + surface->pixels, + &(surface->bitmap_info), + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + + /* 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_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_int16_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int16_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_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_int16_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int16_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_int_status_t +_cairo_os2_surface_get_extents (void *abstract_surface, + cairo_rectangle_int16_t *rectangle) +{ + 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_STATUS_SURFACE_TYPE_MISMATCH; + } + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = local_os2_surface->bitmap_info.cx; + rectangle->height = local_os2_surface->bitmap_info.cy; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_surface_t * +cairo_os2_surface_create (HPS hps_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + int rc; + + /* Check the size of the window */ + if ((width <= 0) || + (height <= 0)) + { + /* Invalid window size! */ + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); + if (!local_os2_surface) { + /* Not enough memory! */ + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + /* 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! */ + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + /* 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); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + /* 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 *) malloc (width * height * 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); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + /* This is possibly not needed, malloc'd space is + * usually zero'd out! + */ + /* + memset (local_os2_surface->pixels, 0x00, swpTemp.cx * swpTemp.cy * 4); + */ + + /* 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 */ + + if (local_os2_surface->image_surface->base.status) { + /* Could not create image surface! */ + 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); + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t *) &_cairo_surface_nil; + } + + /* 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; +} + +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_STATUS_SURFACE_TYPE_MISMATCH; + } + + if ((new_width <= 0) || + (new_height <= 0)) + { + /* Invalid size! */ + return CAIRO_STATUS_NO_MEMORY; + } + + /* Allocate memory for new stuffs */ + pchNewPixels = (unsigned char *) malloc (new_width * new_height * 4); + if (!pchNewPixels) { + /* Not enough memory for the pixels! + * Everything remains the same! + */ + return CAIRO_STATUS_NO_MEMORY; + } + + /* This is possibly not needed, malloc'd space is usually + * already zero'd out! + */ + /* + memset (pchNewPixels, 0x00, new_width * new_height * 4); + */ + + /* 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! + */ + free (pchNewPixels); + return 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); + free (pchNewPixels); + return 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); + free (pchNewPixels); + return 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); + free (pchNewPixels); + return CAIRO_STATUS_NO_MEMORY; + } + } + + /* Destroy old image surface */ + cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); + /* Destroy old pixel 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; +} + +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_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 */ + 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; +} + +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); +} + +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_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_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, /* copy_page */ + NULL, /* show_page */ + NULL, /* set_clip_region */ + NULL, /* intersect_clip_path */ + _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 */ +}; |