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/image.h"
     13 #include "android/resource.h"
     14 #include <assert.h>
     15 #include <limits.h>
     16 
     17 #define  DEBUG  0
     18 
     19 #if DEBUG
     20 static void D(const char*  fmt, ...)
     21 {
     22     va_list  args;
     23     va_start(args, fmt);
     24     vfprintf(stderr, fmt, args);
     25     va_end(args);
     26 }
     27 #else
     28 #define  D(...)  do{}while(0)
     29 #endif
     30 
     31 /********************************************************************************/
     32 /********************************************************************************/
     33 /*****                                                                      *****/
     34 /*****            U T I L I T Y   F U N C T I O N S                         *****/
     35 /*****                                                                      *****/
     36 /********************************************************************************/
     37 /********************************************************************************/
     38 
     39 SDL_Surface*
     40 sdl_surface_from_argb32( void*  base, int  w, int  h )
     41 {
     42     return SDL_CreateRGBSurfaceFrom(
     43                         base, w, h, 32, w*4,
     44 #if HOST_WORDS_BIGENDIAN
     45                         0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
     46 #else
     47                         0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
     48 #endif
     49                         );
     50 }
     51 
     52 static void*
     53 rotate_image( void*  data, unsigned  width, unsigned  height,  SkinRotation  rotation )
     54 {
     55     void*  result;
     56 
     57     result = malloc( width*height*4 );
     58     if (result == NULL)
     59         return NULL;
     60 
     61     switch (rotation & 3)
     62     {
     63     case SKIN_ROTATION_0:
     64         memcpy( (char*)result, (const char*)data, width*height*4 );
     65         break;
     66 
     67     case SKIN_ROTATION_270:
     68         {
     69             unsigned*  start    = (unsigned*)data;
     70             unsigned*  src_line = start + (width-1);
     71             unsigned*  dst_line = (unsigned*)result;
     72             unsigned   hh;
     73 
     74             for (hh = width; hh > 0; hh--)
     75             {
     76                 unsigned*  src   = src_line;
     77                 unsigned*  dst   = dst_line;
     78                 unsigned   count = height;
     79 
     80                 for ( ; count > 0; count-- ) {
     81                     dst[0] = src[0];
     82                     dst   += 1;
     83                     src   += width;
     84                 }
     85 
     86                 src_line -= 1;
     87                 dst_line += height;
     88             }
     89         }
     90         break;
     91 
     92     case SKIN_ROTATION_180:
     93         {
     94             unsigned*  start    = (unsigned*)data;
     95             unsigned*  src_line = start + width*(height-1);
     96             unsigned*  dst_line = (unsigned*)result;
     97             unsigned   hh;
     98 
     99             for (hh = height; hh > 0; hh--)
    100             {
    101                 unsigned*  src = src_line + (width-1);
    102                 unsigned*  dst = dst_line;
    103 
    104                 while (src >= src_line)
    105                     *dst++ = *src--;
    106 
    107                 dst_line += width;
    108                 src_line -= width;
    109             }
    110         }
    111         break;
    112 
    113     case SKIN_ROTATION_90:
    114         {
    115             unsigned*  start    = (unsigned*)data;
    116             unsigned*  src_line = start + width*(height-1);
    117             unsigned*  dst_line = (unsigned*)result ;
    118             unsigned   hh;
    119 
    120             for (hh = width; hh > 0; hh--)
    121             {
    122                 unsigned*  src = src_line;
    123                 unsigned*  dst = dst_line;
    124                 unsigned   count;
    125 
    126                 for (count = height; count > 0; count--) {
    127                     dst[0] = src[0];
    128                     dst   += 1;
    129                     src   -= width;
    130                 }
    131 
    132                 dst_line += height;
    133                 src_line += 1;
    134             }
    135         }
    136         break;
    137 
    138     default:
    139         ;
    140     }
    141 
    142     return result;
    143 }
    144 
    145 
    146 static void
    147 blend_image( unsigned*  dst_pixels,
    148              unsigned*  src_pixels,
    149              unsigned   w,
    150              unsigned   h,
    151              int        alpha )
    152 {
    153     unsigned*  dst     = dst_pixels;
    154     unsigned*  dst_end = dst + w*h;
    155     unsigned*  src     = src_pixels;
    156 
    157     for ( ; dst < dst_end; dst++, src++ )
    158     {
    159         {
    160             unsigned  ag = (src[0] >> 8) & 0xff00ff;
    161             unsigned  rb =  src[0]       & 0xff00ff;
    162 
    163             ag = (ag*alpha) & 0xff00ff00;
    164             rb = ((rb*alpha) >> 8) & 0x00ff00ff;
    165 
    166             dst[0] = ag | rb;
    167         }
    168     }
    169 }
    170 
    171 
    172 static unsigned
    173 skin_image_desc_hash( SkinImageDesc*  desc )
    174 {
    175     unsigned  h = 0;
    176     int       n;
    177 
    178     for (n = 0; desc->path[n] != 0; n++) {
    179         int  c = desc->path[n];
    180         h = h*33 + c;
    181     }
    182     h += desc->rotation*1573;
    183     h += desc->blend * 7;
    184 
    185     return  h;
    186 }
    187 
    188 
    189 static int
    190 skin_image_desc_equal( SkinImageDesc*  a,
    191                        SkinImageDesc*  b )
    192 {
    193     return (a->rotation == b->rotation &&
    194             a->blend    == b->blend    &&
    195             !strcmp(a->path, b->path));
    196 }
    197 
    198 /********************************************************************************/
    199 /********************************************************************************/
    200 /*****                                                                      *****/
    201 /*****            S K I N   I M A G E S                                     *****/
    202 /*****                                                                      *****/
    203 /********************************************************************************/
    204 /********************************************************************************/
    205 
    206 enum {
    207     SKIN_IMAGE_CLONE = (1 << 0)   /* this image is a clone */
    208 };
    209 
    210 struct SkinImage {
    211     unsigned         hash;
    212     SkinImage*       link;
    213     int              ref_count;
    214     SkinImage*       next;
    215     SkinImage*       prev;
    216     SDL_Surface*     surface;
    217     unsigned         flags;
    218     unsigned         w, h;
    219     void*            pixels;  /* 32-bit ARGB */
    220     SkinImageDesc    desc;
    221 };
    222 
    223 
    224 
    225 
    226 static const SkinImage  _no_image[1] = {
    227     { 0, NULL, 0, NULL, NULL, NULL, 0, 0, 0, NULL, { "<none>", SKIN_ROTATION_0, 0 } }
    228 };
    229 
    230 SkinImage*  SKIN_IMAGE_NONE = (SkinImage*)&_no_image;
    231 
    232 static void
    233 skin_image_free( SkinImage*  image )
    234 {
    235     if (image && image != _no_image)
    236     {
    237         if (image->surface) {
    238             SDL_FreeSurface(image->surface);
    239             image->surface = NULL;
    240         }
    241 
    242         if (image->pixels) {
    243             free( image->pixels );
    244             image->pixels = NULL;
    245         }
    246 
    247         free(image);
    248     }
    249 }
    250 
    251 
    252 static SkinImage*
    253 skin_image_alloc( SkinImageDesc*  desc, unsigned  hash )
    254 {
    255     int         len   = strlen(desc->path);
    256     SkinImage*  image = calloc(1, sizeof(*image) + len + 1);
    257 
    258     if (image) {
    259         image->desc = desc[0];
    260         image->desc.path = (const char*)(image + 1);
    261         memcpy( (char*)image->desc.path, desc->path, len );
    262         ((char*)image->desc.path)[len] = 0;
    263 
    264         image->hash      = hash;
    265         image->next      = image->prev = image;
    266         image->ref_count = 1;
    267     }
    268     return image;
    269 }
    270 
    271 
    272 extern void *loadpng(const char *fn, unsigned *_width, unsigned *_height);
    273 extern void *readpng(const unsigned char*  base, size_t  size, unsigned *_width, unsigned *_height);
    274 
    275 static int
    276 skin_image_load( SkinImage*  image )
    277 {
    278     void*     data;
    279     unsigned  w, h;
    280     const char*  path = image->desc.path;
    281 
    282     if (path[0] == ':') {
    283         size_t                size;
    284         const unsigned char*  base;
    285 
    286         if (path[1] == '/' || path[1] == '\\')
    287             path += 1;
    288 
    289         base = android_resource_find( path+1, &size );
    290         if (base == NULL) {
    291             fprintf(stderr, "failed to locate built-in image file '%s'\n", path );
    292             return -1;
    293         }
    294 
    295         data = readpng(base, size, &w, &h);
    296         if (data == NULL) {
    297             fprintf(stderr, "failed to load built-in image file '%s'\n", path );
    298             return -1;
    299         }
    300     } else {
    301         data = loadpng(path, &w, &h);
    302         if (data == NULL) {
    303             fprintf(stderr, "failed to load image file '%s'\n", path );
    304             return -1;
    305         }
    306     }
    307 
    308    /* the data is loaded into memory as RGBA bytes by libpng. we want to manage
    309     * the values as 32-bit ARGB pixels, so swap the bytes accordingly depending
    310     * on our CPU endianess
    311     */
    312     {
    313         unsigned*  d     = data;
    314         unsigned*  d_end = d + w*h;
    315 
    316         for ( ; d < d_end; d++ ) {
    317             unsigned  pix = d[0];
    318 #if HOST_WORDS_BIGENDIAN
    319             /* R,G,B,A read as RGBA => ARGB */
    320             pix = ((pix >> 8) & 0xffffff) | (pix << 24);
    321 #else
    322             /* R,G,B,A read as ABGR => ARGB */
    323             pix = (pix & 0xff00ff00) | ((pix >> 16) & 0xff) | ((pix & 0xff) << 16);
    324 #endif
    325             d[0] = pix;
    326         }
    327     }
    328 
    329     image->pixels = data;
    330     image->w      = w;
    331     image->h      = h;
    332 
    333     image->surface = sdl_surface_from_argb32( image->pixels, w, h );
    334     if (image->surface == NULL) {
    335         fprintf(stderr, "failed to create SDL surface for '%s' image\n", path);
    336         return -1;
    337     }
    338     return 0;
    339 }
    340 
    341 
    342 /* simple hash table for images */
    343 
    344 #define  NUM_BUCKETS  64
    345 
    346 typedef struct {
    347     SkinImage*     buckets[ NUM_BUCKETS ];
    348     SkinImage      mru_head;
    349     int            num_images;
    350     unsigned long  total_pixels;
    351     unsigned long  max_pixels;
    352     unsigned long  total_images;
    353 } SkinImageCache;
    354 
    355 
    356 static void
    357 skin_image_cache_init( SkinImageCache*  cache )
    358 {
    359     memset(cache, 0, sizeof(*cache));
    360 #if DEBUG
    361     cache->max_pixels = 1;
    362 #else
    363     cache->max_pixels = 4*1024*1024;  /* limit image cache to 4 MB */
    364 #endif
    365     cache->mru_head.next = cache->mru_head.prev = &cache->mru_head;
    366 }
    367 
    368 
    369 static void
    370 skin_image_cache_remove( SkinImageCache*  cache,
    371                          SkinImage*       image )
    372 {
    373     /* remove from hash table */
    374     SkinImage**  pnode = cache->buckets + (image->hash & (NUM_BUCKETS-1));
    375     SkinImage*   node;
    376 
    377     for (;;) {
    378         node = *pnode;
    379         assert(node != NULL);
    380         if (node == NULL)  /* should not happen */
    381             break;
    382         if (node == image) {
    383             *pnode = node->link;
    384             break;
    385         }
    386         pnode = &node->link;
    387     }
    388 
    389     D( "skin_image_cache: remove '%s' (rot=%d), %d pixels\n",
    390        node->desc.path, node->desc.rotation, node->w*node->h );
    391 
    392     /* remove from mru list */
    393     image->prev->next = image->next;
    394     image->next->prev = image->prev;
    395 
    396     cache->total_pixels -= image->w*image->h;
    397     cache->total_images -= 1;
    398 }
    399 
    400 
    401 static SkinImage*
    402 skin_image_cache_raise( SkinImageCache*  cache,
    403                         SkinImage*       image )
    404 {
    405     if (image != cache->mru_head.next) {
    406         SkinImage*  prev = image->prev;
    407         SkinImage*  next = image->next;
    408 
    409         /* remove from mru list */
    410         prev->next = next;
    411         next->prev = prev;
    412 
    413         /* add to top */
    414         image->prev = &cache->mru_head;
    415         image->next = image->prev->next;
    416         image->prev->next = image;
    417         image->next->prev = image;
    418     }
    419     return image;
    420 }
    421 
    422 
    423 static void
    424 skin_image_cache_flush( SkinImageCache*  cache )
    425 {
    426     SkinImage*     image = cache->mru_head.prev;
    427     int            count = 0;
    428 
    429     D("skin_image_cache_flush: starting\n");
    430     while (cache->total_pixels > cache->max_pixels &&
    431            image != &cache->mru_head)
    432     {
    433         SkinImage*  prev = image->prev;
    434 
    435         if (image->ref_count == 0) {
    436             skin_image_cache_remove(cache, image);
    437             count += 1;
    438         }
    439         image = prev;
    440     }
    441     D("skin_image_cache_flush: finished, %d images flushed\n", count);
    442 }
    443 
    444 
    445 static SkinImage**
    446 skin_image_lookup_p( SkinImageCache*   cache,
    447                      SkinImageDesc*    desc,
    448                      unsigned         *phash )
    449 {
    450     unsigned     h     = skin_image_desc_hash(desc);
    451     unsigned     index = h & (NUM_BUCKETS-1);
    452     SkinImage**  pnode = &cache->buckets[index];
    453     for (;;) {
    454         SkinImage*  node = *pnode;
    455         if (node == NULL)
    456             break;
    457         if (node->hash == h && skin_image_desc_equal(desc, &node->desc))
    458             break;
    459         pnode = &node->link;
    460     }
    461     *phash = h;
    462     return  pnode;
    463 }
    464 
    465 
    466 static SkinImage*
    467 skin_image_create( SkinImageDesc*  desc, unsigned  hash )
    468 {
    469     SkinImage*  node;
    470 
    471     node = skin_image_alloc( desc, hash );
    472     if (node == NULL)
    473         return SKIN_IMAGE_NONE;
    474 
    475     if (desc->rotation == SKIN_ROTATION_0 &&
    476         desc->blend    == SKIN_BLEND_FULL)
    477     {
    478         if (skin_image_load(node) < 0) {
    479             skin_image_free(node);
    480             return SKIN_IMAGE_NONE;
    481         }
    482     }
    483     else
    484     {
    485         SkinImageDesc  desc0 = desc[0];
    486         SkinImage*     parent;
    487 
    488         desc0.rotation = SKIN_ROTATION_0;
    489         desc0.blend    = SKIN_BLEND_FULL;
    490 
    491         parent = skin_image_find( &desc0 );
    492         if (parent == SKIN_IMAGE_NONE)
    493             return SKIN_IMAGE_NONE;
    494 
    495         SDL_LockSurface(parent->surface);
    496 
    497         if (desc->rotation == SKIN_ROTATION_90 ||
    498             desc->rotation == SKIN_ROTATION_270)
    499         {
    500             node->w = parent->h;
    501             node->h = parent->w;
    502         } else {
    503             node->w = parent->w;
    504             node->h = parent->h;
    505         }
    506 
    507         node->pixels = rotate_image( parent->pixels, parent->w, parent->h,
    508                                     desc->rotation );
    509 
    510         SDL_UnlockSurface(parent->surface);
    511         skin_image_unref(&parent);
    512 
    513         if (node->pixels  == NULL) {
    514             skin_image_free(node);
    515             return SKIN_IMAGE_NONE;
    516         }
    517 
    518         if (desc->blend != SKIN_BLEND_FULL)
    519             blend_image( node->pixels, node->pixels, node->w, node->h, desc->blend );
    520 
    521         node->surface = sdl_surface_from_argb32( node->pixels, node->w, node->h );
    522         if (node->surface == NULL) {
    523             skin_image_free(node);
    524             return SKIN_IMAGE_NONE;
    525         }
    526     }
    527     return node;
    528 }
    529 
    530 
    531 static SkinImageCache   _image_cache[1];
    532 static int              _image_cache_init;
    533 
    534 SkinImage*
    535 skin_image_find( SkinImageDesc*  desc )
    536 {
    537     SkinImageCache*  cache = _image_cache;
    538     unsigned         hash;
    539     SkinImage**      pnode = skin_image_lookup_p( cache, desc, &hash );
    540     SkinImage*       node  = *pnode;
    541 
    542     if (!_image_cache_init) {
    543         _image_cache_init = 1;
    544         skin_image_cache_init(cache);
    545     }
    546 
    547     if (node) {
    548         node->ref_count += 1;
    549         return skin_image_cache_raise( cache, node );
    550     }
    551     node = skin_image_create( desc, hash );
    552     if (node == SKIN_IMAGE_NONE)
    553         return node;
    554 
    555     /* add to hash table */
    556     node->link = *pnode;
    557     *pnode     = node;
    558 
    559     /* add to mru list */
    560     skin_image_cache_raise( cache, node );
    561 
    562     D( "skin_image_cache: add '%s' (rot=%d), %d pixels\n",
    563        node->desc.path, node->desc.rotation, node->w*node->h );
    564 
    565     cache->total_pixels += node->w*node->h;
    566     if (cache->total_pixels > cache->max_pixels)
    567         skin_image_cache_flush( cache );
    568 
    569     return node;
    570 }
    571 
    572 
    573 SkinImage*
    574 skin_image_find_simple( const char*  path )
    575 {
    576     SkinImageDesc  desc;
    577 
    578     desc.path     = path;
    579     desc.rotation = SKIN_ROTATION_0;
    580     desc.blend    = SKIN_BLEND_FULL;
    581 
    582     return skin_image_find( &desc );
    583 }
    584 
    585 
    586 SkinImage*
    587 skin_image_ref( SkinImage*  image )
    588 {
    589     if (image && image != _no_image)
    590         image->ref_count += 1;
    591 
    592     return image;
    593 }
    594 
    595 
    596 void
    597 skin_image_unref( SkinImage**  pimage )
    598 {
    599     SkinImage*  image = *pimage;
    600 
    601     if (image) {
    602         if (image != _no_image && --image->ref_count == 0) {
    603             if ((image->flags & SKIN_IMAGE_CLONE) != 0) {
    604                 skin_image_free(image);
    605             }
    606         }
    607         *pimage = NULL;
    608     }
    609 }
    610 
    611 
    612 SkinImage*
    613 skin_image_rotate( SkinImage*  source, SkinRotation  rotation )
    614 {
    615     SkinImageDesc  desc;
    616     SkinImage*     image;
    617 
    618     if (source == _no_image || source->desc.rotation == rotation)
    619         return source;
    620 
    621     desc          = source->desc;
    622     desc.rotation = rotation;
    623     image         = skin_image_find( &desc );
    624     skin_image_unref( &source );
    625     return image;
    626 }
    627 
    628 
    629 SkinImage*
    630 skin_image_clone( SkinImage*  source )
    631 {
    632     SkinImage*   image;
    633 
    634     if (source == NULL || source == _no_image)
    635         return SKIN_IMAGE_NONE;
    636 
    637     image = calloc(1,sizeof(*image));
    638     if (image == NULL)
    639         goto Fail;
    640 
    641     image->desc  = source->desc;
    642     image->hash  = source->hash;
    643     image->flags = SKIN_IMAGE_CLONE;
    644     image->w     = source->w;
    645     image->h     = source->h;
    646     image->pixels = rotate_image( source->pixels, source->w, source->h,
    647                                   SKIN_ROTATION_0 );
    648     if (image->pixels == NULL)
    649         goto Fail;
    650 
    651     image->surface = sdl_surface_from_argb32( image->pixels, image->w, image->h );
    652     if (image->surface == NULL)
    653         goto Fail;
    654 
    655     return image;
    656 Fail:
    657     if (image != NULL)
    658         skin_image_free(image);
    659     return SKIN_IMAGE_NONE;
    660 }
    661 
    662 SkinImage*
    663 skin_image_clone_full( SkinImage*    source,
    664                        SkinRotation  rotation,
    665                        int           blend )
    666 {
    667     SkinImageDesc   desc;
    668     SkinImage*      clone;
    669 
    670     if (source == NULL || source == SKIN_IMAGE_NONE)
    671         return SKIN_IMAGE_NONE;
    672 
    673     if (rotation == SKIN_ROTATION_0 &&
    674         blend    == SKIN_BLEND_FULL)
    675     {
    676         return skin_image_clone(source);
    677     }
    678 
    679     desc.path     = source->desc.path;
    680     desc.rotation = rotation;
    681     desc.blend    = blend;
    682 
    683     clone = skin_image_create( &desc, 0 );
    684     if (clone != SKIN_IMAGE_NONE)
    685         clone->flags |= SKIN_IMAGE_CLONE;
    686 
    687     return clone;
    688 }
    689 
    690 /* apply blending to a source skin image and copy the result to a target clone image */
    691 extern void
    692 skin_image_blend_clone( SkinImage*  clone, SkinImage*  source, int  blend )
    693 {
    694     SDL_LockSurface( clone->surface );
    695     blend_image( clone->pixels, source->pixels, source->w, source->h, blend );
    696     SDL_UnlockSurface( clone->surface );
    697     SDL_SetAlpha( clone->surface, SDL_SRCALPHA, 255 );
    698 }
    699 
    700 int
    701 skin_image_w( SkinImage*  image )
    702 {
    703     return  image ? image->w : 0;
    704 }
    705 
    706 int
    707 skin_image_h( SkinImage*  image )
    708 {
    709     return  image ? image->h : 0;
    710 }
    711 
    712 int
    713 skin_image_org_w( SkinImage*  image )
    714 {
    715     if (image) {
    716         if (image->desc.rotation == SKIN_ROTATION_90 ||
    717             image->desc.rotation == SKIN_ROTATION_270)
    718             return image->h;
    719         else
    720             return image->w;
    721     }
    722     return 0;
    723 }
    724 
    725 int
    726 skin_image_org_h( SkinImage*  image )
    727 {
    728     if (image) {
    729         if (image->desc.rotation == SKIN_ROTATION_90 ||
    730             image->desc.rotation == SKIN_ROTATION_270)
    731             return image->w;
    732         else
    733             return image->h;
    734     }
    735     return 0;
    736 }
    737 
    738 SDL_Surface*
    739 skin_image_surface( SkinImage*  image )
    740 {
    741     return image ? image->surface : NULL;
    742 }
    743