diff options
Diffstat (limited to 'android/skin/surface.c')
-rw-r--r-- | android/skin/surface.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/android/skin/surface.c b/android/skin/surface.c new file mode 100644 index 0000000..4424bd8 --- /dev/null +++ b/android/skin/surface.c @@ -0,0 +1,613 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** 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. +*/ +#include "android/skin/surface.h" +#include "android/skin/argb.h" +#include <SDL.h> + +#define DEBUG 1 + +#if DEBUG +#include "android/utils/debug.h" +#define D(...) VERBOSE_PRINT(surface,__VA_ARGS__) +#else +#define D(...) ((void)0) +#endif + +struct SkinSurface { + int refcount; + uint32_t* pixels; + SDL_Surface* surface; + SkinSurfaceDoneFunc done_func; + void* done_user; +}; + +static void +skin_surface_free( SkinSurface* s ) +{ + if (s->done_func) { + s->done_func( s->done_user ); + s->done_func = NULL; + } + if (s->surface) { + SDL_FreeSurface(s->surface); + s->surface = NULL; + } + free(s); +} + +extern SkinSurface* +skin_surface_ref( SkinSurface* surface ) +{ + if (surface) + surface->refcount += 1; + return surface; +} + +extern void +skin_surface_unrefp( SkinSurface* *psurface ) +{ + SkinSurface* surf = *psurface; + if (surf) { + if (--surf->refcount <= 0) + skin_surface_free(surf); + *psurface = NULL; + } +} + + +void +skin_surface_set_done( SkinSurface* s, SkinSurfaceDoneFunc done_func, void* done_user ) +{ + s->done_func = done_func; + s->done_user = done_user; +} + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +# define ARGB32_R_MASK 0xff000000 +# define ARGB32_G_MASK 0x00ff0000 +# define ARGB32_B_MASK 0x0000ff00 +# define ARGB32_A_MASK 0x000000ff +#else +# define ARGB32_R_MASK 0x000000ff +# define ARGB32_G_MASK 0x0000ff00 +# define ARGB32_B_MASK 0x00ff0000 +# define ARGB32_A_MASK 0xff000000 +#endif + +static SDL_Surface* +_sdl_surface_create_rgb( int width, + int height, + int depth, + int flags ) +{ + Uint32 rmask, gmask, bmask, amask; + + if (depth == 8) { + rmask = gmask = bmask = 0; + amask = 0xff; + } else if (depth == 32) { + rmask = ARGB32_R_MASK; + gmask = ARGB32_G_MASK; + bmask = ARGB32_B_MASK; + amask = ARGB32_A_MASK; + } else + return NULL; + + return SDL_CreateRGBSurface( flags, width, height, depth, + rmask, gmask, bmask, amask ); +} + + +static SDL_Surface* +_sdl_surface_create_rgb_from( int width, + int height, + int pitch, + void* pixels, + int depth ) +{ + Uint32 rmask, gmask, bmask, amask; + + if (depth == 8) { + rmask = gmask = bmask = 0; + amask = 0xff; + } else if (depth == 32) { + rmask = ARGB32_R_MASK; + gmask = ARGB32_G_MASK; + bmask = ARGB32_B_MASK; + amask = ARGB32_A_MASK; + } else + return NULL; + + return SDL_CreateRGBSurfaceFrom( pixels, width, height, pitch, depth, + rmask, gmask, bmask, amask ); +} + + +static SkinSurface* +_skin_surface_create( SDL_Surface* surface, + void* pixels ) +{ + SkinSurface* s = malloc(sizeof(*s)); + if (s != NULL) { + s->refcount = 1; + s->pixels = pixels; + s->surface = surface; + s->done_func = NULL; + s->done_user = NULL; + } + else { + SDL_FreeSurface(surface); + free(pixels); + D( "not enough memory to allocate new skin surface !" ); + } + return s; +} + + +SkinSurface* +skin_surface_create_fast( int w, int h ) +{ + SDL_Surface* surface; + + surface = _sdl_surface_create_rgb( w, h, 32, SDL_HWSURFACE ); + if (surface == NULL) { + surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE ); + if (surface == NULL) { + D( "could not create fast %dx%d ARGB32 surface: %s", + w, h, SDL_GetError() ); + return NULL; + } + } + return _skin_surface_create( surface, NULL ); +} + + +SkinSurface* +skin_surface_create_slow( int w, int h ) +{ + SDL_Surface* surface; + + surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE ); + if (surface == NULL) { + D( "could not create slow %dx%d ARGB32 surface: %s", + w, h, SDL_GetError() ); + return NULL; + } + return _skin_surface_create( surface, NULL ); +} + + +SkinSurface* +skin_surface_create_argb32_from( + int w, + int h, + int pitch, + uint32_t* pixels, + int do_copy ) +{ + SDL_Surface* surface; + uint32_t* pixcopy = NULL; + + if (do_copy) { + size_t size = h*pitch; + pixcopy = malloc( size ); + if (pixcopy == NULL && size > 0) { + D( "not enough memory to create %dx%d ARGB32 surface", + w, h ); + return NULL; + } + memcpy( pixcopy, pixels, size ); + } + + surface = _sdl_surface_create_rgb_from( w, h, pitch, + pixcopy ? pixcopy : pixels, + 32 ); + if (surface == NULL) { + D( "could not create %dx%d slow ARGB32 surface: %s", + w, h, SDL_GetError() ); + return NULL; + } + return _skin_surface_create( surface, pixcopy ); +} + + + + +extern int +skin_surface_lock( SkinSurface* s, SkinSurfacePixels *pix ) +{ + if (!s || !s->surface) { + D( "error: trying to lock stale surface %p", s ); + return -1; + } + if ( SDL_LockSurface( s->surface ) != 0 ) { + D( "could not lock surface %p: %s", s, SDL_GetError() ); + return -1; + } + pix->w = s->surface->w; + pix->h = s->surface->h; + pix->pitch = s->surface->pitch; + pix->pixels = s->surface->pixels; + return 0; +} + +/* unlock a slow surface that was previously locked */ +extern void +skin_surface_unlock( SkinSurface* s ) +{ + if (s && s->surface) + SDL_UnlockSurface( s->surface ); +} + + +#if 0 +static uint32_t +skin_surface_map_argb( SkinSurface* s, uint32_t c ) +{ + if (s && s->surface) { + return SDL_MapRGBA( s->surface->format, + ((c) >> 16) & 255, + ((c) >> 8) & 255, + ((c) & 255), + ((c) >> 24) & 255 ); + } + return 0x00000000; +} +#endif + +typedef struct { + int x; + int y; + int w; + int h; + int sx; + int sy; + + uint8_t* dst_line; + int dst_pitch; + SDL_Surface* dst_lock; + + uint8_t* src_line; + int src_pitch; + SDL_Surface* src_lock; + uint32_t src_color; + +} SkinBlit; + + +static int +skin_blit_init_fill( SkinBlit* blit, + SkinSurface* dst, + SkinRect* dst_rect, + uint32_t color ) +{ + int x = dst_rect->pos.x; + int y = dst_rect->pos.y; + int w = dst_rect->size.w; + int h = dst_rect->size.h; + int delta; + + if (x < 0) { + w += x; + x = 0; + } + delta = (x + w) - dst->surface->w; + if (delta > 0) + w -= delta; + + if (y < 0) { + h += y; + y = 0; + } + delta = (y + h) - dst->surface->h; + if (delta > 0) + h -= delta; + + if (w <= 0 || h <= 0) + return 0; + + blit->x = x; + blit->y = y; + blit->w = w; + blit->h = h; + + if ( !SDL_LockSurface(dst->surface) ) + return 0; + + blit->dst_lock = dst->surface; + blit->dst_pitch = dst->surface->pitch; + blit->dst_line = dst->surface->pixels + y*blit->dst_pitch; + + blit->src_lock = NULL; + blit->src_color = color; + + return 1; +} + +static int +skin_blit_init_blit( SkinBlit* blit, + SkinSurface* dst, + SkinPos* dst_pos, + SkinSurface* src, + SkinRect* src_rect ) +{ + int x = dst_pos->x; + int y = dst_pos->y; + int sx = src_rect->pos.x; + int sy = src_rect->pos.y; + int w = src_rect->size.w; + int h = src_rect->size.h; + int delta; + + if (x < 0) { + w += x; + sx -= x; + x = 0; + } + if (sx < 0) { + w += sx; + x -= sx; + sx = 0; + } + + delta = (x + w) - dst->surface->w; + if (delta > 0) + w -= delta; + + delta = (sx + w) - src->surface->w; + if (delta > 0) + w -= delta; + + if (y < 0) { + h += y; + sy += y; + y = 0; + } + if (sy < 0) { + h += sy; + y -= sy; + sy = 0; + } + delta = (y + h) - dst->surface->h; + if (delta > 0) + h -= delta; + + delta = (sy + h) - src->surface->h; + + if (w <= 0 || h <= 0) + return 0; + + blit->x = x; + blit->y = y; + blit->w = w; + blit->h = h; + + blit->sx = sx; + blit->sy = sy; + + if ( !SDL_LockSurface(dst->surface) ) + return 0; + + blit->dst_lock = dst->surface; + blit->dst_pitch = dst->surface->pitch; + blit->dst_line = (uint8_t*) dst->surface->pixels + y*blit->dst_pitch; + + if ( !SDL_LockSurface(src->surface) ) { + SDL_UnlockSurface(dst->surface); + return 0; + } + + blit->src_lock = src->surface; + blit->src_pitch = src->surface->pitch; + blit->src_line = (uint8_t*) src->surface->pixels + sy*blit->src_pitch; + + return 1; +} + +static void +skin_blit_done( SkinBlit* blit ) +{ + if (blit->src_lock) + SDL_UnlockSurface( blit->src_lock ); + if (blit->dst_lock) + SDL_UnlockSurface( blit->dst_lock ); + ARGB_DONE; +} + +typedef void (*SkinLineFillFunc)( uint32_t* dst, uint32_t color, int len ); +typedef void (*SkinLineBlitFunc)( uint32_t* dst, const uint32_t* src, int len ); + +static void +skin_line_fill_copy( uint32_t* dst, uint32_t color, int len ) +{ + uint32_t* end = dst + len; + + while (dst + 4 <= end) { + dst[0] = dst[1] = dst[2] = dst[3] = color; + dst += 4; + } + while (dst < end) { + dst[0] = color; + dst += 1; + } +} + +static void +skin_line_fill_srcover( uint32_t* dst, uint32_t color, int len ) +{ + uint32_t* end = dst + len; + uint32_t alpha = (color >> 24); + + if (alpha == 255) + { + skin_line_fill_copy(dst, color, len); + } + else + { + ARGB_DECL(src_c); + ARGB_DECL_ZERO(); + + alpha = 255 - alpha; + alpha += (alpha >> 7); + + ARGB_UNPACK(src_c,color); + + for ( ; dst < end; dst++ ) + { + ARGB_DECL(dst_c); + + ARGB_READ(dst_c,dst); + ARGB_MULSHIFT(dst_c,dst_c,alpha,8); + ARGB_ADD(dst_c,src_c); + ARGB_WRITE(dst_c,dst); + } + } +} + +static void +skin_line_fill_dstover( uint32_t* dst, uint32_t color, int len ) +{ + uint32_t* end = dst + len; + ARGB_DECL(src_c); + ARGB_DECL_ZERO(); + + ARGB_UNPACK(src_c,color); + + for ( ; dst < end; dst++ ) + { + ARGB_DECL(dst_c); + ARGB_DECL(val); + + uint32_t alpha; + + ARGB_READ(dst_c,dst); + alpha = 256 - (dst[0] >> 24); + ARGB_MULSHIFT(val,src_c,alpha,8); + ARGB_ADD(val,dst_c); + ARGB_WRITE(val,dst); + } +} + +extern void +skin_surface_fill( SkinSurface* dst, + SkinRect* rect, + uint32_t argb_premul, + SkinBlitOp blitop ) +{ + SkinLineFillFunc fill; + SkinBlit blit[1]; + + switch (blitop) { + case SKIN_BLIT_COPY: fill = skin_line_fill_copy; break; + case SKIN_BLIT_SRCOVER: fill = skin_line_fill_srcover; break; + case SKIN_BLIT_DSTOVER: fill = skin_line_fill_dstover; break; + default: return; + } + + if ( skin_blit_init_fill( blit, dst, rect, argb_premul ) ) { + uint8_t* line = blit->dst_line; + int pitch = blit->dst_pitch; + uint8_t* end = line + pitch*blit->h; + + for ( ; line != end; line += pitch ) + fill( (uint32_t*)line + blit->x, argb_premul, blit->w ); + } +} + + +static void +skin_line_blit_copy( uint32_t* dst, const uint32_t* src, int len ) +{ + memcpy( (char*)dst, (const char*)src, len*4 ); +} + + + +static void +skin_line_blit_srcover( uint32_t* dst, const uint32_t* src, int len ) +{ + uint32_t* end = dst + len; + ARGB_DECL_ZERO(); + + for ( ; dst < end; dst++ ) { + ARGB_DECL(s); + ARGB_DECL(d); + ARGB_DECL(v); + uint32_t alpha; + + ARGB_READ(s,src); + alpha = (src[0] >> 24); + if (alpha > 0) { + ARGB_READ(d,dst); + alpha = 256 - alpha; + ARGB_MULSHIFT(v,d,alpha,8); + ARGB_ADD(v,d); + ARGB_WRITE(v,dst); + } + } +} + +static void +skin_line_blit_dstover( uint32_t* dst, const uint32_t* src, int len ) +{ + uint32_t* end = dst + len; + ARGB_DECL_ZERO(); + + for ( ; dst < end; dst++ ) { + ARGB_DECL(s); + ARGB_DECL(d); + ARGB_DECL(v); + uint32_t alpha; + + ARGB_READ(d,dst); + alpha = (dst[0] >> 24); + if (alpha < 255) { + ARGB_READ(s,src); + alpha = 256 - alpha; + ARGB_MULSHIFT(v,s,alpha,8); + ARGB_ADD(v,s); + ARGB_WRITE(v,dst); + } + } +} + + +extern void +skin_surface_blit( SkinSurface* dst, + SkinPos* dst_pos, + SkinSurface* src, + SkinRect* src_rect, + SkinBlitOp blitop ) +{ + SkinLineBlitFunc func; + SkinBlit blit[1]; + + switch (blitop) { + case SKIN_BLIT_COPY: func = skin_line_blit_copy; break; + case SKIN_BLIT_SRCOVER: func = skin_line_blit_srcover; break; + case SKIN_BLIT_DSTOVER: func = skin_line_blit_dstover; break; + default: return; + } + + if ( skin_blit_init_blit( blit, dst, dst_pos, src, src_rect ) ) { + uint8_t* line = blit->dst_line; + uint8_t* sline = blit->src_line; + int pitch = blit->dst_pitch; + int spitch = blit->src_pitch; + uint8_t* end = line + pitch*blit->h; + + for ( ; line != end; line += pitch, sline += spitch ) + func( (uint32_t*)line + blit->x, (uint32_t*)sline + blit->sx, blit->w ); + + skin_blit_done(blit); + } +} |