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