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/composer.h"
     13 #include <stddef.h>
     14 #include "android/utils/system.h"
     15 
     16 /* forwards */
     17 static void  skin_plate_get_region       ( SkinPlate*  p, SkinRegion  *pregion );
     18 static void  skin_plate_get_opaque_region( SkinPlate*  p, SkinRegion  *pregion );
     19 
     20 /* recompute region if needed */
     21 static void
     22 skin_plate_ensure_region( SkinPlate*  p )
     23 {
     24     if (p->any.type == SKIN_PLATE_SURFACE || p->group.hasRegion)
     25         return;
     26     else {
     27         int  n, count = areflist_count( p->group.children );
     28 
     29         skin_region_reset(p->any.region);
     30 
     31         for (n = 0; n < count; n++) {
     32             SkinRegion  r[1];
     33             SkinPlate*  child = areflist_get( p->group.children, n );
     34 
     35             skin_plate_get_region( child, r );
     36             skin_region_translate( r, child->any.pos.x, child->any.pos.y );
     37             skin_region_union( p->any.region, r );
     38         }
     39 
     40         p->group.hasRegion = 1;
     41     }
     42 }
     43 
     44 /* return region in 'region' */
     45 static void
     46 skin_plate_get_region( SkinPlate*  p, SkinRegion*  region )
     47 {
     48     if ( p->any.type != SKIN_PLATE_SURFACE && !p->group.hasRegion ) {
     49         skin_plate_ensure_region(p);
     50     }
     51     skin_region_init_copy( region, p->any.region );
     52 }
     53 
     54 
     55 /* recompute opaque region is needed */
     56 static void
     57 skin_plate_ensure_opaque_region( SkinPlate*  p )
     58 {
     59     if (p->any.type != SKIN_PLATE_SURFACE && !p->group.hasOpaqueRegion) {
     60         int  n, count = areflist_count( p->group.children );
     61 
     62         skin_region_reset(p->group.opaqueRegion);
     63 
     64         for (n = 0; n < count; n++) {
     65             SkinRegion  r[1];
     66             SkinPlate*  child = areflist_get( p->group.children, n );
     67 
     68             skin_plate_get_opaque_region(child, r);
     69             skin_region_translate(r, child->any.pos.x, child->any.pos.y);
     70             skin_region_union( p->group.opaqueRegion, r);
     71         }
     72 
     73         p->group.hasOpaqueRegion = 1;
     74     }
     75 }
     76 
     77 
     78 /* return opaque pixels region */
     79 static void
     80 skin_plate_get_opaque_region( SkinPlate*  p, SkinRegion  *pregion )
     81 {
     82     if ( p->any.type == SKIN_PLATE_SURFACE ) {
     83         if (p->any.isOpaque)
     84             skin_region_init_copy(pregion, p->any.region);
     85         else
     86             skin_region_reset(pregion);
     87     } else {
     88         skin_plate_ensure_opaque_region(p);
     89         skin_region_init_copy(pregion, p->group.opaqueRegion);
     90     }
     91 }
     92 
     93 
     94 /* invalidate region in parent groups */
     95 static void
     96 skin_plate_invalidate_parent( SkinPlate*  p )
     97 {
     98     if (!p->any.isVisible)
     99         return;
    100 
    101     while (p) {
    102         if (p->any.type != SKIN_PLATE_SURFACE) {
    103             p->group.hasRegion       = 0;
    104             p->group.hasOpaqueRegion = 0;
    105         }
    106         p = p->any.parent;
    107     }
    108 }
    109 
    110 
    111 static void
    112 skin_plate_invalidate_( SkinPlate*  p, SkinRegion*  r, SkinPlate*  child )
    113 {
    114     if (p->any.type != SKIN_PLATE_SURFACE) {
    115         int  n = areflist_count( p->group.children );
    116         if (child != NULL) {
    117             n = areflist_indexOf( p->group.children, child );
    118         }
    119         while (n > 0) {
    120             n -= 1;
    121             child = areflist_get( p->group.children, n );
    122             skin_region_translate( r, child->any.pos.x, child->any.pos.y );
    123             skin_plate_invalidate_( p, r, NULL );
    124             skin_region_translate( r, -child->any.pos.x, -child->any.pos.y );
    125             if (skin_region_is_empty(r))
    126                 return;
    127         }
    128         if (p->any.type != SKIN_PLATE_SPACE) {
    129             SkinPlate*  parent = p->any.parent;
    130             skin_region_translate(r, parent->any.pos.x, parent->any.pos.y );
    131             skin_plate_invalidate_(parent, r, p);
    132         } else {
    133             /* send to viewports */
    134             int  n, count = areflist_count( p->space.viewports );
    135             for (n = 0; n < count; n++) {
    136                 SkinViewport*  v = areflist_get( p->space.viewports, n );
    137                 skin_viewport_invalidate(v, r);
    138             }
    139         }
    140     }
    141 }
    142 
    143 static void
    144 skin_plate_invalidate_region( SkinPlate*  p )
    145 {
    146     SkinRegion  r[1];
    147 
    148     skin_plate_get_region( p, r );
    149     skin_plate_invalidate_(p->any.parent, r, p);
    150     skin_region_reset(r);
    151 }
    152 
    153 /* change visibility */
    154 void
    155 skin_plate_set_visible( SkinPlate*  p, int  isVisible )
    156 {
    157     isVisible = !!isVisible;
    158     if (isVisible == p->any.isVisible)
    159         return;
    160 
    161     skin_plate_invalidate_parent(p);
    162     skin_plate_invalidate_region(p);
    163     p->any.isVisible = isVisible;
    164 }
    165 
    166 void
    167 skin_plate_set_opaque( SkinPlate*  p, int  isOpaque )
    168 {
    169     isOpaque = !!isOpaque;
    170     if (isOpaque == p->any.isOpaque)
    171         return;
    172 
    173     skin_plate_invalidate_parent(p);
    174     skin_plate_invalidate_region(p);
    175     p->any.isOpaque = isOpaque;
    176 }
    177 
    178 
    179 
    180 extern SkinPlate*
    181 skin_plate_surface( SkinPlate*         parent,
    182                     SkinPos*           pos,
    183                     SkinRegion*        region,
    184                     void*              surface,
    185                     SkinPlateDrawFunc  draw,
    186                     SkinPlateDoneFunc  done )
    187 {
    188     SkinPlate*  p;
    189 
    190     ANEW0(p);
    191     p->any.type      = SKIN_PLATE_SURFACE;
    192     p->any.parent    = parent;
    193     p->any.pos.x     = pos->x;
    194     p->any.pos.y     = pos->y;
    195     p->any.isVisible = 1;
    196     p->any.isOpaque  = 1;
    197 
    198     skin_region_init_copy( p->any.region, region );
    199     return p;
    200 }
    201 
    202 
    203 SkinPlate*
    204 skin_plate_group( SkinPlate*  parent, SkinPos*  pos )
    205 {
    206     SkinRegion  r[1];
    207     SkinPlate*  p;
    208 
    209     skin_region_reset(r);
    210     p = skin_plate_surface( parent, pos, r, NULL, NULL, NULL );
    211     p->any.type              = SKIN_PLATE_GROUP;
    212     p->group.hasOpaqueRegion = 0;
    213     skin_region_init_empty( p->group.opaqueRegion );
    214 
    215     areflist_init( p->group.children );
    216     return p;
    217 }
    218 
    219 
    220 SkinPlate*
    221 skin_plate_space( void )
    222 {
    223     SkinPos     pos;
    224     SkinPlate*  p;
    225 
    226     pos.x       = pos.y = 0;
    227     p           = skin_plate_group( NULL, &pos );
    228     p->any.type = SKIN_PLATE_SPACE;
    229     areflist_init( p->space.viewports );
    230     return p;
    231 }
    232 
    233 
    234 extern void
    235 skin_plate_free( SkinPlate*  p )
    236 {
    237     if (p->any.type >= SKIN_PLATE_SPACE) {
    238         while ( areflist_count( p->space.viewports ) )
    239             skin_viewport_free( areflist_get( p->space.viewports, 0 ) );
    240     }
    241     if (p->any.type >= SKIN_PLATE_GROUP) {
    242         skin_region_reset( p->group.opaqueRegion );
    243         p->group.hasOpaqueRegion = 0;
    244         p->group.hasRegion       = 0;
    245 
    246         while ( areflist_count( p->group.children ) )
    247             skin_plate_free( areflist_get( p->group.children, 0 ) );
    248     }
    249     if (p->any.type == SKIN_PLATE_SURFACE) {
    250         if (p->surface.done)
    251             p->surface.done( p->surface.user );
    252     }
    253 
    254     skin_region_reset( p->any.region );
    255 
    256     if (p->any.parent) {
    257         areflist_delFirst( p->any.parent->group.children, p );
    258     }
    259 }
    260 
    261 void
    262 skin_plate_invalidate( SkinPlate*  plate, SkinRegion*  region )
    263 {
    264     SkinRegion  r[1];
    265     skin_region_init_copy( r, region );
    266 }
    267 
    268 
    269 /* we use two regions to manage the front-to-back composition here
    270  *
    271  *  'updated' initially contains the update region, in parent coordinates
    272  *
    273  *  'drawn'   is initially empty, and will be filled with the region of translucent
    274  *            pixels that have been
    275  *
    276  *  for a given surface plate, we translate the regions to plate coordinates,
    277  *  then we do an opaque blit of 'intersection(updated,region)', then removing it from 'updated'
    278  *
    279  *  after that, we make a DSTOVER blit of 'intersection(drawn,region)'
    280  *  if the plate is not opaque, we add this intersection to 'drawn'
    281  *
    282  */
    283 static void
    284 skin_plate_redraw( SkinPlate*  plate, SkinRegion*  updated, SkinRegion*  drawn, SkinPos*  apos, SkinViewport*  viewport )
    285 {
    286     SkinPos  pos = plate->any.pos;
    287 
    288     if (!plate->any.isVisible)
    289         return;
    290 
    291     if (skin_region_is_empty(updated) && skin_region_is_empty(drawn))
    292         return;
    293 
    294     /* translate regions to plate coordinates */
    295     skin_region_translate( updated, pos.x, pos.y );
    296     skin_region_translate( drawn,   pos.y, pos.y );
    297     apos->x += pos.x;
    298     apos->y += pos.y;
    299 
    300     if (plate->any.type == SKIN_PLATE_SURFACE) {
    301         SkinRegion  r[1];
    302 
    303         /* inter(updated,region) => opaque blit + remove 'region' from 'updated'*/
    304         skin_plate_get_region(plate, r);
    305         skin_region_intersect(r, updated);
    306         if (!skin_region_is_empty(r)) {
    307             plate->surface.draw( plate->surface.user, r, apos, viewport, 1 );
    308             skin_region_substract(updated, r);
    309             skin_region_reset(r);
    310         }
    311 
    312         /* inter(drawn,region) => DSTOVER blit + if non-opaque add it to 'drawn' */
    313         skin_plate_get_region(plate, r);
    314         skin_region_intersect(r, drawn);
    315         if (!skin_region_is_empty(r)) {
    316             plate->surface.draw( plate->surface.user, r, apos, viewport, 0);
    317             if (!plate->any.isOpaque)
    318                 skin_region_union(drawn, r);
    319             skin_region_reset(r);
    320         }
    321 
    322     } else {
    323         int  n, count = areflist_count(plate->group.children);
    324         for (n = 0; n < count; n++) {
    325             SkinPos  pos;
    326 
    327             pos.x = apos->x + plate->any.pos.x;
    328             pos.y = apos->y + plate->any.pos.y;
    329 
    330             skin_plate_redraw( areflist_get(plate->group.children, n ), updated, drawn, &pos, viewport );
    331             if (skin_region_is_empty(updated) && skin_region_is_empty(drawn))
    332                 break;
    333         }
    334     }
    335 
    336     /* convert back to parent coordinates */
    337     apos->x -= pos.x;
    338     apos->y -= pos.y;
    339     skin_region_translate( updated, -pos.x, -pos.y );
    340     skin_region_translate( drawn,   -pos.x, -pos.y );
    341 }
    342 
    343 
    344 extern SkinViewport*
    345 skin_viewport( SkinPlate*  space, SkinRect*  rect, void*  surface, int  sx, int  sy )
    346 {
    347     SkinViewport*  v;
    348 
    349     ANEW0(v);
    350     v->space   = space;
    351     v->rect    = rect[0];
    352     v->spos.x  = sx;
    353     v->spos.y  = sy;
    354     v->surface = surface;
    355 
    356     skin_region_init_empty( v->update );
    357     return v;
    358 }
    359 
    360 extern void
    361 skin_viewport_free( SkinViewport*  v )
    362 {
    363     SkinPlate*  space = v->space;
    364     if (space != NULL) {
    365         areflist_delFirst( space->space.viewports, v );
    366         v->space = NULL;
    367     }
    368     skin_region_reset( v->update );
    369     AFREE(v);
    370 }
    371 
    372 extern void
    373 skin_viewport_invalidate( SkinViewport*  v, SkinRegion*  region )
    374 {
    375     SkinRegion  r[1];
    376     skin_region_init_copy(r,region);
    377     skin_region_translate(r, -v->spos.x, -v->spos.y);
    378     skin_region_intersect_rect(r,&v->rect);
    379     skin_region_union( v->update, r );
    380     skin_region_reset(r);
    381 }
    382 
    383 extern void
    384 skin_viewport_redraw( SkinViewport*  v )
    385 {
    386     if (v->space && !skin_region_is_empty(v->update)) {
    387         SkinRegion  update[1];
    388         SkinRegion  drawn[1];
    389         SkinPos     apos;
    390 
    391         skin_region_copy(update, v->update);
    392         skin_region_reset(drawn);
    393         skin_region_reset( v->update );
    394 
    395         apos.x = apos.y = 0;
    396         skin_plate_redraw( v->space, update, drawn, &apos, v );
    397 
    398         skin_region_reset(update);
    399         skin_region_reset(drawn);
    400     }
    401 }
    402