[cairo-commit] 9 commits - src/cairo-os2.h src/cairo-os2-private.h src/cairo-os2-surface.c

M. Joonas Pihlaja joonas at kemper.freedesktop.org
Sun Aug 8 12:39:47 PDT 2010


 src/cairo-os2-private.h |    1 
 src/cairo-os2-surface.c |  545 ++++++++++++++++++++++++++++++------------------
 src/cairo-os2.h         |   14 +
 3 files changed, 356 insertions(+), 204 deletions(-)

New commits:
commit 046b642db0782ab5e2a5c82988b21e05afe8e716
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 16:30:59 2010 +0300

    os2: Restore surface type checking in the get_extents method.

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 754f60c..b975828 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -714,7 +714,15 @@ static cairo_bool_t
 _cairo_os2_surface_get_extents (void                    *abstract_surface,
                                 cairo_rectangle_int_t   *rectangle)
 {
-    cairo_os2_surface_t *local_os2_surface = 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);
+    }
 
     rectangle->x = 0;
     rectangle->y = 0;
commit 78bdd87b7545f8e85632ac301a69da145727fcec
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 16:22:26 2010 +0300

    os2: Fix clipping to bounds when blitting pixels.
    
    The bounds checks on the rectangle to blit were done
    after converting target/source to inclusive/noninclusive
    coordinates rather than before, potentially causing an
    off-by-one in the sizes, since the same logic was applied
    to both inclusive and non-inclusive coordinates.  This
    patch enforces bounds up front.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 0e83545..754f60c 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -297,45 +297,37 @@ _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
                                 PRECTL               prcl_begin_paint_rect)
 {
     POINTL aptlPoints[4];
-    LONG lOldYInversion, rc = GPI_OK;
-
-    /* Enable Y Inversion for the HPS, so the
-     * GpiDrawBits will work with upside-top image, not with upside-down image!
-     */
-    lOldYInversion = GpiQueryYInversion (hps_begin_paint);
-    GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
-
-    /* Target coordinates (Noninclusive) */
-    aptlPoints[0].x = prcl_begin_paint_rect->xLeft;
-    aptlPoints[0].y = prcl_begin_paint_rect->yBottom;
-
-    aptlPoints[1].x = prcl_begin_paint_rect->xRight-1;
-    aptlPoints[1].y = prcl_begin_paint_rect->yTop-1;
+    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;
 
-    /* Source coordinates (Inclusive) */
-    aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
-    aptlPoints[2].y = prcl_begin_paint_rect->yBottom;
+    /* Set the Target & Source coordinates */
+    *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect;
+    *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect;
 
-    aptlPoints[3].x = prcl_begin_paint_rect->xRight;
-    aptlPoints[3].y = (prcl_begin_paint_rect->yTop);
+    /* Make the Target coordinates non-inclusive */
+    aptlPoints[1].x -= 1;
+    aptlPoints[1].y -= 1;
 
-    /* Some extra checking for limits
-     * (Dunno if really needed, but had some crashes sometimes without it,
-     *  while developing the code...)
+    /* Enable Y Inversion for the HPS, so  GpiDrawBits will
+     * work with upside-top image, not with upside-down image!
      */
-    {
-        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;
-        }
-    }
+    lOldYInversion = GpiQueryYInversion (hps_begin_paint);
+    GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
 
     /* Debug code to draw rectangle limits */
 #if 0
commit d0284687b32161212d6bc52309e2d5bb516dc3e8
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 03:53:24 2010 +0300

    os2: Tweak an inline declaration.
    
    C99 and cairo prefers "static inline <return type>" rather
    than "static <return type> inline".
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index cd909bd..0e83545 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -68,7 +68,7 @@
 /* Initialization counter: */
 static int cairo_os2_initialization_count = 0;
 
-static void inline
+static inline void
 DisableFPUException (void)
 {
     unsigned short usCW;
commit cb30340064a2ff24dc408e185c5a309a14f6c78c
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 03:33:10 2010 +0300

    os2: Consolidate error paths of cairo_os2_surface_create().
    
    Use a single code path to release the resources acquired
    for a surface that's been partially constructed, rather than
    have multiple error exits which each release the resources
    acquired so far.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 482c657..cd909bd 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -758,72 +758,48 @@ cairo_os2_surface_create (HPS hps_client_window,
                           int width,
                           int height)
 {
-    cairo_os2_surface_t *local_os2_surface;
+    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))
-    {
-        /* Invalid window size! */
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+    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) {
-        /* Not enough memory! */
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto error_exit;
     }
 
-    /* Initialize the OS/2 specific parts of the surface! */
+    memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t));
 
-    /* Create mutex semaphore */
-    rc = DosCreateMutexSem (NULL,
-                            &(local_os2_surface->hmtx_use_private_fields),
-                            0,
-                            FALSE);
-    if (rc != NO_ERROR) {
-        /* Could not create mutex semaphore! */
-        free (local_os2_surface);
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    /* 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;
     }
 
-    /* Save PS handle */
-    local_os2_surface->hps_client_window = hps_client_window;
-
-    /* Defaults */
-    local_os2_surface->hwnd_client_window = NULLHANDLE;
-    local_os2_surface->blit_as_changes = TRUE;
-    local_os2_surface->pixel_array_lend_count = 0;
-    rc = DosCreateEventSem (NULL,
-                            &(local_os2_surface->hev_pixel_array_came_back),
-                            0,
-                            FALSE);
-
-    if (rc != NO_ERROR) {
-        /* Could not create event semaphore! */
-        DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
-        free (local_os2_surface);
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    if (DosCreateEventSem (NULL,
+                           &(local_os2_surface->hev_pixel_array_came_back),
+                           0,
+                           FALSE))
+    {
+        status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
+        goto error_exit;
     }
 
-    /* Prepare BITMAPINFO2 structure for our buffer */
-    memset (&(local_os2_surface->bitmap_info), 0, sizeof (local_os2_surface->bitmap_info));
-    local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2);
-    local_os2_surface->bitmap_info.cx = width;
-    local_os2_surface->bitmap_info.cy = height;
-    local_os2_surface->bitmap_info.cPlanes = 1;
-    local_os2_surface->bitmap_info.cBitCount = 32;
-
-    /* Allocate memory for pixels */
     local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
-    if (!(local_os2_surface->pixels)) {
-        /* Not enough memory for the pixels! */
-        DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
-        DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
-        free (local_os2_surface);
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    if (!local_os2_surface->pixels) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto error_exit;
     }
 
     /* Create image surface from pixel array */
@@ -833,24 +809,48 @@ cairo_os2_surface_create (HPS hps_client_window,
                                              width,      /* Width */
                                              height,     /* Height */
                                              width * 4); /* Rowstride */
-
     status = local_os2_surface->image_surface->base.status;
-    if (status) {
-        /* Could not create image surface! */
-        _buffer_free (local_os2_surface->pixels);
-        DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
-        DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
-        free (local_os2_surface);
-        return _cairo_surface_create_in_error (status);
-    }
+    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 */
+                         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 occured */
+
+    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);
 }
 
 /**
commit 66e3d650fe90754c811195c75579ac7a3512b7be
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 03:12:12 2010 +0300

    os2: Document ownership of OS/2 objects passed to surfaces.
    
    Clarify the documentation for cairo_os2_surface_create()
    and cairo_os2_surface_set_hwnd() to note that the ownership
    of the presentation space and window respectively
    remains with the caller.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 85a2040..482c657 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -739,14 +739,15 @@ _cairo_os2_surface_get_extents (void                    *abstract_surface,
  * @height: the height of the surface
  *
  * Create a Cairo surface which is bound to a given presentation space (HPS).
- * The surface will be created to have the given size.
- * By default every change to the surface will be made visible immediately by
- * blitting it into the window. This can be changed with
+ * 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.
+ * 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
  *
@@ -1173,10 +1174,10 @@ _cairo_os2_surface_finish (void *abstract_surface)
  * @surface: the cairo surface to associate with the window handle
  * @hwnd_client_window: the window handle of the client window
  *
- * Sets window handle for surface. If Cairo wants to blit into the window
- * because it is set to blit as the surface changes (see
- * cairo_os2_surface_set_manual_window_refresh()), then there are two ways it
- * can choose:
+ * 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
commit 01525271544d5b88f87027e470fa85038d59bf8d
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 03:08:21 2010 +0300

    os2: Don't fake an Anchor Block when one isn't needed.
    
    The code was being tricky about passing in a HAB to
    DevOpenDC() even though one isn't needed at all under
    OS/2.  Pass in NULL instead.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index ead5f99..85a2040 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -446,7 +446,6 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
 {
     HPS hps;
     HDC hdc;
-    HAB hab;
     SIZEL sizlTemp;
     HBITMAP hbmpTemp;
     BITMAPINFO2 bmi2Temp;
@@ -464,19 +463,10 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
      *   -- 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);
+    hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE);
     if (!hdc) {
         return;
     }
@@ -484,7 +474,7 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
     /* 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,
+    hps = GpiCreatePS (0,
                        hdc,
                        &sizlTemp,
                        PU_PELS | GPIT_NORMAL | GPIA_ASSOC);
@@ -549,7 +539,7 @@ _cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
         GpiQueryBitmapBits (hps,
                             sizlTemp.cy - y - 1, /* lScanStart */
                             1,                   /* lScans */
-                            pchTemp,
+                            (PBYTE)pchTemp,
                             &bmi2Temp);
 
         /* Go for next line */
commit 883d9725e0f844e7d5cc4c50c75a812b5435702b
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 03:01:56 2010 +0300

    os2: Fix buffer allocator behaviour on arithmetic overflow.
    
    On arithmetic overflow the buffer allocator would attempt
    to allocate zero bytes instead of failing the allocation.
    Depending on the implementation of the underlying allocator
    being proxied, this may result in an allocation of a zero
    length buffer, causing all kinds of grief.  This patch
    causes arithmetic overflows to explicitly fail the allocation.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 1df61fd..ead5f99 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -171,43 +171,35 @@ cairo_os2_fini (void)
  */
 void *_buffer_alloc (size_t a, size_t b, const unsigned int size)
 {
-    /* check length like in the _cairo_malloc_abc macro, but we can leave
-     * away the unsigned casts as our arguments are unsigned already
-     */
-    size_t nbytes = b &&
-                    a >= INT32_MAX / b ? 0 : size &&
-                    a*b >= INT32_MAX / size ? 0 : a * b * size;
-    void *buffer = NULL;
-#ifdef OS2_USE_PLATFORM_ALLOC
-    APIRET rc = NO_ERROR;
+    size_t nbytes;
+    void  *buffer = NULL;
 
-    rc = DosAllocMem ((PPVOID)&buffer,
-                      nbytes,
-#ifdef OS2_HIGH_MEMORY           /* only if compiled with high-memory support, */
-                      OBJ_ANY |  /* we can allocate anywhere!                  */
-#endif
-                      PAG_READ | PAG_WRITE | PAG_COMMIT);
-    if (rc != NO_ERROR) {
-        /* should there for some reason be another error, let's return
-         * a null surface and free the buffer again, because that's
-         * how a malloc failure would look like
-         */
-        if (rc != ERROR_NOT_ENOUGH_MEMORY && buffer) {
-            DosFreeMem (buffer);
-        }
+    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
-
-    /* This does not seem to be needed, malloc'd space is usually
-     * already zero'd out!
-     */
-    /*
-     * memset (buffer, 0x00, nbytes);
-     */
-
     return buffer;
 }
 
commit b54037828c5b338b2f1dcbe48201225b258c1e1e
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 02:40:20 2010 +0300

    os2: Fix blitting 24 bpp pixel data.
    
    There's a format mismatch between cairo's pixel formats
    and OS/2 24 bpp data: the individual pixels in OS/2 take
    only three bytes per pixel, but there needs to be padding
    between lines to make the start of a line always be aligned
    at a DWORD boundary.
    
    This patch fixes the format conversion code to include
    the required padding between rows.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html

diff --git a/src/cairo-os2-private.h b/src/cairo-os2-private.h
index fa4c8e8..829dd3c 100644
--- a/src/cairo-os2-private.h
+++ b/src/cairo-os2-private.h
@@ -61,6 +61,7 @@ typedef struct _cairo_os2_surface
 
     /* General flags: */
     cairo_bool_t           blit_as_changes;
+    cairo_bool_t           use_24bpp;
 } cairo_os2_surface_t;
 
 #endif /* CAIRO_OS2_PRIVATE_H */
diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 38cf6d6..1df61fd 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -366,63 +366,81 @@ _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
         }
     }
 #endif
-    rc = GpiDrawBits (hps_begin_paint,
-                      surface->pixels,
-                      &(surface->bitmap_info),
-                      4,
-                      aptlPoints,
-                      ROP_SRCCOPY,
-                      BBO_IGNORE);
-
-    if (rc != GPI_OK) {
-        /* if GpiDrawBits () failed then this is most likely because the
+    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
          */
-        BITMAPINFOHEADER2 bmpheader;
-        unsigned char *pchPixBuf, *pchPixSource;
-        void *pBufStart;
-        ULONG ulPixels;
-
-        /* allocate temporary pixel buffer */
-        pchPixBuf = (unsigned char *) _buffer_alloc (surface->bitmap_info.cy,
-                                                     surface->bitmap_info.cx,
-                                                     3);
-        pchPixSource = surface->pixels; /* start at beginning of pixel buffer */
-        pBufStart = pchPixBuf; /* remember beginning of the new pixel buffer */
-
-        /* copy the first three bytes for each pixel but skip over the fourth */
-        for (ulPixels = 0; ulPixels < surface->bitmap_info.cx * surface->bitmap_info.cy; ulPixels++)
-        {
-            /* copy BGR from source buffer */
-            *pchPixBuf++ = *pchPixSource++;
-            *pchPixBuf++ = *pchPixSource++;
-            *pchPixBuf++ = *pchPixSource++;
-            pchPixSource++; /* jump over alpha channel in source buffer */
-        }
-
-        /* jump back to start of the buffer for display and cleanup */
-        pchPixBuf = pBufStart;
+        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));
 
-        /* set up the bitmap header, but this time with 24bit depth only */
-        memset (&bmpheader, 0, sizeof (bmpheader));
-        bmpheader.cbFix = sizeof (BITMAPINFOHEADER2);
-        bmpheader.cx = surface->bitmap_info.cx;
-        bmpheader.cy = surface->bitmap_info.cy;
-        bmpheader.cPlanes = surface->bitmap_info.cPlanes;
-        bmpheader.cBitCount = 24;
-        rc = GpiDrawBits (hps_begin_paint,
-                          pchPixBuf,
-                          (PBITMAPINFO2)&bmpheader,
-                          4,
-                          aptlPoints,
-                          ROP_SRCCOPY,
-                          BBO_IGNORE);
+        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;
+            }
 
-        _buffer_free (pchPixBuf);
+            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 */
commit 5aadecb3ac03d379c2c8113566fa8062843df42e
Author: Rich Walsh <DragText at E-vertise.Com>
Date:   Sat Aug 7 02:30:01 2010 +0300

    os2: New API to create surfaces without a persistent HPS.
    
    It's not always necessary to create a presentation space (HPS) for
    surface tied to a window, as the user may have one already.  This patch
    introduces three new functions to create a surface without an HPS
    and let the user manage an HPS associated with the surface.
    
    Thread "OS/2 surface fixes" on cairo-l:
    http://lists.cairographics.org/archives/cairo/2010-July/020343.html
    
    Mozilla Bugzilla:
    https://bugzilla.mozilla.org/show_bug.cgi?id=557159

diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 4ca88b7..38cf6d6 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -33,6 +33,7 @@
  *
  * Contributor(s):
  *     Peter Weilbacher <mozilla at Weilbacher.org>
+ *     Rich Walsh <dragtext at e-vertise.com>
  */
 
 #include "cairoint.h"
@@ -852,6 +853,51 @@ cairo_os2_surface_create (HPS hps_client_window,
 }
 
 /**
+ * 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
@@ -1016,6 +1062,7 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface,
 {
     cairo_os2_surface_t *local_os2_surface;
     RECTL rclTemp;
+    HPS hpsTemp = 0;
 
     local_os2_surface = (cairo_os2_surface_t *) surface;
     if ((!local_os2_surface) ||
@@ -1025,9 +1072,19 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface,
         return;
     }
 
-    /* Manage defaults (NULLs) */
-    if (!hps_begin_paint)
+    /* 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! */
@@ -1048,6 +1105,8 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface,
         != NO_ERROR)
     {
         /* Could not get mutex! */
+        if (hpsTemp)
+            WinReleasePS(hpsTemp);
         return;
     }
 
@@ -1075,6 +1134,9 @@ cairo_os2_surface_refresh_window (cairo_surface_t *surface,
     }
 
     DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+    if (hpsTemp)
+        WinReleasePS(hpsTemp);
 }
 
 static cairo_status_t
@@ -1216,6 +1278,74 @@ cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
     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,
@@ -1333,5 +1463,11 @@ static const cairo_surface_backend_t cairo_os2_surface_backend = {
     NULL, /* stroke */
     NULL, /* fill */
     NULL, /* show_glyphs */
-    NULL  /* snapshot */
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+    NULL, /* has_show_text_glyphs */
+    NULL  /* show_text_glyphs */
 };
diff --git a/src/cairo-os2.h b/src/cairo-os2.h
index ae9073c..d23f2de 100644
--- a/src/cairo-os2.h
+++ b/src/cairo-os2.h
@@ -33,6 +33,7 @@
  *
  * Contributor(s):
  *     Peter Weilbacher <mozilla at Weilbacher.org>
+ *     Rich Walsh <dragtext at e-vertise.com>
  */
 
 #ifndef _CAIRO_OS2_H_
@@ -65,6 +66,11 @@ cairo_os2_surface_create (HPS hps_client_window,
                           int width,
                           int height);
 
+cairo_public cairo_surface_t *
+cairo_os2_surface_create_for_window (HWND hwnd_client_window,
+                                     int  width,
+                                     int  height);
+
 cairo_public void
 cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
                             HWND             hwnd_client_window);
@@ -87,6 +93,14 @@ cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
 cairo_public cairo_bool_t
 cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface);
 
+cairo_public cairo_status_t
+cairo_os2_surface_get_hps (cairo_surface_t *surface,
+                           HPS             *hps);
+
+cairo_public cairo_status_t
+cairo_os2_surface_set_hps (cairo_surface_t *surface,
+                           HPS              hps);
+
 #else  /* CAIRO_HAS_OS2_SURFACE */
 # error Cairo was not compiled with support for the OS/2 backend
 #endif /* CAIRO_HAS_OS2_SURFACE */


More information about the cairo-commit mailing list