1 /* vim: set sw=4 sts=4 et cin: */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright (c) 2005-2006 netlabs.org
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is
32 * Doodle <doodle@scenergy.dfmk.hu>
35 * Peter Weilbacher <mozilla@Weilbacher.org>
36 * Rich Walsh <dragtext@e-vertise.com>
41 #include "cairo-os2-private.h"
42 #include "cairo-default-context-private.h"
43 #include "cairo-error-private.h"
44 #include "cairo-surface-fallback-private.h"
45 #include "cairo-image-surface-private.h"
48 #include <fontconfig/fontconfig.h>
52 #ifdef BUILD_CAIRO_DLL
53 # include "cairo-os2.h"
55 # include <emx/startup.h>
60 * Here comes the extra API for the OS/2 platform. Currently it consists
61 * of two extra functions, the cairo_os2_init() and the
62 * cairo_os2_fini(). Both of them are called automatically if
63 * Cairo is compiled to be a DLL file, but you have to call them before
64 * using the Cairo API if you link to Cairo statically!
66 * You'll also find the code in here which deals with DLL initialization
67 * and termination, if the code is built to be a DLL.
68 * (if BUILD_CAIRO_DLL is defined)
71 /* Initialization counter: */
72 static int cairo_os2_initialization_count
= 0;
75 DisableFPUException (void)
79 /* Some OS/2 PM API calls modify the FPU Control Word,
80 * but forget to restore it.
82 * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
83 * so to be sure, we disable Invalid Opcode FPU exception
84 * before using FPU stuffs.
86 usCW
= _control87 (0, 0);
87 usCW
= usCW
| EM_INVALID
| 0x80;
88 _control87 (usCW
, MCW_EM
| 0x80);
94 * Initializes the Cairo library. This function is automatically called if
95 * Cairo was compiled to be a DLL (however it's not a problem if it's called
96 * multiple times). But if you link to Cairo statically, you have to call it
97 * once to set up Cairo's internal structures and mutexes.
102 cairo_os2_init (void)
104 /* This may initialize some stuffs, like create mutex semaphores etc.. */
106 cairo_os2_initialization_count
++;
107 if (cairo_os2_initialization_count
> 1) return;
109 DisableFPUException ();
111 #if CAIRO_HAS_FC_FONT
112 /* Initialize FontConfig */
116 CAIRO_MUTEX_INITIALIZE ();
122 * Uninitializes the Cairo library. This function is automatically called if
123 * Cairo was compiled to be a DLL (however it's not a problem if it's called
124 * multiple times). But if you link to Cairo statically, you have to call it
125 * once to shut down Cairo, to let it free all the resources it has allocated.
130 cairo_os2_fini (void)
132 /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */
134 if (cairo_os2_initialization_count
<= 0) return;
135 cairo_os2_initialization_count
--;
136 if (cairo_os2_initialization_count
> 0) return;
138 DisableFPUException ();
140 cairo_debug_reset_static_data ();
142 #if CAIRO_HAS_FC_FONT
144 /* Uninitialize FontConfig */
150 /* It can happen that the libraries we use have memory leaks,
151 * so there are still memory chunks allocated at this point.
152 * In these cases, Watcom might still have a bigger memory chunk,
153 * called "the heap" allocated from the OS.
154 * As we want to minimize the memory we lose from the point of
155 * view of the OS, we call this function to shrink that heap
156 * as much as possible.
160 /* GCC has a heapmin function that approximately corresponds to
161 * what the Watcom function does
168 * This function calls the allocation function depending on which
169 * method was compiled into the library: it can be native allocation
170 * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free).
171 * Actually, for pixel buffers that we use this function for, cairo
172 * uses _cairo_malloc_abc, so we use that here, too. And use the
173 * change to check the size argument
175 void *_buffer_alloc (size_t a
, size_t b
, const unsigned int size
)
180 if (!a
|| !b
|| !size
||
181 a
>= INT32_MAX
/ b
|| a
*b
>= INT32_MAX
/ size
) {
184 nbytes
= a
* b
* size
;
186 #ifdef OS2_USE_PLATFORM_ALLOC
187 /* Using OBJ_ANY on a machine that isn't configured for hi-mem
188 * will cause ERROR_INVALID_PARAMETER. If this occurs, or this
189 * build doesn't have hi-mem enabled, fall back to using lo-mem.
191 #ifdef OS2_HIGH_MEMORY
192 if (!DosAllocMem (&buffer
, nbytes
,
193 OBJ_ANY
| PAG_READ
| PAG_WRITE
| PAG_COMMIT
))
196 if (DosAllocMem (&buffer
, nbytes
,
197 PAG_READ
| PAG_WRITE
| PAG_COMMIT
))
200 /* Clear the malloc'd buffer the way DosAllocMem() does. */
201 buffer
= malloc (nbytes
);
203 memset (buffer
, 0, nbytes
);
210 * This function selects the free function depending on which
211 * allocation method was compiled into the library
213 void _buffer_free (void *buffer
)
215 #ifdef OS2_USE_PLATFORM_ALLOC
223 * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and
224 * the LibMain code moved to cairo-system.c. It should also call
225 * cairo_debug_reset_static_data() instead of duplicating its logic...
228 #ifdef BUILD_CAIRO_DLL
229 /* The main DLL entry for DLL initialization and uninitialization */
230 /* Only include this code if we're about to build a DLL. */
234 LibMain (unsigned hmod
,
235 unsigned termination
)
237 unsigned long _System
238 _DLL_InitTerm (unsigned long hModule
,
239 unsigned long termination
)
243 /* Unloading the DLL */
247 /* Uninitialize RTL of GCC */
253 /* Loading the DLL */
255 /* Initialize RTL of GCC */
256 if (_CRT_init () != 0)
266 #endif /* BUILD_CAIRO_DLL */
269 * The following part of the source file contains the code which might
270 * be called the "core" of the OS/2 backend support. This contains the
271 * OS/2 surface support functions and structures.
274 /* Forward declaration */
275 static const cairo_surface_backend_t cairo_os2_surface_backend
;
278 * GpiEnableYInversion = PMGPI.723
279 * GpiQueryYInversion = PMGPI.726
280 * BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
281 * LONG APIENTRY GpiQueryYInversion (HPS hps);
283 BOOL APIENTRY
GpiEnableYInversion (HPS hps
, LONG lHeight
);
284 LONG APIENTRY
GpiQueryYInversion (HPS hps
);
287 /* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */
288 LONG APIENTRY
GpiDrawBits (HPS hps
,
290 PBITMAPINFO2 pbmiInfoTable
,
298 _cairo_os2_surface_blit_pixels (cairo_os2_surface_t
*surface
,
300 PRECTL prcl_begin_paint_rect
)
302 POINTL aptlPoints
[4];
306 /* Check the limits (may not be necessary) */
307 if (prcl_begin_paint_rect
->xLeft
< 0)
308 prcl_begin_paint_rect
->xLeft
= 0;
309 if (prcl_begin_paint_rect
->yBottom
< 0)
310 prcl_begin_paint_rect
->yBottom
= 0;
311 if (prcl_begin_paint_rect
->xRight
> (LONG
) surface
->bitmap_info
.cx
)
312 prcl_begin_paint_rect
->xRight
= (LONG
) surface
->bitmap_info
.cx
;
313 if (prcl_begin_paint_rect
->yTop
> (LONG
) surface
->bitmap_info
.cy
)
314 prcl_begin_paint_rect
->yTop
= (LONG
) surface
->bitmap_info
.cy
;
316 /* Exit if the rectangle is empty */
317 if (prcl_begin_paint_rect
->xLeft
>= prcl_begin_paint_rect
->xRight
||
318 prcl_begin_paint_rect
->yBottom
>= prcl_begin_paint_rect
->yTop
)
321 /* Set the Target & Source coordinates */
322 *((PRECTL
)&aptlPoints
[0]) = *prcl_begin_paint_rect
;
323 *((PRECTL
)&aptlPoints
[2]) = *prcl_begin_paint_rect
;
325 /* Make the Target coordinates non-inclusive */
326 aptlPoints
[1].x
-= 1;
327 aptlPoints
[1].y
-= 1;
329 /* Enable Y Inversion for the HPS, so GpiDrawBits will
330 * work with upside-top image, not with upside-down image!
332 lOldYInversion
= GpiQueryYInversion (hps_begin_paint
);
333 GpiEnableYInversion (hps_begin_paint
, surface
->bitmap_info
.cy
-1);
335 /* Debug code to draw rectangle limits */
339 unsigned char *pixels
;
341 pixels
= surface
->pixels
;
342 for (x
= 0; x
< surface
->bitmap_info
.cx
; x
++) {
343 for (y
= 0; y
< surface
->bitmap_info
.cy
; y
++) {
347 (x
>= surface
->bitmap_info
.cx
-1) ||
348 (y
>= surface
->bitmap_info
.cy
-1))
350 pixels
[y
*surface
->bitmap_info
.cx
*4+x
*4] = 255;
356 if (!surface
->use_24bpp
) {
357 rc
= GpiDrawBits (hps_begin_paint
,
359 &(surface
->bitmap_info
),
365 surface
->use_24bpp
= TRUE
;
368 if (surface
->use_24bpp
) {
369 /* If GpiDrawBits () failed then this is most likely because the
370 * display driver could not handle a 32bit bitmap. So we need to
371 * - create a buffer that only contains 3 bytes per pixel
372 * - change the bitmap info header to contain 24bit
373 * - pass the new buffer to GpiDrawBits () again
374 * - clean up the new buffer
377 unsigned char *pchPixBuf
;
378 unsigned char *pchTarget
;
384 /* Set up the bitmap header, but this time for 24bit depth. */
385 bmpinfo
= surface
->bitmap_info
;
386 bmpinfo
.cBitCount
= 24;
388 /* The start of each row has to be DWORD aligned. Calculate the
389 * of number aligned bytes per row, the total size of the bitmap,
390 * and the number of padding bytes at the end of each row.
392 ulX
= (((bmpinfo
.cx
* bmpinfo
.cBitCount
) + 31) / 32) * 4;
393 bmpinfo
.cbImage
= ulX
* bmpinfo
.cy
;
394 ulPad
= ulX
- bmpinfo
.cx
* 3;
396 /* Allocate temporary pixel buffer. If the rows don't need
397 * padding, it has to be 1 byte larger than the size of the
398 * bitmap or else the high-order byte from the last source
399 * row will end up in unallocated memory.
401 pchPixBuf
= (unsigned char *)_buffer_alloc (1, 1,
402 bmpinfo
.cbImage
+ (ulPad
? 0 : 1));
405 /* Copy 4 bytes from the source but advance the target ptr only
406 * 3 bytes, so the high-order alpha byte will be overwritten by
407 * the next copy. At the end of each row, skip over the padding.
409 pchTarget
= pchPixBuf
;
410 pulSource
= (ULONG
*)surface
->pixels
;
411 for (ulY
= bmpinfo
.cy
; ulY
; ulY
--) {
412 for (ulX
= bmpinfo
.cx
; ulX
; ulX
--) {
413 *((ULONG
*)pchTarget
) = *pulSource
++;
419 rc
= GpiDrawBits (hps_begin_paint
,
427 surface
->use_24bpp
= FALSE
;
429 _buffer_free (pchPixBuf
);
433 /* Restore Y inversion */
434 GpiEnableYInversion (hps_begin_paint
, lOldYInversion
);
438 _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t
*surface
,
440 PRECTL prcl_begin_paint_rect
)
446 BITMAPINFO2 bmi2Temp
;
447 POINTL aptlPoints
[4];
449 unsigned char *pchTemp
;
451 /* To copy pixels from screen to our buffer, we do the following steps:
453 * - Blit pixels from screen to a HBITMAP:
454 * -- Create Memory Device Context
455 * -- Create a PS into it
456 * -- Create a HBITMAP
457 * -- Select HBITMAP into memory PS
458 * -- Blit dirty pixels from screen to HBITMAP
459 * - Copy HBITMAP lines (pixels) into our buffer
463 /* Create a memory device context */
464 hdc
= DevOpenDC (0, OD_MEMORY
,"*",0L, NULL
, NULLHANDLE
);
469 /* Create a memory PS */
470 sizlTemp
.cx
= prcl_begin_paint_rect
->xRight
- prcl_begin_paint_rect
->xLeft
;
471 sizlTemp
.cy
= prcl_begin_paint_rect
->yTop
- prcl_begin_paint_rect
->yBottom
;
472 hps
= GpiCreatePS (0,
475 PU_PELS
| GPIT_NORMAL
| GPIA_ASSOC
);
481 /* Create an uninitialized bitmap. */
482 /* Prepare BITMAPINFO2 structure for our buffer */
483 memset (&bmi2Temp
, 0, sizeof (bmi2Temp
));
484 bmi2Temp
.cbFix
= sizeof (BITMAPINFOHEADER2
);
485 bmi2Temp
.cx
= sizlTemp
.cx
;
486 bmi2Temp
.cy
= sizlTemp
.cy
;
487 bmi2Temp
.cPlanes
= 1;
488 bmi2Temp
.cBitCount
= 32;
490 hbmpTemp
= GpiCreateBitmap (hps
,
491 (PBITMAPINFOHEADER2
) &bmi2Temp
,
502 /* Select the bitmap into the memory device context. */
503 GpiSetBitmap (hps
, hbmpTemp
);
505 /* Target coordinates (Noninclusive) */
509 aptlPoints
[1].x
= sizlTemp
.cx
;
510 aptlPoints
[1].y
= sizlTemp
.cy
;
512 /* Source coordinates (Inclusive) */
513 aptlPoints
[2].x
= prcl_begin_paint_rect
->xLeft
;
514 aptlPoints
[2].y
= surface
->bitmap_info
.cy
- prcl_begin_paint_rect
->yBottom
;
516 aptlPoints
[3].x
= prcl_begin_paint_rect
->xRight
;
517 aptlPoints
[3].y
= surface
->bitmap_info
.cy
- prcl_begin_paint_rect
->yTop
;
519 /* Blit pixels from screen to bitmap */
527 /* Now we have to extract the pixels from the bitmap. */
530 (prcl_begin_paint_rect
->yBottom
)*surface
->bitmap_info
.cx
*4 +
531 prcl_begin_paint_rect
->xLeft
*4;
532 for (y
= 0; y
< sizlTemp
.cy
; y
++) {
533 /* Get one line of pixels */
534 GpiQueryBitmapBits (hps
,
535 sizlTemp
.cy
- y
- 1, /* lScanStart */
540 /* Go for next line */
541 pchTemp
+= surface
->bitmap_info
.cx
*4;
544 /* Clean up resources */
545 GpiSetBitmap (hps
, (HBITMAP
) NULL
);
546 GpiDeleteBitmap (hbmpTemp
);
551 static cairo_status_t
552 _cairo_os2_surface_acquire_source_image (void *abstract_surface
,
553 cairo_image_surface_t
**image_out
,
556 cairo_os2_surface_t
*surface
= (cairo_os2_surface_t
*) abstract_surface
;
558 DosRequestMutexSem (surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
);
560 /* Increase lend counter */
561 surface
->pixel_array_lend_count
++;
563 *image_out
= surface
->image_surface
;
566 DosReleaseMutexSem (surface
->hmtx_use_private_fields
);
568 return CAIRO_STATUS_SUCCESS
;
572 _cairo_os2_surface_release_source_image (void *abstract_surface
,
573 cairo_image_surface_t
*image
,
576 cairo_os2_surface_t
*surface
= (cairo_os2_surface_t
*) abstract_surface
;
578 /* Decrease Lend counter! */
579 DosRequestMutexSem (surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
);
581 if (surface
->pixel_array_lend_count
> 0)
582 surface
->pixel_array_lend_count
--;
583 DosPostEventSem (surface
->hev_pixel_array_came_back
);
585 DosReleaseMutexSem (surface
->hmtx_use_private_fields
);
588 static cairo_image_surface_t
*
589 _cairo_os2_surface_map_to_image (void *abstract_surface
,
590 const cairo_rectangle_int_t
*extents
)
592 cairo_os2_surface_t
*surface
= (cairo_os2_surface_t
*) abstract_surface
;
594 DosRequestMutexSem (surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
);
595 /* Increase lend counter */
596 surface
->pixel_array_lend_count
++;
597 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
600 *image_out
= _cairo_surface_create_for_rectangle_int (surface
->image_surface
,
603 return CAIRO_STATUS_SUCCESS
;
606 static cairo_int_status_t
607 _cairo_os2_surface_unmap_image (void *abstract_surface
,
608 cairo_image_surface_t
*image
)
610 cairo_os2_surface_t
*surface
= (cairo_os2_surface_t
*) abstract_surface
;
612 /* So, we got back the image, and if all goes well, then
613 * something has been changed inside the interest_rect.
614 * So, we blit it to the screen!
616 if (surface
->blit_as_changes
) {
619 /* Get mutex, we'll work with the pixel array! */
620 if (DosRequestMutexSem (surface
->hmtx_use_private_fields
,
621 SEM_INDEFINITE_WAIT
) != NO_ERROR
)
623 /* Could not get mutex! */
627 rclToBlit
.xLeft
= image
->base
.device_transform_inverse
.x0
;
628 rclToBlit
.xRight
= rclToBlit
.xLeft
+ image
->width
; /* Noninclusive */
629 rclToBlit
.yTop
= image
->base
.device_transform_inverse
.y0
;
630 rclToBlit
.yBottom
= rclToBlit
.yTop
+ image
->height
; /* Noninclusive */
632 if (surface
->hwnd_client_window
) {
633 /* We know the HWND, so let's invalidate the window region,
634 * so the application will redraw itself, using the
635 * cairo_os2_surface_refresh_window () API from its own PM thread.
637 * This is the safe method, which should be preferred every time.
639 rclToBlit
.yTop
= surface
->bitmap_info
.cy
- rclToBlit
.yTop
;
640 rclToBlit
.yBottom
= surface
->bitmap_info
.cy
- rclToBlit
.yTop
;
641 WinInvalidateRect (surface
->hwnd_client_window
,
645 /* We don't know the HWND, so try to blit the pixels from here!
646 * Please note that it can be problematic if this is not the PM thread!
648 * It can cause internal PM stuffs to be screwed up, for some reason.
649 * Please always tell the HWND to the surface using the
650 * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
651 * from your WM_PAINT, if it's possible!
653 _cairo_os2_surface_blit_pixels (surface
,
654 surface
->hps_client_window
,
658 DosReleaseMutexSem (surface
->hmtx_use_private_fields
);
660 /* Also decrease Lend counter! */
661 DosRequestMutexSem (surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
);
663 if (surface
->pixel_array_lend_count
> 0)
664 surface
->pixel_array_lend_count
--;
665 DosPostEventSem (surface
->hev_pixel_array_came_back
);
667 DosReleaseMutexSem (surface
->hmtx_use_private_fields
);
671 _cairo_os2_surface_get_extents (void *abstract_surface
,
672 cairo_rectangle_int_t
*rectangle
)
674 cairo_os2_surface_t
*surface
= (cairo_os2_surface_t
*) abstract_surface
;
678 rectangle
->width
= surface
->bitmap_info
.cx
;
679 rectangle
->height
= surface
->bitmap_info
.cy
;
685 * cairo_os2_surface_create:
686 * @hps_client_window: the presentation handle to bind the surface to
687 * @width: the width of the surface
688 * @height: the height of the surface
690 * Create a Cairo surface which is bound to a given presentation space (HPS).
691 * The caller retains ownership of the HPS and must dispose of it after the
692 * the surface has been destroyed. The surface will be created to have the
693 * given size. By default every change to the surface will be made visible
694 * immediately by blitting it into the window. This can be changed with
695 * cairo_os2_surface_set_manual_window_refresh().
696 * Note that the surface will contain garbage when created, so the pixels
697 * have to be initialized by hand first. You can use the Cairo functions to
698 * fill it with black, or use cairo_surface_mark_dirty() to fill the surface
699 * with pixels from the window/HPS.
701 * Return value: the newly created surface
706 cairo_os2_surface_create (HPS hps_client_window
,
710 cairo_os2_surface_t
*local_os2_surface
= 0;
711 cairo_status_t status
;
714 /* Check the size of the window */
715 if ((width
<= 0) || (height
<= 0)) {
716 status
= _cairo_error (CAIRO_STATUS_INVALID_SIZE
);
720 /* Allocate an OS/2 surface structure. */
721 local_os2_surface
= (cairo_os2_surface_t
*) malloc (sizeof (cairo_os2_surface_t
));
722 if (!local_os2_surface
) {
723 status
= _cairo_error (CAIRO_STATUS_NO_MEMORY
);
727 memset(local_os2_surface
, 0, sizeof(cairo_os2_surface_t
));
729 /* Allocate resources: mutex & event semaphores and the pixel buffer */
730 if (DosCreateMutexSem (NULL
,
731 &(local_os2_surface
->hmtx_use_private_fields
),
735 status
= _cairo_error (CAIRO_STATUS_DEVICE_ERROR
);
739 if (DosCreateEventSem (NULL
,
740 &(local_os2_surface
->hev_pixel_array_came_back
),
744 status
= _cairo_error (CAIRO_STATUS_DEVICE_ERROR
);
748 local_os2_surface
->pixels
= (unsigned char *) _buffer_alloc (height
, width
, 4);
749 if (!local_os2_surface
->pixels
) {
750 status
= _cairo_error (CAIRO_STATUS_NO_MEMORY
);
754 /* Create image surface from pixel array */
755 local_os2_surface
->image_surface
= (cairo_image_surface_t
*)
756 cairo_image_surface_create_for_data (local_os2_surface
->pixels
,
760 width
* 4); /* Rowstride */
761 status
= local_os2_surface
->image_surface
->base
.status
;
765 /* Set values for OS/2-specific data that aren't zero/NULL/FALSE.
766 * Note: hps_client_window may be null if this was called by
767 * cairo_os2_surface_create_for_window().
769 local_os2_surface
->hps_client_window
= hps_client_window
;
770 local_os2_surface
->blit_as_changes
= TRUE
;
772 /* Prepare BITMAPINFO2 structure for our buffer */
773 local_os2_surface
->bitmap_info
.cbFix
= sizeof (BITMAPINFOHEADER2
);
774 local_os2_surface
->bitmap_info
.cx
= width
;
775 local_os2_surface
->bitmap_info
.cy
= height
;
776 local_os2_surface
->bitmap_info
.cPlanes
= 1;
777 local_os2_surface
->bitmap_info
.cBitCount
= 32;
779 /* Initialize base surface */
780 _cairo_surface_init (&local_os2_surface
->base
,
781 &cairo_os2_surface_backend
,
783 _cairo_content_from_format (CAIRO_FORMAT_ARGB32
));
785 /* Successful exit */
786 return (cairo_surface_t
*)local_os2_surface
;
790 /* This point will only be reached if an error occurred */
792 if (local_os2_surface
) {
793 if (local_os2_surface
->pixels
)
794 _buffer_free (local_os2_surface
->pixels
);
795 if (local_os2_surface
->hev_pixel_array_came_back
)
796 DosCloseEventSem (local_os2_surface
->hev_pixel_array_came_back
);
797 if (local_os2_surface
->hmtx_use_private_fields
)
798 DosCloseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
799 free (local_os2_surface
);
802 return _cairo_surface_create_in_error (status
);
806 * cairo_os2_surface_create_for_window:
807 * @hwnd_client_window: the window handle to bind the surface to
808 * @width: the width of the surface
809 * @height: the height of the surface
811 * Create a Cairo surface which is bound to a given window; the caller retains
812 * ownership of the window. This is a convenience function for use with
813 * windows that will only be updated when cairo_os2_surface_refresh_window()
814 * is called (usually in response to a WM_PAINT message). It avoids the need
815 * to create a persistent HPS for every window and assumes that one will be
816 * supplied by the caller when a cairo function needs one. If it isn't, an
817 * HPS will be created on-the-fly and released before the function which needs
820 * Return value: the newly created surface
825 cairo_os2_surface_create_for_window (HWND hwnd_client_window
,
829 cairo_os2_surface_t
*local_os2_surface
;
831 /* A window handle must be provided. */
832 if (!hwnd_client_window
) {
833 return _cairo_surface_create_in_error (
834 _cairo_error (CAIRO_STATUS_NO_MEMORY
));
837 /* Create the surface. */
838 local_os2_surface
= (cairo_os2_surface_t
*)
839 cairo_os2_surface_create (0, width
, height
);
841 /* If successful, save the hwnd & turn off automatic repainting. */
842 if (!local_os2_surface
->image_surface
->base
.status
) {
843 local_os2_surface
->hwnd_client_window
= hwnd_client_window
;
844 local_os2_surface
->blit_as_changes
= FALSE
;
847 return (cairo_surface_t
*)local_os2_surface
;
851 * cairo_os2_surface_set_size:
852 * @surface: the cairo surface to resize
853 * @new_width: the new width of the surface
854 * @new_height: the new height of the surface
855 * @timeout: timeout value in milliseconds
857 * When the client window is resized, call this API to set the new size in the
858 * underlying surface accordingly. This function will reallocate everything,
859 * so you'll have to redraw everything in the surface after this call.
860 * The surface will contain garbage after the resizing. So the notes of
861 * cairo_os2_surface_create() apply here, too.
863 * The timeout value specifies how long the function should wait on other parts
864 * of the program to release the buffers. It is necessary, because it can happen
865 * that Cairo is just drawing something into the surface while we want to
866 * destroy and recreate it.
868 * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
869 * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
870 * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
871 * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
872 * timeout happened before all the buffers were released
877 cairo_os2_surface_set_size (cairo_surface_t
*surface
,
882 cairo_os2_surface_t
*local_os2_surface
;
883 unsigned char *pchNewPixels
;
885 cairo_image_surface_t
*pNewImageSurface
;
887 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
888 if ((!local_os2_surface
) ||
889 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
891 /* Invalid parameter (wrong surface)! */
892 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
);
895 if ((new_width
<= 0) ||
899 return _cairo_error (CAIRO_STATUS_INVALID_SIZE
);
902 /* Allocate memory for new stuffs */
903 pchNewPixels
= (unsigned char *) _buffer_alloc (new_height
, new_width
, 4);
905 /* Not enough memory for the pixels!
906 * Everything remains the same!
908 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
911 /* Create image surface from new pixel array */
912 pNewImageSurface
= (cairo_image_surface_t
*)
913 cairo_image_surface_create_for_data (pchNewPixels
,
915 new_width
, /* Width */
916 new_height
, /* Height */
917 new_width
* 4); /* Rowstride */
919 if (pNewImageSurface
->base
.status
) {
920 /* Could not create image surface!
921 * Everything remains the same!
923 _buffer_free (pchNewPixels
);
924 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
927 /* Okay, new memory allocated, so it's time to swap old buffers
930 if (DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
)!=NO_ERROR
) {
931 /* Could not get mutex!
932 * Everything remains the same!
934 cairo_surface_destroy ((cairo_surface_t
*) pNewImageSurface
);
935 _buffer_free (pchNewPixels
);
936 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
939 /* We have to make sure that we won't destroy a surface which
940 * is lent to some other code (Cairo is drawing into it)!
942 while (local_os2_surface
->pixel_array_lend_count
> 0) {
944 DosResetEventSem (local_os2_surface
->hev_pixel_array_came_back
, &ulPostCount
);
945 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
946 /* Wait for somebody to return the pixels! */
947 rc
= DosWaitEventSem (local_os2_surface
->hev_pixel_array_came_back
, timeout
);
948 if (rc
!= NO_ERROR
) {
949 /* Either timeout or something wrong... Exit. */
950 cairo_surface_destroy ((cairo_surface_t
*) pNewImageSurface
);
951 _buffer_free (pchNewPixels
);
952 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
954 /* Okay, grab mutex and check counter again! */
955 if (DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
)
958 /* Could not get mutex!
959 * Everything remains the same!
961 cairo_surface_destroy ((cairo_surface_t
*) pNewImageSurface
);
962 _buffer_free (pchNewPixels
);
963 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
967 /* Destroy old image surface */
968 cairo_surface_destroy ((cairo_surface_t
*) (local_os2_surface
->image_surface
));
969 /* Destroy old pixel buffer */
970 _buffer_free (local_os2_surface
->pixels
);
971 /* Set new image surface */
972 local_os2_surface
->image_surface
= pNewImageSurface
;
973 /* Set new pixel buffer */
974 local_os2_surface
->pixels
= pchNewPixels
;
975 /* Change bitmap2 structure */
976 local_os2_surface
->bitmap_info
.cx
= new_width
;
977 local_os2_surface
->bitmap_info
.cy
= new_height
;
979 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
980 return CAIRO_STATUS_SUCCESS
;
984 * cairo_os2_surface_refresh_window:
985 * @surface: the cairo surface to refresh
986 * @hps_begin_paint: the presentation handle of the window to refresh
987 * @prcl_begin_paint_rect: the rectangle to redraw
989 * This function can be used to force a repaint of a given area of the client
990 * window. It should usually be called from the WM_PAINT processing of the
991 * window procedure. However, it can be called any time a given part of the
992 * window has to be updated.
994 * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
995 * of the window procedure, but you can also get the HPS using WinGetPS, and you
996 * can assemble your own update rectangle by hand.
997 * If hps_begin_paint is %NULL, the function will use the HPS passed into
998 * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
999 * will query the current window size and repaint the whole window.
1001 * Cairo assumes that if you set the HWND to the surface using
1002 * cairo_os2_surface_set_hwnd(), this function will be called by the application
1003 * every time it gets a WM_PAINT for that HWND. If the HWND is set in the
1004 * surface, Cairo uses this function to handle dirty areas too.
1009 cairo_os2_surface_refresh_window (cairo_surface_t
*surface
,
1010 HPS hps_begin_paint
,
1011 PRECTL prcl_begin_paint_rect
)
1013 cairo_os2_surface_t
*local_os2_surface
;
1017 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1018 if ((!local_os2_surface
) ||
1019 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1021 /* Invalid parameter (wrong surface)! */
1025 /* If an HPS wasn't provided, see if we can get one. */
1026 if (!hps_begin_paint
) {
1027 hps_begin_paint
= local_os2_surface
->hps_client_window
;
1028 if (!hps_begin_paint
) {
1029 if (local_os2_surface
->hwnd_client_window
) {
1030 hpsTemp
= WinGetPS(local_os2_surface
->hwnd_client_window
);
1031 hps_begin_paint
= hpsTemp
;
1033 /* No HPS & no way to get one, so exit */
1034 if (!hps_begin_paint
)
1039 if (prcl_begin_paint_rect
== NULL
) {
1040 /* Update the whole window! */
1042 rclTemp
.xRight
= local_os2_surface
->bitmap_info
.cx
;
1043 rclTemp
.yTop
= local_os2_surface
->bitmap_info
.cy
;
1044 rclTemp
.yBottom
= 0;
1046 /* Use the rectangle we got passed as parameter! */
1047 rclTemp
.xLeft
= prcl_begin_paint_rect
->xLeft
;
1048 rclTemp
.xRight
= prcl_begin_paint_rect
->xRight
;
1049 rclTemp
.yTop
= local_os2_surface
->bitmap_info
.cy
- prcl_begin_paint_rect
->yBottom
;
1050 rclTemp
.yBottom
= local_os2_surface
->bitmap_info
.cy
- prcl_begin_paint_rect
->yTop
;
1053 /* Get mutex, we'll work with the pixel array! */
1054 if (DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
)
1057 /* Could not get mutex! */
1059 WinReleasePS(hpsTemp
);
1063 if ((local_os2_surface
->dirty_area_present
) &&
1064 (local_os2_surface
->rcl_dirty_area
.xLeft
== rclTemp
.xLeft
) &&
1065 (local_os2_surface
->rcl_dirty_area
.xRight
== rclTemp
.xRight
) &&
1066 (local_os2_surface
->rcl_dirty_area
.yTop
== rclTemp
.yTop
) &&
1067 (local_os2_surface
->rcl_dirty_area
.yBottom
== rclTemp
.yBottom
))
1069 /* Aha, this call was because of a dirty area, so in this case we
1070 * have to blit the pixels from the screen to the surface!
1072 local_os2_surface
->dirty_area_present
= FALSE
;
1073 _cairo_os2_surface_get_pixels_from_screen (local_os2_surface
,
1077 /* Okay, we have the surface, have the HPS
1078 * (might be from WinBeginPaint () or from WinGetPS () )
1079 * Now blit there the stuffs!
1081 _cairo_os2_surface_blit_pixels (local_os2_surface
,
1086 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
1089 WinReleasePS(hpsTemp
);
1092 static cairo_status_t
1093 _cairo_os2_surface_finish (void *abstract_surface
)
1095 cairo_os2_surface_t
*local_os2_surface
;
1097 local_os2_surface
= (cairo_os2_surface_t
*) abstract_surface
;
1098 if ((!local_os2_surface
) ||
1099 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1101 /* Invalid parameter (wrong surface)! */
1102 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
);
1105 DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
);
1107 /* Destroy old image surface */
1108 cairo_surface_destroy ((cairo_surface_t
*) (local_os2_surface
->image_surface
));
1109 /* Destroy old pixel buffer */
1110 _buffer_free (local_os2_surface
->pixels
);
1111 DosCloseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
1112 DosCloseEventSem (local_os2_surface
->hev_pixel_array_came_back
);
1114 /* The memory itself will be free'd by the cairo_surface_destroy ()
1118 return CAIRO_STATUS_SUCCESS
;
1122 * cairo_os2_surface_set_hwnd:
1123 * @surface: the cairo surface to associate with the window handle
1124 * @hwnd_client_window: the window handle of the client window
1126 * Sets window handle for surface; the caller retains ownership of the window.
1127 * If Cairo wants to blit into the window because it is set to blit as the
1128 * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
1129 * there are two ways it can choose:
1130 * If it knows the HWND of the surface, then it invalidates that area, so the
1131 * application will get a WM_PAINT message and it can call
1132 * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
1133 * will use the HPS it got at surface creation time, and blit the pixels itself.
1134 * It's also a solution, but experience shows that if this happens from a non-PM
1135 * thread, then it can screw up PM internals.
1137 * So, best solution is to set the HWND for the surface after the surface
1138 * creation, so every blit will be done from application's message processing
1139 * loop, which is the safest way to do.
1144 cairo_os2_surface_set_hwnd (cairo_surface_t
*surface
,
1145 HWND hwnd_client_window
)
1147 cairo_os2_surface_t
*local_os2_surface
;
1149 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1150 if ((!local_os2_surface
) ||
1151 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1153 /* Invalid parameter (wrong surface)! */
1157 if (DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
)
1160 /* Could not get mutex! */
1164 local_os2_surface
->hwnd_client_window
= hwnd_client_window
;
1166 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
1170 * cairo_os2_surface_set_manual_window_refresh:
1171 * @surface: the cairo surface to set the refresh mode for
1172 * @manual_refresh: the switch for manual surface refresh
1174 * This API can tell Cairo if it should show every change to this surface
1175 * immediately in the window or if it should be cached and will only be visible
1176 * once the user calls cairo_os2_surface_refresh_window() explicitly. If the
1177 * HWND was not set in the cairo surface, then the HPS will be used to blit the
1178 * graphics. Otherwise it will invalidate the given window region so the user
1179 * will get the WM_PAINT message to redraw that area of the window.
1181 * So, if you're only interested in displaying the final result after several
1182 * drawing operations, you might get better performance if you put the surface
1183 * into manual refresh mode by passing a true value to this function. Then call
1184 * cairo_os2_surface_refresh() whenever desired.
1189 cairo_os2_surface_set_manual_window_refresh (cairo_surface_t
*surface
,
1190 cairo_bool_t manual_refresh
)
1192 cairo_os2_surface_t
*local_os2_surface
;
1194 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1195 if ((!local_os2_surface
) ||
1196 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1198 /* Invalid parameter (wrong surface)! */
1202 local_os2_surface
->blit_as_changes
= !manual_refresh
;
1206 * cairo_os2_surface_get_manual_window_refresh:
1207 * @surface: the cairo surface to query the refresh mode from
1209 * This space left intentionally blank.
1211 * Return value: current refresh mode of the surface (true by default)
1216 cairo_os2_surface_get_manual_window_refresh (cairo_surface_t
*surface
)
1218 cairo_os2_surface_t
*local_os2_surface
;
1220 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1221 if ((!local_os2_surface
) ||
1222 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1224 /* Invalid parameter (wrong surface)! */
1228 return !(local_os2_surface
->blit_as_changes
);
1232 * cairo_os2_surface_get_hps:
1233 * @surface: the cairo surface to be querued
1234 * @hps: HPS currently associated with the surface (if any)
1236 * This API retrieves the HPS associated with the surface.
1238 * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
1239 * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
1240 * %CAIRO_STATUS_NULL_POINTER if the hps argument is null
1245 cairo_os2_surface_get_hps (cairo_surface_t
*surface
,
1248 cairo_os2_surface_t
*local_os2_surface
;
1250 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1251 if ((!local_os2_surface
) ||
1252 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1254 /* Invalid parameter (wrong surface)! */
1255 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
);
1259 return _cairo_error (CAIRO_STATUS_NULL_POINTER
);
1261 *hps
= local_os2_surface
->hps_client_window
;
1263 return CAIRO_STATUS_SUCCESS
;
1267 * cairo_os2_surface_set_hps:
1268 * @surface: the cairo surface to associate with the HPS
1269 * @hps: new HPS to be associated with the surface (the HPS may be null)
1271 * This API replaces the HPS associated with the surface with a new one.
1272 * The caller retains ownership of the HPS and must dispose of it after
1273 * the surface has been destroyed or it has been replaced by another
1274 * call to this function.
1276 * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
1277 * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
1282 cairo_os2_surface_set_hps (cairo_surface_t
*surface
,
1285 cairo_os2_surface_t
*local_os2_surface
;
1287 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1288 if ((!local_os2_surface
) ||
1289 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1291 /* Invalid parameter (wrong surface)! */
1292 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
);
1294 local_os2_surface
->hps_client_window
= hps
;
1296 return CAIRO_STATUS_SUCCESS
;
1299 static cairo_status_t
1300 _cairo_os2_surface_mark_dirty_rectangle (void *surface
,
1306 cairo_os2_surface_t
*local_os2_surface
;
1309 local_os2_surface
= (cairo_os2_surface_t
*) surface
;
1310 if ((!local_os2_surface
) ||
1311 (local_os2_surface
->base
.backend
!= &cairo_os2_surface_backend
))
1313 /* Invalid parameter (wrong surface)! */
1314 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH
);
1317 /* Get mutex, we'll work with the pixel array! */
1318 if (DosRequestMutexSem (local_os2_surface
->hmtx_use_private_fields
, SEM_INDEFINITE_WAIT
)
1321 /* Could not get mutex! */
1322 return CAIRO_STATUS_NO_MEMORY
;
1325 /* Check for defaults */
1327 width
= local_os2_surface
->bitmap_info
.cx
;
1329 height
= local_os2_surface
->bitmap_info
.cy
;
1331 if (local_os2_surface
->hwnd_client_window
) {
1332 /* We know the HWND, so let's invalidate the window region,
1333 * so the application will redraw itself, using the
1334 * cairo_os2_surface_refresh_window () API from its own PM thread.
1335 * From that function we'll note that it's not a redraw but a
1336 * dirty-rectangle deal stuff, so we'll handle the things from
1339 * This is the safe method, which should be preferred every time.
1341 rclToBlit
.xLeft
= x
;
1342 rclToBlit
.xRight
= x
+ width
; /* Noninclusive */
1343 rclToBlit
.yTop
= local_os2_surface
->bitmap_info
.cy
- (y
);
1344 rclToBlit
.yBottom
= local_os2_surface
->bitmap_info
.cy
- (y
+ height
); /* Noninclusive */
1347 if (local_os2_surface
->dirty_area_present
) {
1348 /* Yikes, there is already a dirty area which should be
1349 * cleaned up, but we'll overwrite it. Sorry.
1350 * TODO: Something clever should be done here.
1355 /* Set up dirty area reminder stuff */
1356 memcpy (&(local_os2_surface
->rcl_dirty_area
), &rclToBlit
, sizeof (RECTL
));
1357 local_os2_surface
->dirty_area_present
= TRUE
;
1359 /* Invalidate window area */
1360 WinInvalidateRect (local_os2_surface
->hwnd_client_window
,
1364 /* We don't know the HWND, so try to blit the pixels from here!
1365 * Please note that it can be problematic if this is not the PM thread!
1367 * It can cause internal PM stuffs to be scewed up, for some reason.
1368 * Please always tell the HWND to the surface using the
1369 * cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
1370 * from your WM_PAINT, if it's possible!
1373 rclToBlit
.xLeft
= x
;
1374 rclToBlit
.xRight
= x
+ width
; /* Noninclusive */
1375 rclToBlit
.yBottom
= y
;
1376 rclToBlit
.yTop
= y
+ height
; /* Noninclusive */
1377 /* Now get the pixels from the screen! */
1378 _cairo_os2_surface_get_pixels_from_screen (local_os2_surface
,
1379 local_os2_surface
->hps_client_window
,
1383 DosReleaseMutexSem (local_os2_surface
->hmtx_use_private_fields
);
1385 return CAIRO_STATUS_SUCCESS
;
1388 static const cairo_surface_backend_t cairo_os2_surface_backend
= {
1389 CAIRO_SURFACE_TYPE_OS2
,
1390 _cairo_os2_surface_finish
,
1391 _cairo_default_context_create
,
1393 NULL
, /* create_similar */
1394 NULL
, /* create_similar_image */
1395 _cairo_os2_surface_map_to_image
,
1396 _cairo_os2_surface_unmap_image
,
1398 _cairo_surface_default_source
,
1399 _cairo_os2_surface_acquire_source_image
,
1400 _cairo_os2_surface_release_source_image
,
1401 NULL
, /* snapshot */
1403 _cairo_os2_surface_get_extents
,
1404 NULL
, /* get_font_options */
1407 _cairo_os2_surface_mark_dirty_rectangle
,
1409 _cairo_surface_fallback_paint
,
1410 _cairo_surface_fallback_mask
,
1411 _cairo_surface_fallback_fill
,
1412 _cairo_surface_fallback_stroke
,
1413 NULL
, /* fill/stroke */
1414 _cairo_surface_fallback_glyphs
,