aboutsummaryrefslogtreecommitdiffstats
path: root/android/skin/surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/skin/surface.c')
-rw-r--r--android/skin/surface.c613
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);
+ }
+}