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
skin_plate_ensure_region(SkinPlate * p)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
skin_plate_get_region(SkinPlate * p,SkinRegion * region)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
skin_plate_ensure_opaque_region(SkinPlate * p)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
skin_plate_get_opaque_region(SkinPlate * p,SkinRegion * pregion)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
skin_plate_invalidate_parent(SkinPlate * p)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
skin_plate_invalidate_(SkinPlate * p,SkinRegion * r,SkinPlate * child)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
skin_plate_invalidate_region(SkinPlate * p)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
skin_plate_set_visible(SkinPlate * p,int isVisible)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
skin_plate_set_opaque(SkinPlate * p,int isOpaque)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*
skin_plate_surface(SkinPlate * parent,SkinPos * pos,SkinRegion * region,void * surface,SkinPlateDrawFunc draw,SkinPlateDoneFunc done)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*
skin_plate_group(SkinPlate * parent,SkinPos * pos)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*
skin_plate_space(void)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
skin_plate_free(SkinPlate * p)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
skin_plate_invalidate(SkinPlate * plate,SkinRegion * region)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
skin_plate_redraw(SkinPlate * plate,SkinRegion * updated,SkinRegion * drawn,SkinPos * apos,SkinViewport * viewport)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*
skin_viewport(SkinPlate * space,SkinRect * rect,void * surface,int sx,int sy)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
skin_viewport_free(SkinViewport * v)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
skin_viewport_invalidate(SkinViewport * v,SkinRegion * region)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
skin_viewport_redraw(SkinViewport * v)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