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