diff options
author | M Joonas Pihlaja <jpihlaja@cc.helsinki.fi> | 2009-02-15 15:10:30 +0200 |
---|---|---|
committer | M Joonas Pihlaja <jpihlaja@cc.helsinki.fi> | 2009-02-15 15:10:30 +0200 |
commit | 11ea8fbcc8634b130a22237dadf338c0d735adb1 (patch) | |
tree | 1e5a14ec69759702dc68bbda6f215af6c803db62 /cairosdl.c |
Import cairosdl.
Diffstat (limited to 'cairosdl.c')
-rw-r--r-- | cairosdl.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/cairosdl.c b/cairosdl.c new file mode 100644 index 0000000..27dec4a --- /dev/null +++ b/cairosdl.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2009 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include <assert.h> +#include "cairosdl.h" + +/* from cairo-unpremultiply.c */ +void +_cairosdl_blit_and_unpremultiply ( + void *target_buffer, + size_t target_stride, + void const *source_buffer, + size_t source_stride, + int width, + int height); + +/* from cairo-premultiply.c */ +void +_cairosdl_blit_and_premultiply ( + void *target_buffer, + size_t target_stride, + void const *source_buffer, + size_t source_stride, + int width, + int height); + +/* + * Surface functions + */ + +/* We're hanging the SDL_Surface as a user datum on the + * cairo_surface_t representing it using this key. */ +static cairo_user_data_key_t const CAIROSDL_TARGET_KEY[1]; + +static void +sdl_surface_destroy_func (void *param) +{ + SDL_Surface *sdl_surface = (SDL_Surface *)param; + if (sdl_surface != NULL) + SDL_FreeSurface (sdl_surface); +} + +cairo_surface_t * +cairosdl_surface_create ( + SDL_Surface *sdl_surface) +{ + cairo_surface_t *target; + cairo_format_t format; + int is_dirty; + + /* Cairo only supports a limited number of pixels formats. Make + * sure the surface format is compatible. */ + if (sdl_surface->format->BytesPerPixel != 4 || + sdl_surface->format->BitsPerPixel != 32) + goto unsupported_format; + + if (sdl_surface->format->Rmask != CAIROSDL_RMASK || + sdl_surface->format->Gmask != CAIROSDL_GMASK || + sdl_surface->format->Bmask != CAIROSDL_BMASK) + goto unsupported_format; + + switch (sdl_surface->format->Amask) { + case CAIROSDL_AMASK: + format = CAIRO_FORMAT_ARGB32; + break; + case 0: + format = CAIRO_FORMAT_RGB24; + break; + default: + goto unsupported_format; + } + + /* Make the target point to either the SDL_Surface's data itself + * or a shadow image surface if we need to unpremultiply pixels. */ + if (format == CAIRO_FORMAT_RGB24) { + /* The caller is expected to have locked the surface (_if_ it + * needs locking) so that sdl_surface->pixels is valid and + * constant for the lifetime of the cairo_surface_t. However, + * we're told not to call any OS functions when a surface is + * locked, so we really shouldn't call + * cairo_image_surface_create () as it will malloc, so really + * if the surface needs locking this shouldn't be used. + * + * However, it turns out malloc is actually safe on many (all?) + * platforms so we'll just go ahead anyway. */ + target = cairo_image_surface_create_for_data (sdl_surface->pixels, + format, + sdl_surface->w, + sdl_surface->h, + sdl_surface->pitch); + is_dirty = 0; + } + else { + /* Need a shadow image surface. */ + target = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + sdl_surface->w, + sdl_surface->h); + is_dirty = 1; + } + + if (cairo_surface_status (target) == CAIRO_STATUS_SUCCESS) { + sdl_surface->refcount++; + cairo_surface_set_user_data (target, + CAIROSDL_TARGET_KEY, + sdl_surface, + sdl_surface_destroy_func); + + if (is_dirty) + cairosdl_surface_mark_dirty (target); + } + + return target; + + unsupported_format: + /* Nasty kludge to get a cairo surface in CAIRO_INVALID_FORMAT + * state. */ + return cairo_image_surface_create ( + (cairo_format_t)-1, 0, 0); +} + +SDL_Surface * +cairosdl_surface_get_target ( + cairo_surface_t *surface) +{ + return cairo_surface_get_user_data (surface, CAIROSDL_TARGET_KEY); +} + +static cairo_status_t +_cairosdl_surface_obtain_SDL_buffer( + cairo_surface_t *surface, + unsigned char **OUT_buffer, + size_t *OUT_stride, + size_t *OUT_width, + size_t *OUT_height) +{ + SDL_Surface *sdl_surface = cairosdl_surface_get_target (surface); + if (sdl_surface == NULL) + return CAIRO_STATUS_NULL_POINTER; + + if (OUT_buffer) + *OUT_buffer = sdl_surface->pixels; + if (OUT_stride) + *OUT_stride = sdl_surface->pitch; + if (OUT_width) + *OUT_width = sdl_surface->w; + if (OUT_height) + *OUT_height = sdl_surface->h; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairosdl_surface_obtain_shadow_buffer( + cairo_surface_t *surface, + unsigned char **OUT_buffer, + size_t *OUT_stride, + size_t *OUT_width, + size_t *OUT_height) +{ + cairo_format_t format; + + if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + format = cairo_image_surface_get_format (surface); + if (format != CAIRO_FORMAT_ARGB32) + return CAIRO_STATUS_INVALID_FORMAT; + + if (OUT_buffer != NULL) + *OUT_buffer = cairo_image_surface_get_data (surface); + if (OUT_stride != NULL) + *OUT_stride = cairo_image_surface_get_stride (surface); + if (OUT_width != NULL) + *OUT_width = cairo_image_surface_get_width (surface); + if (OUT_height != NULL) + *OUT_height = cairo_image_surface_get_height (surface); + return CAIRO_STATUS_SUCCESS; +} + +void +cairosdl_surface_flush_rects ( + cairo_surface_t *surface, + int num_rects, + SDL_Rect const *rects) +{ + unsigned char *source_bytes; + size_t source_stride; + size_t source_width; + size_t source_height; + + unsigned char *target_bytes; + size_t target_stride; + size_t target_width; + size_t target_height; + + int width, height; + cairo_status_t status; + + if (num_rects <= 0) + return; + + cairo_surface_flush (surface); + + status = _cairosdl_surface_obtain_SDL_buffer (surface, + &target_bytes, + &target_stride, + &target_width, + &target_height); + if (status != CAIRO_STATUS_SUCCESS) + return; /* no buffer -> nothing to do */ + + status = _cairosdl_surface_obtain_shadow_buffer (surface, + &source_bytes, + &source_stride, + &source_width, + &source_height); + if (status != CAIRO_STATUS_SUCCESS) + return; /* no buffer -> nothing to do */ + + width = source_width < target_width ? source_width : target_width; + height = source_height < target_height ? source_height : target_height; + assert(width >= 0 && height >= 0); + + while (num_rects-- > 0) { + Sint32 x = rects->x; + Sint32 y = rects->y; + Sint32 w = rects->w; + Sint32 h = rects->h; + rects++; + + if (x <= 0) { w += x; x = 0; } + if (y <= 0) { h += y; y = 0; } + if (x + w >= width) w = width - x; + if (y + h >= height) h = height - y; + + _cairosdl_blit_and_unpremultiply ( + target_bytes + target_stride*y + x, target_stride, + source_bytes + source_stride*y + x, source_stride, + w, h); + } +} + +void +cairosdl_surface_mark_dirty_rects ( + cairo_surface_t *surface, + int num_rects, + SDL_Rect const *rects) +{ + unsigned char *source_bytes = NULL; + size_t source_stride = 0; + size_t source_width = 32767; + size_t source_height = 32767; + + unsigned char *target_bytes = NULL; + size_t target_stride = 0; + size_t target_width = 32767; + size_t target_height = 32767; + + int width, height; + cairo_status_t status; + int have_buffers = 1; + + if (num_rects <= 0) + return; + + status = _cairosdl_surface_obtain_SDL_buffer (surface, + &source_bytes, + &source_stride, + &source_width, + &source_height); + if (status != CAIRO_STATUS_SUCCESS) + have_buffers = 0; + + status = _cairosdl_surface_obtain_shadow_buffer (surface, + &target_bytes, + &target_stride, + &target_width, + &target_height); + if (status != CAIRO_STATUS_SUCCESS) + have_buffers = 0; + + width = source_width < target_width ? source_width : target_width; + height = source_height < target_height ? source_height : target_height; + assert(width >= 0 && height >= 0); + + while (num_rects-- > 0) { + Sint32 x = rects->x; + Sint32 y = rects->y; + Sint32 w = rects->w; + Sint32 h = rects->h; + rects++; + + if (x <= 0) { w += x; x = 0; } + if (y <= 0) { h += y; y = 0; } + if (x >= width || y >= height) continue; + if (x + w >= width) w = width - x; + if (y + h >= height) h = height - y; + if (w <= 0 || h <= 0) continue; + + if (have_buffers) { + _cairosdl_blit_and_premultiply ( + target_bytes + target_stride*y + x, target_stride, + source_bytes + source_stride*y + x, source_stride, + w, h); + } + + cairo_surface_mark_dirty_rectangle (surface, x, y, w, h); + } +} + +static SDL_Rect +make_rect(int x, int y, int w, int h) +{ + static SDL_Rect const empty_rect = {0,0,0,0}; + SDL_Rect r; + + /* Clamp the rect to [0,32768). */ + if (w <= 0 || h <= 0) + return empty_rect; + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if ((unsigned)w > 32767) w = 32767; + if ((unsigned)h > 32767) h = 32767; + + r.x = x; + r.y = y; + r.w = w; + r.h = h; + return r; +} + +void +cairosdl_surface_flush_rect ( + cairo_surface_t *surface, + int x, + int y, + int w, + int h) +{ + SDL_Rect rect = make_rect(x, y, w, h); + cairosdl_surface_flush_rects (surface, 1, &rect); +} + +void +cairosdl_surface_mark_dirty_rect ( + cairo_surface_t *surface, + int x, + int y, + int w, + int h) +{ + SDL_Rect rect = make_rect(x, y, w, h); + cairosdl_surface_mark_dirty_rects (surface, 1, &rect); +} + +void +cairosdl_surface_flush (cairo_surface_t *surface) +{ + cairosdl_surface_flush_rect (surface, 0, 0, 32767, 32767); +} + +void +cairosdl_surface_mark_dirty (cairo_surface_t *surface) +{ + cairosdl_surface_mark_dirty_rect (surface, 0, 0, 32767, 32767); +} + +/* + * Context functions for convenience. + */ + +SDL_Surface * +cairosdl_get_target (cairo_t *cr) +{ + return cairosdl_surface_get_target (cairo_get_target (cr)); +} + +cairo_t * +cairosdl_create (SDL_Surface *sdl_surface) +{ + cairo_surface_t *surface = cairosdl_surface_create (sdl_surface); + cairo_t *cr = cairo_create (surface); + cairo_surface_destroy (surface); + return cr; +} + +void +cairosdl_destroy (cairo_t *cr) +{ + cairosdl_surface_flush (cairo_get_target (cr)); + cairo_destroy (cr); +} |