Home | History | Annotate | Download | only in skin
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 #include "android/skin/surface.h"
     13 #include "android/skin/argb.h"
     14 #include <SDL.h>
     15 
     16 #define  DEBUG  1
     17 
     18 #if DEBUG
     19 #include "android/utils/debug.h"
     20 #define  D(...)   VERBOSE_PRINT(surface,__VA_ARGS__)
     21 #else
     22 #define  D(...)   ((void)0)
     23 #endif
     24 
     25 struct SkinSurface {
     26     int                  refcount;
     27     uint32_t*            pixels;
     28     SDL_Surface*         surface;
     29     SkinSurfaceDoneFunc  done_func;
     30     void*                done_user;
     31 };
     32 
     33 static void
     34 skin_surface_free( SkinSurface*  s )
     35 {
     36     if (s->done_func) {
     37         s->done_func( s->done_user );
     38         s->done_func = NULL;
     39     }
     40     if (s->surface) {
     41         SDL_FreeSurface(s->surface);
     42         s->surface = NULL;
     43     }
     44     free(s);
     45 }
     46 
     47 extern SkinSurface*
     48 skin_surface_ref( SkinSurface*  surface )
     49 {
     50     if (surface)
     51         surface->refcount += 1;
     52     return surface;
     53 }
     54 
     55 extern void
     56 skin_surface_unrefp( SkinSurface*  *psurface )
     57 {
     58     SkinSurface*  surf = *psurface;
     59     if (surf) {
     60         if (--surf->refcount <= 0)
     61             skin_surface_free(surf);
     62         *psurface = NULL;
     63     }
     64 }
     65 
     66 
     67 void
     68 skin_surface_set_done( SkinSurface*  s, SkinSurfaceDoneFunc  done_func, void*  done_user )
     69 {
     70     s->done_func = done_func;
     71     s->done_user = done_user;
     72 }
     73 
     74 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
     75 #  define  ARGB32_R_MASK  0xff000000
     76 #  define  ARGB32_G_MASK  0x00ff0000
     77 #  define  ARGB32_B_MASK  0x0000ff00
     78 #  define  ARGB32_A_MASK  0x000000ff
     79 #else
     80 #  define  ARGB32_R_MASK  0x000000ff
     81 #  define  ARGB32_G_MASK  0x0000ff00
     82 #  define  ARGB32_B_MASK  0x00ff0000
     83 #  define  ARGB32_A_MASK  0xff000000
     84 #endif
     85 
     86 static SDL_Surface*
     87 _sdl_surface_create_rgb( int  width,
     88                          int  height,
     89                          int  depth,
     90                          int  flags )
     91 {
     92    Uint32   rmask, gmask, bmask, amask;
     93 
     94     if (depth == 8) {
     95         rmask = gmask = bmask = 0;
     96         amask = 0xff;
     97     } else if (depth == 32) {
     98         rmask = ARGB32_R_MASK;
     99         gmask = ARGB32_G_MASK;
    100         bmask = ARGB32_B_MASK;
    101         amask = ARGB32_A_MASK;
    102     } else
    103         return NULL;
    104 
    105     return SDL_CreateRGBSurface( flags, width, height, depth,
    106                                  rmask, gmask, bmask, amask );
    107 }
    108 
    109 
    110 static SDL_Surface*
    111 _sdl_surface_create_rgb_from( int   width,
    112                               int   height,
    113                               int   pitch,
    114                               void* pixels,
    115                               int   depth )
    116 {
    117    Uint32   rmask, gmask, bmask, amask;
    118 
    119     if (depth == 8) {
    120         rmask = gmask = bmask = 0;
    121         amask = 0xff;
    122     } else if (depth == 32) {
    123         rmask = ARGB32_R_MASK;
    124         gmask = ARGB32_G_MASK;
    125         bmask = ARGB32_B_MASK;
    126         amask = ARGB32_A_MASK;
    127     } else
    128         return NULL;
    129 
    130     return SDL_CreateRGBSurfaceFrom( pixels, width, height, pitch, depth,
    131                                      rmask, gmask, bmask, amask );
    132 }
    133 
    134 
    135 static SkinSurface*
    136 _skin_surface_create( SDL_Surface*  surface,
    137                       void*         pixels )
    138 {
    139     SkinSurface*  s = malloc(sizeof(*s));
    140     if (s != NULL) {
    141         s->refcount = 1;
    142         s->pixels   = pixels;
    143         s->surface  = surface;
    144         s->done_func = NULL;
    145         s->done_user = NULL;
    146     }
    147     else {
    148         SDL_FreeSurface(surface);
    149         free(pixels);
    150         D( "not enough memory to allocate new skin surface !" );
    151     }
    152     return  s;
    153 }
    154 
    155 
    156 SkinSurface*
    157 skin_surface_create_fast( int  w, int  h )
    158 {
    159     SDL_Surface*  surface;
    160 
    161     surface = _sdl_surface_create_rgb( w, h, 32, SDL_HWSURFACE );
    162     if (surface == NULL) {
    163         surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE );
    164         if (surface == NULL) {
    165             D( "could not create fast %dx%d ARGB32 surface: %s",
    166                w, h, SDL_GetError() );
    167             return NULL;
    168         }
    169     }
    170     return _skin_surface_create( surface, NULL );
    171 }
    172 
    173 
    174 SkinSurface*
    175 skin_surface_create_slow( int  w, int  h )
    176 {
    177     SDL_Surface*  surface;
    178 
    179     surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE );
    180     if (surface == NULL) {
    181         D( "could not create slow %dx%d ARGB32 surface: %s",
    182             w, h, SDL_GetError() );
    183         return NULL;
    184     }
    185     return _skin_surface_create( surface, NULL );
    186 }
    187 
    188 
    189 SkinSurface*
    190 skin_surface_create_argb32_from(
    191                         int                  w,
    192                         int                  h,
    193                         int                  pitch,
    194                         uint32_t*            pixels,
    195                         int                  do_copy )
    196 {
    197     SDL_Surface*  surface;
    198     uint32_t*     pixcopy = NULL;
    199 
    200     if (do_copy) {
    201         size_t  size = h*pitch;
    202         pixcopy = malloc( size );
    203         if (pixcopy == NULL && size > 0) {
    204             D( "not enough memory to create %dx%d ARGB32 surface",
    205                w, h );
    206             return NULL;
    207         }
    208         memcpy( pixcopy, pixels, size );
    209     }
    210 
    211     surface = _sdl_surface_create_rgb_from( w, h, pitch,
    212                                             pixcopy ? pixcopy : pixels,
    213                                             32 );
    214     if (surface == NULL) {
    215         D( "could not create %dx%d slow ARGB32 surface: %s",
    216             w, h, SDL_GetError() );
    217         return NULL;
    218     }
    219     return _skin_surface_create( surface, pixcopy );
    220 }
    221 
    222 
    223 
    224 
    225 extern int
    226 skin_surface_lock( SkinSurface*  s, SkinSurfacePixels  *pix )
    227 {
    228     if (!s || !s->surface) {
    229         D( "error: trying to lock stale surface %p", s );
    230         return -1;
    231     }
    232     if ( SDL_LockSurface( s->surface ) != 0 ) {
    233         D( "could not lock surface %p: %s", s, SDL_GetError() );
    234         return -1;
    235     }
    236     pix->w      = s->surface->w;
    237     pix->h      = s->surface->h;
    238     pix->pitch  = s->surface->pitch;
    239     pix->pixels = s->surface->pixels;
    240     return 0;
    241 }
    242 
    243 /* unlock a slow surface that was previously locked */
    244 extern void
    245 skin_surface_unlock( SkinSurface*  s )
    246 {
    247     if (s && s->surface)
    248         SDL_UnlockSurface( s->surface );
    249 }
    250 
    251 
    252 #if 0
    253 static uint32_t
    254 skin_surface_map_argb( SkinSurface*  s, uint32_t  c )
    255 {
    256     if (s && s->surface) {
    257         return SDL_MapRGBA( s->surface->format,
    258                             ((c) >> 16) & 255,
    259                             ((c) >> 8) & 255,
    260                             ((c) & 255),
    261                             ((c) >> 24) & 255 );
    262     }
    263     return 0x00000000;
    264 }
    265 #endif
    266 
    267 typedef struct {
    268     int   x;
    269     int   y;
    270     int   w;
    271     int   h;
    272     int   sx;
    273     int   sy;
    274 
    275     uint8_t*      dst_line;
    276     int           dst_pitch;
    277     SDL_Surface*  dst_lock;
    278 
    279     uint8_t*      src_line;
    280     int           src_pitch;
    281     SDL_Surface*  src_lock;
    282     uint32_t      src_color;
    283 
    284 } SkinBlit;
    285 
    286 
    287 static int
    288 skin_blit_init_fill( SkinBlit*     blit,
    289                      SkinSurface*  dst,
    290                      SkinRect*     dst_rect,
    291                      uint32_t      color )
    292 {
    293     int  x = dst_rect->pos.x;
    294     int  y = dst_rect->pos.y;
    295     int  w = dst_rect->size.w;
    296     int  h = dst_rect->size.h;
    297     int  delta;
    298 
    299     if (x < 0) {
    300         w += x;
    301         x  = 0;
    302     }
    303     delta = (x + w) - dst->surface->w;
    304     if (delta > 0)
    305         w -= delta;
    306 
    307     if (y < 0) {
    308         h += y;
    309         y  = 0;
    310     }
    311     delta = (y + h) - dst->surface->h;
    312     if (delta > 0)
    313         h -= delta;
    314 
    315     if (w <= 0 || h <= 0)
    316         return 0;
    317 
    318     blit->x = x;
    319     blit->y = y;
    320     blit->w = w;
    321     blit->h = h;
    322 
    323     if ( !SDL_LockSurface(dst->surface) )
    324         return 0;
    325 
    326     blit->dst_lock  = dst->surface;
    327     blit->dst_pitch = dst->surface->pitch;
    328     blit->dst_line  = dst->surface->pixels + y*blit->dst_pitch;
    329 
    330     blit->src_lock  = NULL;
    331     blit->src_color = color;
    332 
    333     return 1;
    334 }
    335 
    336 static int
    337 skin_blit_init_blit( SkinBlit*     blit,
    338                      SkinSurface*  dst,
    339                      SkinPos*      dst_pos,
    340                      SkinSurface*  src,
    341                      SkinRect*     src_rect )
    342 {
    343     int  x  = dst_pos->x;
    344     int  y  = dst_pos->y;
    345     int  sx = src_rect->pos.x;
    346     int  sy = src_rect->pos.y;
    347     int  w  = src_rect->size.w;
    348     int  h  = src_rect->size.h;
    349     int  delta;
    350 
    351     if (x < 0) {
    352         w  += x;
    353         sx -= x;
    354         x   = 0;
    355     }
    356     if (sx < 0) {
    357         w  += sx;
    358         x  -= sx;
    359         sx  = 0;
    360     }
    361 
    362     delta = (x + w) - dst->surface->w;
    363     if (delta > 0)
    364         w -= delta;
    365 
    366     delta = (sx + w) - src->surface->w;
    367     if (delta > 0)
    368         w -= delta;
    369 
    370     if (y < 0) {
    371         h  += y;
    372         sy += y;
    373         y   = 0;
    374     }
    375     if (sy < 0) {
    376         h  += sy;
    377         y  -= sy;
    378         sy  = 0;
    379     }
    380     delta = (y + h) - dst->surface->h;
    381     if (delta > 0)
    382         h -= delta;
    383 
    384     delta = (sy + h) - src->surface->h;
    385 
    386     if (w <= 0 || h <= 0)
    387         return 0;
    388 
    389     blit->x = x;
    390     blit->y = y;
    391     blit->w = w;
    392     blit->h = h;
    393 
    394     blit->sx = sx;
    395     blit->sy = sy;
    396 
    397     if ( !SDL_LockSurface(dst->surface) )
    398         return 0;
    399 
    400     blit->dst_lock  = dst->surface;
    401     blit->dst_pitch = dst->surface->pitch;
    402     blit->dst_line  = (uint8_t*) dst->surface->pixels + y*blit->dst_pitch;
    403 
    404     if ( !SDL_LockSurface(src->surface) ) {
    405         SDL_UnlockSurface(dst->surface);
    406         return 0;
    407     }
    408 
    409     blit->src_lock  = src->surface;
    410     blit->src_pitch = src->surface->pitch;
    411     blit->src_line  = (uint8_t*) src->surface->pixels + sy*blit->src_pitch;
    412 
    413     return 1;
    414 }
    415 
    416 static void
    417 skin_blit_done( SkinBlit*  blit )
    418 {
    419     if (blit->src_lock)
    420         SDL_UnlockSurface( blit->src_lock );
    421     if (blit->dst_lock)
    422         SDL_UnlockSurface( blit->dst_lock );
    423     ARGB_DONE;
    424 }
    425 
    426 typedef void (*SkinLineFillFunc)( uint32_t*  dst, uint32_t  color, int  len );
    427 typedef void (*SkinLineBlitFunc)( uint32_t*  dst, const uint32_t*  src,  int  len );
    428 
    429 static void
    430 skin_line_fill_copy( uint32_t*  dst, uint32_t  color, int  len )
    431 {
    432     uint32_t*  end = dst + len;
    433 
    434     while (dst + 4 <= end) {
    435         dst[0] = dst[1] = dst[2] = dst[3] = color;
    436         dst   += 4;
    437     }
    438     while (dst < end) {
    439         dst[0] = color;
    440         dst   += 1;
    441     }
    442 }
    443 
    444 static void
    445 skin_line_fill_srcover( uint32_t*  dst, uint32_t  color, int  len )
    446 {
    447     uint32_t*  end = dst + len;
    448     uint32_t   alpha = (color >> 24);
    449 
    450     if (alpha == 255)
    451     {
    452         skin_line_fill_copy(dst, color, len);
    453     }
    454     else
    455     {
    456         ARGB_DECL(src_c);
    457         ARGB_DECL_ZERO();
    458 
    459         alpha  = 255 - alpha;
    460         alpha += (alpha >> 7);
    461 
    462         ARGB_UNPACK(src_c,color);
    463 
    464         for ( ; dst < end; dst++ )
    465         {
    466             ARGB_DECL(dst_c);
    467 
    468             ARGB_READ(dst_c,dst);
    469             ARGB_MULSHIFT(dst_c,dst_c,alpha,8);
    470             ARGB_ADD(dst_c,src_c);
    471             ARGB_WRITE(dst_c,dst);
    472         }
    473     }
    474 }
    475 
    476 static void
    477 skin_line_fill_dstover( uint32_t*  dst, uint32_t  color, int  len )
    478 {
    479     uint32_t*  end = dst + len;
    480     ARGB_DECL(src_c);
    481     ARGB_DECL_ZERO();
    482 
    483     ARGB_UNPACK(src_c,color);
    484 
    485     for ( ; dst < end; dst++ )
    486     {
    487         ARGB_DECL(dst_c);
    488         ARGB_DECL(val);
    489 
    490         uint32_t   alpha;
    491 
    492         ARGB_READ(dst_c,dst);
    493         alpha = 256 - (dst[0] >> 24);
    494         ARGB_MULSHIFT(val,src_c,alpha,8);
    495         ARGB_ADD(val,dst_c);
    496         ARGB_WRITE(val,dst);
    497     }
    498 }
    499 
    500 extern void
    501 skin_surface_fill( SkinSurface*  dst,
    502                    SkinRect*     rect,
    503                    uint32_t      argb_premul,
    504                    SkinBlitOp    blitop )
    505 {
    506     SkinLineFillFunc  fill;
    507     SkinBlit          blit[1];
    508 
    509     switch (blitop) {
    510         case SKIN_BLIT_COPY:    fill = skin_line_fill_copy; break;
    511         case SKIN_BLIT_SRCOVER: fill = skin_line_fill_srcover; break;
    512         case SKIN_BLIT_DSTOVER: fill = skin_line_fill_dstover; break;
    513         default: return;
    514     }
    515 
    516     if ( skin_blit_init_fill( blit, dst, rect, argb_premul ) ) {
    517         uint8_t*   line  = blit->dst_line;
    518         int        pitch = blit->dst_pitch;
    519         uint8_t*   end   = line + pitch*blit->h;
    520 
    521         for ( ; line != end; line += pitch )
    522             fill( (uint32_t*)line + blit->x, argb_premul, blit->w );
    523     }
    524 }
    525 
    526 
    527 static void
    528 skin_line_blit_copy( uint32_t*  dst, const uint32_t*  src, int  len )
    529 {
    530     memcpy( (char*)dst, (const char*)src, len*4 );
    531 }
    532 
    533 
    534 
    535 static void
    536 skin_line_blit_srcover( uint32_t*  dst, const uint32_t*  src, int  len )
    537 {
    538     uint32_t*  end = dst + len;
    539     ARGB_DECL_ZERO();
    540 
    541     for ( ; dst < end; dst++ ) {
    542         ARGB_DECL(d);
    543         ARGB_DECL(v);
    544         uint32_t  alpha;
    545 
    546         alpha = (src[0] >> 24);
    547         if (alpha > 0) {
    548             ARGB_READ(d,dst);
    549             alpha = 256 - alpha;
    550             ARGB_MULSHIFT(v,d,alpha,8);
    551             ARGB_ADD(v,d);
    552             ARGB_WRITE(v,dst);
    553         }
    554     }
    555 }
    556 
    557 static void
    558 skin_line_blit_dstover( uint32_t*  dst, const uint32_t*  src, int  len )
    559 {
    560     uint32_t*  end = dst + len;
    561     ARGB_DECL_ZERO();
    562 
    563     for ( ; dst < end; dst++ ) {
    564         ARGB_DECL(s);
    565         ARGB_DECL(v);
    566         uint32_t  alpha;
    567 
    568         alpha = (dst[0] >> 24);
    569         if (alpha < 255) {
    570             ARGB_READ(s,src);
    571             alpha = 256 - alpha;
    572             ARGB_MULSHIFT(v,s,alpha,8);
    573             ARGB_ADD(v,s);
    574             ARGB_WRITE(v,dst);
    575         }
    576     }
    577 }
    578 
    579 
    580 extern void
    581 skin_surface_blit( SkinSurface*  dst,
    582                    SkinPos*      dst_pos,
    583                    SkinSurface*  src,
    584                    SkinRect*     src_rect,
    585                    SkinBlitOp    blitop )
    586 {
    587     SkinLineBlitFunc  func;
    588     SkinBlit          blit[1];
    589 
    590     switch (blitop) {
    591         case SKIN_BLIT_COPY:    func = skin_line_blit_copy; break;
    592         case SKIN_BLIT_SRCOVER: func = skin_line_blit_srcover; break;
    593         case SKIN_BLIT_DSTOVER: func = skin_line_blit_dstover; break;
    594         default: return;
    595     }
    596 
    597     if ( skin_blit_init_blit( blit, dst, dst_pos, src, src_rect ) ) {
    598         uint8_t*   line   = blit->dst_line;
    599         uint8_t*   sline  = blit->src_line;
    600         int        pitch  = blit->dst_pitch;
    601         int        spitch = blit->src_pitch;
    602         uint8_t*   end    = line + pitch*blit->h;
    603 
    604         for ( ; line != end; line += pitch, sline += spitch )
    605             func( (uint32_t*)line + blit->x, (uint32_t*)sline + blit->sx, blit->w );
    606 
    607         skin_blit_done(blit);
    608     }
    609 }
    610