| /* Copyright (C) 2007-2008 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| #include "android/skin/composer.h" |
| #include <stddef.h> |
| #include "android/utils/system.h" |
| |
| /* forwards */ |
| static void skin_plate_get_region ( SkinPlate* p, SkinRegion *pregion ); |
| static void skin_plate_get_opaque_region( SkinPlate* p, SkinRegion *pregion ); |
| |
| /* recompute region if needed */ |
| static void |
| skin_plate_ensure_region( SkinPlate* p ) |
| { |
| if (p->any.type == SKIN_PLATE_SURFACE || p->group.hasRegion) |
| return; |
| else { |
| int n, count = areflist_count( p->group.children ); |
| |
| skin_region_reset(p->any.region); |
| |
| for (n = 0; n < count; n++) { |
| SkinRegion r[1]; |
| SkinPlate* child = areflist_get( p->group.children, n ); |
| |
| skin_plate_get_region( child, r ); |
| skin_region_translate( r, child->any.pos.x, child->any.pos.y ); |
| skin_region_union( p->any.region, r ); |
| } |
| |
| p->group.hasRegion = 1; |
| } |
| } |
| |
| /* return region in 'region' */ |
| static void |
| skin_plate_get_region( SkinPlate* p, SkinRegion* region ) |
| { |
| if ( p->any.type != SKIN_PLATE_SURFACE && !p->group.hasRegion ) { |
| skin_plate_ensure_region(p); |
| } |
| skin_region_init_copy( region, p->any.region ); |
| } |
| |
| |
| /* recompute opaque region is needed */ |
| static void |
| skin_plate_ensure_opaque_region( SkinPlate* p ) |
| { |
| if (p->any.type != SKIN_PLATE_SURFACE && !p->group.hasOpaqueRegion) { |
| int n, count = areflist_count( p->group.children ); |
| |
| skin_region_reset(p->group.opaqueRegion); |
| |
| for (n = 0; n < count; n++) { |
| SkinRegion r[1]; |
| SkinPlate* child = areflist_get( p->group.children, n ); |
| |
| skin_plate_get_opaque_region(child, r); |
| skin_region_translate(r, child->any.pos.x, child->any.pos.y); |
| skin_region_union( p->group.opaqueRegion, r); |
| } |
| |
| p->group.hasOpaqueRegion = 1; |
| } |
| } |
| |
| |
| /* return opaque pixels region */ |
| static void |
| skin_plate_get_opaque_region( SkinPlate* p, SkinRegion *pregion ) |
| { |
| if ( p->any.type == SKIN_PLATE_SURFACE ) { |
| if (p->any.isOpaque) |
| skin_region_init_copy(pregion, p->any.region); |
| else |
| skin_region_reset(pregion); |
| } else { |
| skin_plate_ensure_opaque_region(p); |
| skin_region_init_copy(pregion, p->group.opaqueRegion); |
| } |
| } |
| |
| |
| /* invalidate region in parent groups */ |
| static void |
| skin_plate_invalidate_parent( SkinPlate* p ) |
| { |
| if (!p->any.isVisible) |
| return; |
| |
| while (p) { |
| if (p->any.type != SKIN_PLATE_SURFACE) { |
| p->group.hasRegion = 0; |
| p->group.hasOpaqueRegion = 0; |
| } |
| p = p->any.parent; |
| } |
| } |
| |
| |
| static void |
| skin_plate_invalidate_( SkinPlate* p, SkinRegion* r, SkinPlate* child ) |
| { |
| if (p->any.type != SKIN_PLATE_SURFACE) { |
| int n = areflist_count( p->group.children ); |
| if (child != NULL) { |
| n = areflist_indexOf( p->group.children, child ); |
| } |
| while (n > 0) { |
| n -= 1; |
| child = areflist_get( p->group.children, n ); |
| skin_region_translate( r, child->any.pos.x, child->any.pos.y ); |
| skin_plate_invalidate_( p, r, NULL ); |
| skin_region_translate( r, -child->any.pos.x, -child->any.pos.y ); |
| if (skin_region_is_empty(r)) |
| return; |
| } |
| if (p->any.type != SKIN_PLATE_SPACE) { |
| SkinPlate* parent = p->any.parent; |
| skin_region_translate(r, parent->any.pos.x, parent->any.pos.y ); |
| skin_plate_invalidate_(parent, r, p); |
| } else { |
| /* send to viewports */ |
| int n, count = areflist_count( p->space.viewports ); |
| for (n = 0; n < count; n++) { |
| SkinViewport* v = areflist_get( p->space.viewports, n ); |
| skin_viewport_invalidate(v, r); |
| } |
| } |
| } |
| } |
| |
| static void |
| skin_plate_invalidate_region( SkinPlate* p ) |
| { |
| SkinRegion r[1]; |
| |
| skin_plate_get_region( p, r ); |
| skin_plate_invalidate_(p->any.parent, r, p); |
| skin_region_reset(r); |
| } |
| |
| /* change visibility */ |
| void |
| skin_plate_set_visible( SkinPlate* p, int isVisible ) |
| { |
| isVisible = !!isVisible; |
| if (isVisible == p->any.isVisible) |
| return; |
| |
| skin_plate_invalidate_parent(p); |
| skin_plate_invalidate_region(p); |
| p->any.isVisible = isVisible; |
| } |
| |
| void |
| skin_plate_set_opaque( SkinPlate* p, int isOpaque ) |
| { |
| isOpaque = !!isOpaque; |
| if (isOpaque == p->any.isOpaque) |
| return; |
| |
| skin_plate_invalidate_parent(p); |
| skin_plate_invalidate_region(p); |
| p->any.isOpaque = isOpaque; |
| } |
| |
| |
| |
| extern SkinPlate* |
| skin_plate_surface( SkinPlate* parent, |
| SkinPos* pos, |
| SkinRegion* region, |
| void* surface, |
| SkinPlateDrawFunc draw, |
| SkinPlateDoneFunc done ) |
| { |
| SkinPlate* p; |
| |
| ANEW0(p); |
| p->any.type = SKIN_PLATE_SURFACE; |
| p->any.parent = parent; |
| p->any.pos.x = pos->x; |
| p->any.pos.y = pos->y; |
| p->any.isVisible = 1; |
| p->any.isOpaque = 1; |
| |
| skin_region_init_copy( p->any.region, region ); |
| return p; |
| } |
| |
| |
| SkinPlate* |
| skin_plate_group( SkinPlate* parent, SkinPos* pos ) |
| { |
| SkinRegion r[1]; |
| SkinPlate* p; |
| |
| skin_region_reset(r); |
| p = skin_plate_surface( parent, pos, r, NULL, NULL, NULL ); |
| p->any.type = SKIN_PLATE_GROUP; |
| p->group.hasOpaqueRegion = 0; |
| skin_region_init_empty( p->group.opaqueRegion ); |
| |
| areflist_init( p->group.children ); |
| return p; |
| } |
| |
| |
| SkinPlate* |
| skin_plate_space( void ) |
| { |
| SkinPos pos; |
| SkinPlate* p; |
| |
| pos.x = pos.y = 0; |
| p = skin_plate_group( NULL, &pos ); |
| p->any.type = SKIN_PLATE_SPACE; |
| areflist_init( p->space.viewports ); |
| return p; |
| } |
| |
| |
| extern void |
| skin_plate_free( SkinPlate* p ) |
| { |
| if (p->any.type >= SKIN_PLATE_SPACE) { |
| while ( areflist_count( p->space.viewports ) ) |
| skin_viewport_free( areflist_get( p->space.viewports, 0 ) ); |
| } |
| if (p->any.type >= SKIN_PLATE_GROUP) { |
| skin_region_reset( p->group.opaqueRegion ); |
| p->group.hasOpaqueRegion = 0; |
| p->group.hasRegion = 0; |
| |
| while ( areflist_count( p->group.children ) ) |
| skin_plate_free( areflist_get( p->group.children, 0 ) ); |
| } |
| if (p->any.type == SKIN_PLATE_SURFACE) { |
| if (p->surface.done) |
| p->surface.done( p->surface.user ); |
| } |
| |
| skin_region_reset( p->any.region ); |
| |
| if (p->any.parent) { |
| areflist_delFirst( p->any.parent->group.children, p ); |
| } |
| } |
| |
| void |
| skin_plate_invalidate( SkinPlate* plate, SkinRegion* region ) |
| { |
| SkinRegion r[1]; |
| skin_region_init_copy( r, region ); |
| } |
| |
| |
| /* we use two regions to manage the front-to-back composition here |
| * |
| * 'updated' initially contains the update region, in parent coordinates |
| * |
| * 'drawn' is initially empty, and will be filled with the region of translucent |
| * pixels that have been |
| * |
| * for a given surface plate, we translate the regions to plate coordinates, |
| * then we do an opaque blit of 'intersection(updated,region)', then removing it from 'updated' |
| * |
| * after that, we make a DSTOVER blit of 'intersection(drawn,region)' |
| * if the plate is not opaque, we add this intersection to 'drawn' |
| * |
| */ |
| static void |
| skin_plate_redraw( SkinPlate* plate, SkinRegion* updated, SkinRegion* drawn, SkinPos* apos, SkinViewport* viewport ) |
| { |
| SkinPos pos = plate->any.pos; |
| |
| if (!plate->any.isVisible) |
| return; |
| |
| if (skin_region_is_empty(updated) && skin_region_is_empty(drawn)) |
| return; |
| |
| /* translate regions to plate coordinates */ |
| skin_region_translate( updated, pos.x, pos.y ); |
| skin_region_translate( drawn, pos.y, pos.y ); |
| apos->x += pos.x; |
| apos->y += pos.y; |
| |
| if (plate->any.type == SKIN_PLATE_SURFACE) { |
| SkinRegion r[1]; |
| |
| /* inter(updated,region) => opaque blit + remove 'region' from 'updated'*/ |
| skin_plate_get_region(plate, r); |
| skin_region_intersect(r, updated); |
| if (!skin_region_is_empty(r)) { |
| plate->surface.draw( plate->surface.user, r, apos, viewport, 1 ); |
| skin_region_substract(updated, r); |
| skin_region_reset(r); |
| } |
| |
| /* inter(drawn,region) => DSTOVER blit + if non-opaque add it to 'drawn' */ |
| skin_plate_get_region(plate, r); |
| skin_region_intersect(r, drawn); |
| if (!skin_region_is_empty(r)) { |
| plate->surface.draw( plate->surface.user, r, apos, viewport, 0); |
| if (!plate->any.isOpaque) |
| skin_region_union(drawn, r); |
| skin_region_reset(r); |
| } |
| |
| } else { |
| int n, count = areflist_count(plate->group.children); |
| for (n = 0; n < count; n++) { |
| SkinPos pos; |
| |
| pos.x = apos->x + plate->any.pos.x; |
| pos.y = apos->y + plate->any.pos.y; |
| |
| skin_plate_redraw( areflist_get(plate->group.children, n ), updated, drawn, &pos, viewport ); |
| if (skin_region_is_empty(updated) && skin_region_is_empty(drawn)) |
| break; |
| } |
| } |
| |
| /* convert back to parent coordinates */ |
| apos->x -= pos.x; |
| apos->y -= pos.y; |
| skin_region_translate( updated, -pos.x, -pos.y ); |
| skin_region_translate( drawn, -pos.x, -pos.y ); |
| } |
| |
| |
| extern SkinViewport* |
| skin_viewport( SkinPlate* space, SkinRect* rect, void* surface, int sx, int sy ) |
| { |
| SkinViewport* v; |
| |
| ANEW0(v); |
| v->space = space; |
| v->rect = rect[0]; |
| v->spos.x = sx; |
| v->spos.y = sy; |
| v->surface = surface; |
| |
| skin_region_init_empty( v->update ); |
| return v; |
| } |
| |
| extern void |
| skin_viewport_free( SkinViewport* v ) |
| { |
| SkinPlate* space = v->space; |
| if (space != NULL) { |
| areflist_delFirst( space->space.viewports, v ); |
| v->space = NULL; |
| } |
| skin_region_reset( v->update ); |
| AFREE(v); |
| } |
| |
| extern void |
| skin_viewport_invalidate( SkinViewport* v, SkinRegion* region ) |
| { |
| SkinRegion r[1]; |
| skin_region_init_copy(r,region); |
| skin_region_translate(r, -v->spos.x, -v->spos.y); |
| skin_region_intersect_rect(r,&v->rect); |
| skin_region_union( v->update, r ); |
| skin_region_reset(r); |
| } |
| |
| extern void |
| skin_viewport_redraw( SkinViewport* v ) |
| { |
| if (v->space && !skin_region_is_empty(v->update)) { |
| SkinRegion update[1]; |
| SkinRegion drawn[1]; |
| SkinPos apos; |
| |
| skin_region_copy(update, v->update); |
| skin_region_reset(drawn); |
| skin_region_reset( v->update ); |
| |
| apos.x = apos.y = 0; |
| skin_plate_redraw( v->space, update, drawn, &apos, v ); |
| |
| skin_region_reset(update); |
| skin_region_reset(drawn); |
| } |
| } |