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
25struct SkinSurface {
26    int                  refcount;
27    uint32_t*            pixels;
28    SDL_Surface*         surface;
29    SkinSurfaceDoneFunc  done_func;
30    void*                done_user;
31};
32
33static void
34skin_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
47extern SkinSurface*
48skin_surface_ref( SkinSurface*  surface )
49{
50    if (surface)
51        surface->refcount += 1;
52    return surface;
53}
54
55extern void
56skin_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
67void
68skin_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
86static 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
110static 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
135static 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
156SkinSurface*
157skin_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
174SkinSurface*
175skin_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
189SkinSurface*
190skin_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
225extern int
226skin_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 */
244extern void
245skin_surface_unlock( SkinSurface*  s )
246{
247    if (s && s->surface)
248        SDL_UnlockSurface( s->surface );
249}
250
251
252#if 0
253static uint32_t
254skin_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
267typedef 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
287static int
288skin_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
336static int
337skin_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
416static void
417skin_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
426typedef void (*SkinLineFillFunc)( uint32_t*  dst, uint32_t  color, int  len );
427typedef void (*SkinLineBlitFunc)( uint32_t*  dst, const uint32_t*  src,  int  len );
428
429static void
430skin_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
444static void
445skin_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
476static void
477skin_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
500extern void
501skin_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
527static void
528skin_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
535static void
536skin_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
557static void
558skin_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
580extern void
581skin_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