blob: 4424bd8c31aaeceee6da62681195dd60ca8c5b0f [file] [log] [blame]
/* 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);
}
}