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 */ 17static void skin_plate_get_region ( SkinPlate* p, SkinRegion *pregion ); 18static void skin_plate_get_opaque_region( SkinPlate* p, SkinRegion *pregion ); 19 20/* recompute region if needed */ 21static void 22skin_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' */ 45static void 46skin_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 */ 56static void 57skin_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 */ 79static void 80skin_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 */ 95static void 96skin_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 111static void 112skin_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 143static void 144skin_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 */ 154void 155skin_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 166void 167skin_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 180extern SkinPlate* 181skin_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 203SkinPlate* 204skin_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 220SkinPlate* 221skin_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 234extern void 235skin_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 261void 262skin_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 */ 283static void 284skin_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 344extern SkinViewport* 345skin_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 360extern void 361skin_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 372extern void 373skin_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 383extern void 384skin_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