static cairo_status_t _cairo_os2_surface_finish (void *abstract_surface) { cairo_os2_surface_t *local_os2_surface; local_os2_surface = (cairo_os2_surface_t *) abstract_surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); /* Destroy old image surface */ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); /* Destroy old pixel buffer */ _buffer_free (local_os2_surface->pixels); DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); /* The memory itself will be free'd by the cairo_surface_destroy () * who called us. */ return CAIRO_STATUS_SUCCESS; }
/** * cairo_os2_surface_set_size: * @surface: the cairo surface to resize * @new_width: the new width of the surface * @new_height: the new height of the surface * @timeout: timeout value in milliseconds * * When the client window is resized, call this API to set the new size in the * underlying surface accordingly. This function will reallocate everything, * so you'll have to redraw everything in the surface after this call. * The surface will contain garbage after the resizing. So the notes of * cairo_os2_surface_create() apply here, too. * * The timeout value specifies how long the function should wait on other parts * of the program to release the buffers. It is necessary, because it can happen * that Cairo is just drawing something into the surface while we want to * destroy and recreate it. * * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, * %CAIRO_STATUS_INVALID_SIZE for invalid sizes * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the * timeout happened before all the buffers were released * * Since: 1.4 **/ int cairo_os2_surface_set_size (cairo_surface_t *surface, int new_width, int new_height, int timeout) { cairo_os2_surface_t *local_os2_surface; unsigned char *pchNewPixels; int rc; cairo_image_surface_t *pNewImageSurface; local_os2_surface = (cairo_os2_surface_t *) surface; if ((!local_os2_surface) || (local_os2_surface->base.backend != &cairo_os2_surface_backend)) { /* Invalid parameter (wrong surface)! */ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); } if ((new_width <= 0) || (new_height <= 0)) { /* Invalid size! */ return _cairo_error (CAIRO_STATUS_INVALID_SIZE); } /* Allocate memory for new stuffs */ pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4); if (!pchNewPixels) { /* Not enough memory for the pixels! * Everything remains the same! */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Create image surface from new pixel array */ pNewImageSurface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (pchNewPixels, CAIRO_FORMAT_ARGB32, new_width, /* Width */ new_height, /* Height */ new_width * 4); /* Rowstride */ if (pNewImageSurface->base.status) { /* Could not create image surface! * Everything remains the same! */ _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Okay, new memory allocated, so it's time to swap old buffers * to new ones! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { /* Could not get mutex! * Everything remains the same! */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* We have to make sure that we won't destroy a surface which * is lent to some other code (Cairo is drawing into it)! */ while (local_os2_surface->pixel_array_lend_count > 0) { ULONG ulPostCount; DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount); DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); /* Wait for somebody to return the pixels! */ rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout); if (rc != NO_ERROR) { /* Either timeout or something wrong... Exit. */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Okay, grab mutex and check counter again! */ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) != NO_ERROR) { /* Could not get mutex! * Everything remains the same! */ cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); _buffer_free (pchNewPixels); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } /* Destroy old image surface */ cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); /* Destroy old pixel buffer */ _buffer_free (local_os2_surface->pixels); /* Set new image surface */ local_os2_surface->image_surface = pNewImageSurface; /* Set new pixel buffer */ local_os2_surface->pixels = pchNewPixels; /* Change bitmap2 structure */ local_os2_surface->bitmap_info.cx = new_width; local_os2_surface->bitmap_info.cy = new_height; DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); return CAIRO_STATUS_SUCCESS; }
/** * cairo_os2_surface_create: * @hps_client_window: the presentation handle to bind the surface to * @width: the width of the surface * @height: the height of the surface * * Create a Cairo surface which is bound to a given presentation space (HPS). * The surface will be created to have the given size. * By default every change to the surface will be made visible immediately by * blitting it into the window. This can be changed with * cairo_os2_surface_set_manual_window_refresh(). * Note that the surface will contain garbage when created, so the pixels have * to be initialized by hand first. You can use the Cairo functions to fill it * with black, or use cairo_surface_mark_dirty() to fill the surface with pixels * from the window/HPS. * * Return value: the newly created surface * * Since: 1.4 **/ cairo_surface_t * cairo_os2_surface_create (HPS hps_client_window, int width, int height) { cairo_os2_surface_t *local_os2_surface; 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)); } 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)); } /* 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! */ free (local_os2_surface); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } /* 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)); } /* 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)); } /* Create image surface from pixel array */ local_os2_surface->image_surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (local_os2_surface->pixels, CAIRO_FORMAT_ARGB32, width, /* Width */ height, /* Height */ width * 4); /* Rowstride */ status = local_os2_surface->image_surface->base.status; if (status) { /* 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); } /* 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; }
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, 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; /* 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 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 * 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; /* 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); _buffer_free (pchPixBuf); } /* Restore Y inversion */ GpiEnableYInversion (hps_begin_paint, lOldYInversion); }
/** * cairo_os2_surface_create: * @hps_client_window: the presentation handle to bind the surface to * @width: the width of the surface * @height: the height of the surface * * Create a Cairo surface which is bound to a given presentation space (HPS). * The caller retains ownership of the HPS and must dispose of it after the * the surface has been destroyed. The surface will be created to have the * given size. By default every change to the surface will be made visible * immediately by blitting it into the window. This can be changed with * cairo_os2_surface_set_manual_window_refresh(). * Note that the surface will contain garbage when created, so the pixels * have to be initialized by hand first. You can use the Cairo functions to * fill it with black, or use cairo_surface_mark_dirty() to fill the surface * with pixels from the window/HPS. * * Return value: the newly created surface * * Since: 1.4 **/ cairo_surface_t * cairo_os2_surface_create (HPS hps_client_window, int width, int height) { cairo_os2_surface_t *local_os2_surface = 0; cairo_status_t status; int rc; /* Check the size of the window */ if ((width <= 0) || (height <= 0)) { status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); goto error_exit; } /* Allocate an OS/2 surface structure. */ local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t)); if (!local_os2_surface) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto error_exit; } memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); /* Allocate resources: mutex & event semaphores and the pixel buffer */ if (DosCreateMutexSem (NULL, &(local_os2_surface->hmtx_use_private_fields), 0, FALSE)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto error_exit; } if (DosCreateEventSem (NULL, &(local_os2_surface->hev_pixel_array_came_back), 0, FALSE)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto error_exit; } local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); if (!local_os2_surface->pixels) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto error_exit; } /* Create image surface from pixel array */ local_os2_surface->image_surface = (cairo_image_surface_t *) cairo_image_surface_create_for_data (local_os2_surface->pixels, CAIRO_FORMAT_ARGB32, width, /* Width */ height, /* Height */ width * 4); /* Rowstride */ status = local_os2_surface->image_surface->base.status; if (status) goto error_exit; /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. * Note: hps_client_window may be null if this was called by * cairo_os2_surface_create_for_window(). */ local_os2_surface->hps_client_window = hps_client_window; local_os2_surface->blit_as_changes = TRUE; /* Prepare BITMAPINFO2 structure for our buffer */ local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); local_os2_surface->bitmap_info.cx = width; local_os2_surface->bitmap_info.cy = height; local_os2_surface->bitmap_info.cPlanes = 1; local_os2_surface->bitmap_info.cBitCount = 32; /* Initialize base surface */ _cairo_surface_init (&local_os2_surface->base, &cairo_os2_surface_backend, NULL, /* device */ _cairo_content_from_format (CAIRO_FORMAT_ARGB32)); /* Successful exit */ return (cairo_surface_t *)local_os2_surface; error_exit: /* This point will only be reached if an error occurred */ if (local_os2_surface) { if (local_os2_surface->pixels) _buffer_free (local_os2_surface->pixels); if (local_os2_surface->hev_pixel_array_came_back) DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); if (local_os2_surface->hmtx_use_private_fields) DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); free (local_os2_surface); } return _cairo_surface_create_in_error (status); }
static void _cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, HPS hps_begin_paint, PRECTL prcl_begin_paint_rect) { POINTL aptlPoints[4]; LONG lOldYInversion; LONG rc = GPI_OK; /* Check the limits (may not be necessary) */ if (prcl_begin_paint_rect->xLeft < 0) prcl_begin_paint_rect->xLeft = 0; if (prcl_begin_paint_rect->yBottom < 0) prcl_begin_paint_rect->yBottom = 0; if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; /* Exit if the rectangle is empty */ if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) return; /* Set the Target & Source coordinates */ *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; /* Make the Target coordinates non-inclusive */ aptlPoints[1].x -= 1; aptlPoints[1].y -= 1; /* Enable Y Inversion for the HPS, so GpiDrawBits will * work with upside-top image, not with upside-down image! */ lOldYInversion = GpiQueryYInversion (hps_begin_paint); GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); /* Debug code to draw rectangle limits */ #if 0 { int x, y; unsigned char *pixels; pixels = surface->pixels; for (x = 0; x < surface->bitmap_info.cx; x++) { for (y = 0; y < surface->bitmap_info.cy; y++) { if ((x == 0) || (y == 0) || (x == y) || (x >= surface->bitmap_info.cx-1) || (y >= surface->bitmap_info.cy-1)) { pixels[y*surface->bitmap_info.cx*4+x*4] = 255; } } } } #endif if (!surface->use_24bpp) { rc = GpiDrawBits (hps_begin_paint, surface->pixels, &(surface->bitmap_info), 4, aptlPoints, ROP_SRCCOPY, BBO_IGNORE); if (rc != GPI_OK) surface->use_24bpp = TRUE; } if (surface->use_24bpp) { /* If GpiDrawBits () failed then this is most likely because the * display driver could not handle a 32bit bitmap. So we need to * - create a buffer that only contains 3 bytes per pixel * - change the bitmap info header to contain 24bit * - pass the new buffer to GpiDrawBits () again * - clean up the new buffer */ BITMAPINFO2 bmpinfo; unsigned char *pchPixBuf; unsigned char *pchTarget; ULONG *pulSource; ULONG ulX; ULONG ulY; ULONG ulPad; /* Set up the bitmap header, but this time for 24bit depth. */ bmpinfo = surface->bitmap_info; bmpinfo.cBitCount = 24; /* The start of each row has to be DWORD aligned. Calculate the * of number aligned bytes per row, the total size of the bitmap, * and the number of padding bytes at the end of each row. */ ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; bmpinfo.cbImage = ulX * bmpinfo.cy; ulPad = ulX - bmpinfo.cx * 3; /* Allocate temporary pixel buffer. If the rows don't need * padding, it has to be 1 byte larger than the size of the * bitmap or else the high-order byte from the last source * row will end up in unallocated memory. */ pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, bmpinfo.cbImage + (ulPad ? 0 : 1)); if (pchPixBuf) { /* Copy 4 bytes from the source but advance the target ptr only * 3 bytes, so the high-order alpha byte will be overwritten by * the next copy. At the end of each row, skip over the padding. */ pchTarget = pchPixBuf; pulSource = (ULONG*)surface->pixels; for (ulY = bmpinfo.cy; ulY; ulY--) { for (ulX = bmpinfo.cx; ulX; ulX--) { *((ULONG*)pchTarget) = *pulSource++; pchTarget += 3; } pchTarget += ulPad; } rc = GpiDrawBits (hps_begin_paint, pchPixBuf, &bmpinfo, 4, aptlPoints, ROP_SRCCOPY, BBO_IGNORE); if (rc != GPI_OK) surface->use_24bpp = FALSE; _buffer_free (pchPixBuf); } } /* Restore Y inversion */ GpiEnableYInversion (hps_begin_paint, lOldYInversion); }