/* Copyright (C) 2009 Red Hat, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "canvas_utils.h" #ifdef __GNUC__ #include #include #endif #ifdef WIN32 extern int gdi_handlers; #endif #ifndef ASSERT #define ASSERT(x) if (!(x)) { \ printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \ abort(); \ } #endif #ifndef CANVAS_ERROR #define CANVAS_ERROR(format, ...) { \ printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \ abort(); \ } #endif #ifndef ALIGN #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) #endif const cairo_user_data_key_t bitmap_data_type = {0}; const cairo_user_data_key_t bitmap_withstride_data_type = {0}; #ifdef WIN32 static void release_bitmap(void *bitmap_cache) { DeleteObject((HBITMAP)((BitmapCache *)bitmap_cache)->bitmap); CloseHandle(((BitmapCache *)bitmap_cache)->mutex); free(bitmap_cache); gdi_handlers--; } #endif static void release_withstride_bitmap(void *data) { free(data); } static inline cairo_surface_t *__surface_create_stride(cairo_format_t format, int width, int height, int stride) { uint8_t *data; uint8_t *stride_data; cairo_surface_t *surface; data = (uint8_t *)malloc(abs(stride) * height); if (stride < 0) { stride_data = data + (-stride) * (height - 1); } else { stride_data = data; } surface = cairo_image_surface_create_for_data(stride_data, format, width, height, stride); if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { free(data); CANVAS_ERROR("create surface failed, %s", cairo_status_to_string(cairo_surface_status(surface))); } if (cairo_surface_set_user_data(surface, &bitmap_withstride_data_type, data, release_withstride_bitmap) != CAIRO_STATUS_SUCCESS) { free(data); cairo_surface_destroy(surface); CANVAS_ERROR("set_user_data surface failed, %s", cairo_status_to_string(cairo_surface_status(surface))); } return surface; } #ifdef WIN32 cairo_surface_t *surface_create(HDC dc, cairo_format_t format, int width, int height, int top_down) #else cairo_surface_t * surface_create(cairo_format_t format, int width, int height, int top_down) #endif { #ifdef WIN32 /* * Windows xp allow only 10,000 of gdi handlers, considering the fact that * we limit here the number to 5000, we dont use atomic operations to sync * this calculation against the other canvases (in case of multiple * monitors), in worst case there will be little more than 5000 gdi * handlers. */ if (dc && gdi_handlers < 5000) { uint8_t *data; uint8_t *src; struct { BITMAPINFO inf; RGBQUAD palette[255]; } bitmap_info; int nstride; cairo_surface_t *surface; BitmapCache *bitmap_cache; memset(&bitmap_info, 0, sizeof(bitmap_info)); bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader); bitmap_info.inf.bmiHeader.biWidth = width; bitmap_info.inf.bmiHeader.biHeight = (!top_down) ? height : -height; bitmap_info.inf.bmiHeader.biPlanes = 1; switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: bitmap_info.inf.bmiHeader.biBitCount = 32; nstride = width * 4; break; case CAIRO_FORMAT_A8: bitmap_info.inf.bmiHeader.biBitCount = 8; nstride = ALIGN(width, 4); break; case CAIRO_FORMAT_A1: bitmap_info.inf.bmiHeader.biBitCount = 1; nstride = ALIGN(width, 32) / 8; break; default: CANVAS_ERROR("invalid format"); } bitmap_info.inf.bmiHeader.biCompression = BI_RGB; bitmap_cache = (BitmapCache *)malloc(sizeof(*bitmap_cache)); if (!bitmap_cache) { CANVAS_ERROR("malloc failed"); return NULL; } bitmap_cache->mutex = CreateMutex(NULL, 0, NULL); if (!bitmap_cache->mutex) { free(bitmap_cache); CANVAS_ERROR("Unable to CreateMutex"); return NULL; } bitmap_cache->bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0); if (!bitmap_cache->bitmap) { CloseHandle(bitmap_cache->mutex); free(bitmap_cache); CANVAS_ERROR("Unable to CreateDIBSection"); return NULL; } if (top_down) { src = data; } else { src = data + nstride * (height - 1); nstride = -nstride; } surface = cairo_image_surface_create_for_data(src, format, width, height, nstride); if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { CloseHandle(bitmap_cache->mutex); DeleteObject((HBITMAP)bitmap_cache->bitmap); free(bitmap_cache); CANVAS_ERROR("create surface failed, %s", cairo_status_to_string(cairo_surface_status(surface))); } if (cairo_surface_set_user_data(surface, &bitmap_data_type, bitmap_cache, release_bitmap) != CAIRO_STATUS_SUCCESS) { CloseHandle(bitmap_cache->mutex); cairo_surface_destroy(surface); DeleteObject((HBITMAP)bitmap_cache->bitmap); free(bitmap_cache); CANVAS_ERROR("set_user_data surface failed, %s", cairo_status_to_string(cairo_surface_status(surface))); } gdi_handlers++; return surface; } else { #endif if (top_down) { return cairo_image_surface_create(format, width, height); } else { // NOTE: we assume here that the lz decoders always decode to RGB32. int stride = 0; switch (format) { case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: stride = width * 4; break; case CAIRO_FORMAT_A8: stride = ALIGN(width, 4); break; case CAIRO_FORMAT_A1: stride = ALIGN(width, 32) / 8; break; default: CANVAS_ERROR("invalid format"); } stride = -stride; return __surface_create_stride(format, width, height, stride); } #ifdef WIN32 } #endif } #ifdef WIN32 cairo_surface_t *surface_create_stride(HDC dc, cairo_format_t format, int width, int height, int stride) #else cairo_surface_t *surface_create_stride(cairo_format_t format, int width, int height, int stride) #endif { #ifdef WIN32 if (dc) { if (abs(stride) == (width * 4)) { return surface_create(dc, format, width, height, (stride > 0)); } } #endif return __surface_create_stride(format, width, height, stride); } cairo_surface_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data, LzImageType type, int width, int height, int gross_pixels, int top_down) { int stride; int alpha; cairo_surface_t *surface = NULL; stride = (gross_pixels / height) * 4; if (!top_down) { stride = -stride; } if (type == LZ_IMAGE_TYPE_RGB32) { alpha = 0; } else if (type == LZ_IMAGE_TYPE_RGBA) { alpha = 1; } else { CANVAS_ERROR("unexpected image type"); } surface = surface_create_stride( #ifdef WIN32 canvas_data->dc, #endif alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height, stride); canvas_data->out_surface = surface; return surface; }