• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
D(const char * fmt,...)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*
sdl_surface_from_argb32(void * base,int w,int h)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*
rotate_image(void * data,unsigned width,unsigned height,SkinRotation rotation)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
blend_image(unsigned * dst_pixels,unsigned * src_pixels,unsigned w,unsigned h,int alpha)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
skin_image_desc_hash(SkinImageDesc * desc)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
skin_image_desc_equal(SkinImageDesc * a,SkinImageDesc * b)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
skin_image_free(SkinImage * image)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*
skin_image_alloc(SkinImageDesc * desc,unsigned hash)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
skin_image_load(SkinImage * image)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
skin_image_cache_init(SkinImageCache * cache)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
skin_image_cache_remove(SkinImageCache * cache,SkinImage * image)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*
skin_image_cache_raise(SkinImageCache * cache,SkinImage * image)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
skin_image_cache_flush(SkinImageCache * cache)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**
skin_image_lookup_p(SkinImageCache * cache,SkinImageDesc * desc,unsigned * phash)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*
skin_image_create(SkinImageDesc * desc,unsigned hash)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*
skin_image_find(SkinImageDesc * desc)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*
skin_image_find_simple(const char * path)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*
skin_image_ref(SkinImage * image)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
skin_image_unref(SkinImage ** pimage)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*
skin_image_rotate(SkinImage * source,SkinRotation rotation)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*
skin_image_clone(SkinImage * source)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*
skin_image_clone_full(SkinImage * source,SkinRotation rotation,int blend)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
skin_image_blend_clone(SkinImage * clone,SkinImage * source,int blend)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
skin_image_w(SkinImage * image)701 skin_image_w( SkinImage*  image )
702 {
703     return  image ? image->w : 0;
704 }
705 
706 int
skin_image_h(SkinImage * image)707 skin_image_h( SkinImage*  image )
708 {
709     return  image ? image->h : 0;
710 }
711 
712 int
skin_image_org_w(SkinImage * image)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
skin_image_org_h(SkinImage * image)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*
skin_image_surface(SkinImage * image)739 skin_image_surface( SkinImage*  image )
740 {
741     return image ? image->surface : NULL;
742 }
743