core: use a monotonic clock for animation timing calculations
[fbsplash.git] / core / src / render.c
blobb6e2d28dd39cf4b83df1b25e5b607b6ee135419b
1 /*
2 * render.c - Functions for rendering boxes and icons
4 * Copyright (C) 2004-2008, Michal Januszewski <spock@gentoo.org>
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License v2. See the file COPYING in the main directory of this archive for
8 * more details.
13 * HACK WARNING:
14 * This is necessary to get FD_SET and FD_ZERO on platforms other than x86.
17 #ifdef TARGET_KERNEL
18 #define __KERNEL__
19 #include <linux/posix_types.h>
20 #undef __KERNEL__
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include "common.h"
31 #include "render.h"
34 * Lower level graphics fucntions
36 inline void put_pixel(u8 a, u8 r, u8 g, u8 b, u8 *src, u8 *dst, u8 add)
38 /* Can we use optimized code for 24/32bpp modes? */
39 if (fbd.opt) {
40 if (a == 0) {
41 dst[fbd.ro] = src[fbd.ro];
42 dst[fbd.go] = src[fbd.go];
43 dst[fbd.bo] = src[fbd.bo];
44 } else if (a == 255) {
45 dst[fbd.ro] = r;
46 dst[fbd.go] = g;
47 dst[fbd.bo] = b;
48 } else {
49 dst[fbd.ro] = (src[fbd.ro]*(255-a) + r*a) / 255;
50 dst[fbd.go] = (src[fbd.go]*(255-a) + g*a) / 255;
51 dst[fbd.bo] = (src[fbd.bo]*(255-a) + b*a) / 255;
53 } else {
54 u32 i;
55 u8 tr, tg, tb;
57 if (a != 255) {
58 if (fbd.var.bits_per_pixel == 16) {
59 i = *(u16*)src;
60 } else if (fbd.var.bits_per_pixel == 24) {
61 i = *(u32*)src & 0xffffff;
62 } else if (fbd.var.bits_per_pixel == 32) {
63 i = *(u32*)src;
64 } else {
65 i = *(u32*)src & ((2 << fbd.var.bits_per_pixel)-1);
68 tr = (( (i >> fbd.var.red.offset & ((1 << fbd.rlen)-1))
69 << (8 - fbd.rlen)) * (255 - a) + r * a) / 255;
70 tg = (( (i >> fbd.var.green.offset & ((1 << fbd.glen)-1))
71 << (8 - fbd.glen)) * (255 - a) + g * a) / 255;
72 tb = (( (i >> fbd.var.blue.offset & ((1 << fbd.blen)-1))
73 << (8 - fbd.blen)) * (255 - a) + b * a) / 255;
74 } else {
75 tr = r;
76 tg = g;
77 tb = b;
80 /* We only need to do dithering if depth is <24bpp */
81 if (fbd.var.bits_per_pixel < 24) {
82 tr = CLAMP(tr + add*2 + 1);
83 tg = CLAMP(tg + add);
84 tb = CLAMP(tb + add*2 + 1);
87 tr >>= (8 - fbd.rlen);
88 tg >>= (8 - fbd.glen);
89 tb >>= (8 - fbd.blen);
91 i = (tr << fbd.var.red.offset) |
92 (tg << fbd.var.green.offset) |
93 (tb << fbd.var.blue.offset);
95 if (fbd.var.bits_per_pixel == 16) {
96 *(u16*)dst = i;
97 } else if (fbd.var.bits_per_pixel == 24) {
98 if (endianess == little) {
99 *(u16*)dst = i & 0xffff;
100 dst[2] = (i >> 16) & 0xff;
101 } else {
102 *(u16*)dst = (i >> 8) & 0xffff;
103 dst[2] = i & 0xff;
105 } else if (fbd.var.bits_per_pixel == 32) {
106 *(u32*)dst = i;
111 /* Converts a RGBA/RGB image to whatever format the framebuffer uses */
112 void rgba2fb(rgbacolor* data, u8 *bg, u8* out, int len, int y, u8 alpha, u8 opacity)
114 int i, add = 0;
115 rgbcolor* rgb = (rgbcolor*)data;
117 add ^= (0 ^ y) & 1 ? 1 : 3;
119 for (i = 0; i < len; i++) {
120 if (alpha) {
121 if (opacity == 0xff)
122 put_pixel(data->a, data->r, data->g, data->b, bg, out, add);
123 else
124 put_pixel(opacity * data->a / 255, data->r, data->g, data->b, bg, out, add);
125 data++;
126 } else {
127 put_pixel(opacity, rgb->r, rgb->g, rgb->b, bg, out, add);
128 rgb++;
131 out += fbd.bytespp;
132 bg += fbd.bytespp;
133 add ^= 3;
137 void blit(u8 *src, rect *bnd, int src_w, u8 *dst, int x, int y, int dst_w)
139 u8 *to;
140 int cy, j;
142 to = dst + (y * dst_w + x) * fbd.bytespp;
144 j = (bnd->x2 - bnd->x1 + 1) * fbd.bytespp;
145 for (cy = bnd->y1; cy <= bnd->y2; cy++) {
146 memcpy(to, src + (cy * src_w + bnd->x1) * fbd.bytespp, j);
147 to += dst_w * fbd.bytespp;
152 * Rectangle-related functions
153 * ---------------------------
155 * The following assumptions are made about the rects as used withing the
156 * fbsplash rendering subsystem:
158 * - coordinates span the ranges: 0..xres-1, 0..yres-1
159 * - a rect is valid if the coordinates fall within the specified ranges
160 * and y1 >= x2, y2 >= y1
161 * - if x1 = x2 or y1 = y2, then the rect has width/height equal to 1 pixel
165 * Interpolate between two rects based on the current value of the progress variable.
167 * @param a A rect to interpolate from.
168 * @param b A rect to interpolate to.
169 * @param c A rect where the result of the interpolation is saved.
171 void rect_interpolate(rect *a, rect *b, rect *c)
173 int h = FBSPL_PROGRESS_MAX - config.progress;
175 c->x1 = (a->x1 * h + b->x1 * config.progress) / FBSPL_PROGRESS_MAX;
176 c->x2 = (a->x2 * h + b->x2 * config.progress) / FBSPL_PROGRESS_MAX;
177 c->y1 = (a->y1 * h + b->y1 * config.progress) / FBSPL_PROGRESS_MAX;
178 c->y2 = (a->y2 * h + b->y2 * config.progress) / FBSPL_PROGRESS_MAX;
182 * Set 'c' to a rectangle encompassing both 'a' and 'b'.
184 void rect_bnd(rect *a, rect *b, rect *c)
186 c->x1 = min(a->x1, b->x1);
187 c->x2 = max(a->x2, b->x2);
188 c->y1 = min(a->y1, b->y1);
189 c->y2 = max(a->y2, b->y2);
193 * Set 'c' to the intersection of 'a' and 'b'.
195 void rect_min(rect *a, rect *b, rect *c)
197 c->x1 = max(a->x1, b->x1);
198 c->x2 = min(a->x2, b->x2);
199 c->y1 = max(a->y1, b->y1);
200 c->y2 = min(a->y2, b->y2);
203 bool rect_intersect(rect *a, rect *b)
205 if (a->x2 < b->x1 || a->x1 > b->x2 || a->y1 > b->y2 || a->y2 < b->y1)
206 return false;
207 else
208 return true;
212 * Check if rect a contains rect b.
214 bool rect_contains(rect *a, rect *b)
216 if (a->x1 <= b->x1 && a->y1 <= b->y1 && a->x2 >= b->x2 && a->y2 >= b->y2)
217 return true;
218 else
219 return false;
223 * Sanitize a rectangle.
225 * @param theme The theme to which the rect belongs.
226 * @param re The rectangle to be sanitized.
228 void rect_sanitize(stheme_t *theme, rect *re)
230 if (re->x1 < 0)
231 re->x1 = 0;
232 else if (re->x1 >= theme->xres)
233 re->x1 = theme->xres - 1;
235 if (re->x2 < 0)
236 re->x2 = 0;
237 else if (re->x2 >= theme->xres)
238 re->x2 = theme->xres - 1;
240 if (re->y1 < 0)
241 re->y1 = 0;
242 else if (re->y1 >= theme->yres)
243 re->y1 = theme->yres - 1;
245 if (re->y2 < 0)
246 re->y2 = 0;
247 else if (re->y2 >= theme->yres)
248 re->y2 = theme->yres - 1;
252 * Add a rect to the re-blit list.
254 * @param theme The theme for which the rect is to be added.
255 * @param a The rect to be added.
257 void blit_add(stheme_t *theme, rect *a)
259 rect *re = malloc(sizeof(rect));
260 if (!re)
261 return;
263 memcpy(re, a, sizeof(rect));
266 * TODO: Possibly remove this call when the whole module
267 * is properly unittested and we're sure no pathological situations
268 * can arise.
270 rect_sanitize(theme, re);
272 list_add(&theme->blit, re);
276 * Normalize the re-blit list.
278 * Collapse rects that contain each other into single items.
280 * TODO: normalize partially overlapping rects.
282 void blit_normalize(stheme_t *theme)
284 item *i, *j, *prev;
285 rect *a, *b;
287 for (i = theme->blit.head; i; i = i->next) {
288 start: a = i->p;
289 prev = i;
291 for (j = i->next; j; j = j->next) {
292 b = j->p;
294 if (rect_contains(a, b)) {
295 list_del(&theme->blit, prev, j);
296 free(b);
297 j = prev;
298 } else if (rect_contains(b, a)) {
299 i->p = b;
300 list_del(&theme->blit, prev, j);
301 free(a);
302 goto start;
304 prev = j;
309 void render_add(stheme_t *theme, obj *o, rect *a)
311 // printf("blit add: %d %d %d %d %x\n ", a->x1, a->y1, a->x2, a->y2, o->type);
312 return;
315 int obj_opacity_steps(int time) {
316 int t;
318 if (time < 500)
319 return 10;
321 t = 20 * time / 1000;
323 if (t > 255)
324 return 255;
325 else
326 return t;
329 void obj_visibility_set(stheme_t *theme, obj *o, bool visible)
331 int n;
334 * If an object is being made invisible, it won't be processed by
335 * any prerender routines and we have to make sure its area will be
336 * repainted.
338 if (!visible && o->visible) {
339 blit_add(theme, &o->bnd);
340 render_add(theme, o, &o->bnd);
342 if (o->blendout) {
343 n = obj_opacity_steps(o->blendout);
344 o->op_tstep = o->blendout / n;
345 o->wait_msecs = o->op_tstep;
346 o->op_step = -255 / n;
347 list_add(&theme->fxobjs, o);
348 } else {
349 o->visible = visible;
352 } else if (visible && !o->visible) {
353 if (o->blendin) {
354 o->opacity = 0;
355 n = obj_opacity_steps(o->blendin);
356 o->op_tstep = o->blendin / n;
357 o->wait_msecs = o->op_tstep;
358 o->op_step = 255 / n;
359 list_add(&theme->fxobjs, o);
362 o->visible = visible;
368 * Interpolate two boxes, based on the value of the config->progress variable.
370 * This is a somewhat strange implementation of a progress bar, introduced
371 * by the authors of Bootsplash.
373 void box_interpolate(box *a, box *b, box *c)
375 int h = FBSPL_PROGRESS_MAX - config.progress;
377 #define inter_color(clo, cl1, cl2) \
379 clo.r = (cl1.r * h + cl2.r * config.progress) / FBSPL_PROGRESS_MAX; \
380 clo.g = (cl1.g * h + cl2.g * config.progress) / FBSPL_PROGRESS_MAX; \
381 clo.b = (cl1.b * h + cl2.b * config.progress) / FBSPL_PROGRESS_MAX; \
382 clo.a = (cl1.a * h + cl2.a * config.progress) / FBSPL_PROGRESS_MAX; \
384 c->attr = a->attr;
386 rect_interpolate(&a->re, &b->re, &c->re);
388 inter_color(c->c_ul, a->c_ul, b->c_ul);
389 inter_color(c->c_ur, a->c_ur, b->c_ur);
390 inter_color(c->c_ll, a->c_ll, b->c_ll);
391 inter_color(c->c_lr, a->c_lr, b->c_lr);
394 void box_prerender(stheme_t *theme, box *b, bool force)
396 obj *o = container_of(b);
398 if (b->attr & BOX_INTER) {
399 box *tb = malloc(sizeof(box));
400 if (!tb)
401 return;
403 box_interpolate(b, b->inter, tb);
405 /* No change since last time? */
406 if (!memcmp(tb, b->curr, sizeof(box)) && !force) {
407 free(tb);
408 return;
412 * In the case of horizontal and vertical gradients, or solid boxes,
413 * optimize the rendering process by only rendering the new part of
414 * the box.
416 if (b->attr & (BOX_VGRAD | BOX_SOLID) && b->curr->re.y1 == tb->re.y1 && b->curr->re.y2 == tb->re.y2 && !force) {
417 rect re;
419 re.y1 = tb->re.y1;
420 re.y2 = tb->re.y2;
422 if (b->curr->re.x1 != tb->re.x1) {
423 re.x1 = min(b->curr->re.x1, tb->re.x1);
424 re.x2 = max(b->curr->re.x1, tb->re.x1);
425 blit_add(theme, &re);
426 render_add(theme, o, &re);
429 if (b->curr->re.x2 != tb->re.x2) {
430 re.x1 = min(b->curr->re.x2, tb->re.x2);
431 re.x2 = max(b->curr->re.x2, tb->re.x2);
432 blit_add(theme, &re);
433 render_add(theme, o, &re);
436 if (memcmp(&tb->re, &o->bnd, sizeof(rect))) {
437 memcpy(&o->bnd, &tb->re, sizeof(rect));
440 } else if (b->attr & (BOX_HGRAD | BOX_SOLID) && b->curr->re.x1 == tb->re.x1 && b->curr->re.x2 == tb->re.x2 && !force) {
441 rect re;
443 re.x1 = tb->re.x1;
444 re.x2 = tb->re.x2;
446 if (b->curr->re.y1 != tb->re.y1) {
447 re.y1 = min(b->curr->re.y1, tb->re.y1);
448 re.y2 = max(b->curr->re.y1, tb->re.y1);
449 blit_add(theme, &re);
450 render_add(theme, o, &re);
453 if (b->curr->re.y2 != tb->re.y2) {
454 re.y1 = min(b->curr->re.y2, tb->re.y2);
455 re.y2 = max(b->curr->re.y2, tb->re.y2);
456 blit_add(theme, &re);
457 render_add(theme, o, &re);
460 if (memcmp(&tb->re, &o->bnd, sizeof(rect))) {
461 memcpy(&o->bnd, &tb->re, sizeof(rect));
463 /* Render the whole box without any optimizations. */
464 } else {
465 if (memcmp(&tb->re, &o->bnd, sizeof(rect))) {
466 blit_add(theme, &o->bnd);
467 render_add(theme, o, &o->bnd);
468 memcpy(&o->bnd, &tb->re, sizeof(rect));
471 /* TODO: add some more optimizations here? */
472 blit_add(theme, &tb->re);
473 render_add(theme, o, &tb->re);
476 free(b->curr);
477 b->curr = tb;
479 } else {
480 blit_add(theme, &o->bnd);
481 render_add(theme, o, &o->bnd);
485 void box_render(stheme_t *theme, box *box, rect *re, u8 *target, u8 opacity)
487 int x, y, a, r, g, b, h;
488 int add;
489 u8 *pic;
490 float hr, hg, hb, ha, fr, fg, fb, fa;
491 int r1, r2, g1, g2, b1, b2, a1, a2;
492 int h1, h2;
494 int b_width = box->re.x2 - box->re.x1 + 1;
495 int b_height = box->re.y2 - box->re.y1 + 1;
497 if (b_height > 1)
498 h = b_height -1;
499 else
500 h = 1;
502 for (y = re->y1; y <= re->y2; y++) {
503 bool opt = false;
504 pic = target + (re->x1 + y * theme->xres) * fbd.bytespp;
506 /* Do a nice 2x2 ordered dithering, like it was done in bootsplash;
507 * this makes the pics in 15/16bpp modes look much nicer;
508 * the produced pattern is:
509 * 303030303..
510 * 121212121..
512 add = (box->re.x1 & 1);
513 add ^= (add ^ y) & 1 ? 1 : 3;
515 if (box->attr & BOX_SOLID) {
516 r = box->c_ul.r;
517 g = box->c_ul.g;
518 b = box->c_ul.b;
519 a = box->c_ul.a;
520 opt = true;
521 } else {
522 h1 = box->re.y2 - y;
523 h2 = y - box->re.y1;
525 r1 = (h1 * box->c_ul.r + h2 * box->c_ll.r)/h;
526 r2 = (h1 * box->c_ur.r + h2 * box->c_lr.r)/h;
528 g1 = (h1 * box->c_ul.g + h2 * box->c_ll.g)/h;
529 g2 = (h1 * box->c_ur.g + h2 * box->c_lr.g)/h;
531 b1 = (h1 * box->c_ul.b + h2 * box->c_ll.b)/h;
532 b2 = (h1 * box->c_ur.b + h2 * box->c_lr.b)/h;
534 a1 = (h1 * box->c_ul.a + h2 * box->c_ll.a)/h;
535 a2 = (h1 * box->c_ur.a + h2 * box->c_lr.a)/h;
537 if (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2) {
538 opt = true;
539 } else {
540 opt = false;
541 r2 -= r1;
542 g2 -= g1;
543 b2 -= b1;
544 a2 -= a1;
546 hr = 1.0/b_width * r2;
547 hg = 1.0/b_width * g2;
548 hb = 1.0/b_width * b2;
549 ha = 1.0/b_width * a2;
552 r = r1; fr = (float)r1 + hr * (re->x1 - box->re.x1);
553 g = g1; fg = (float)g1 + hg * (re->x1 - box->re.x1);
554 b = b1; fb = (float)b1 + hb * (re->x1 - box->re.x1);
555 a = a1; fa = (float)a1 + ha * (re->x1 - box->re.x1);
558 for (x = re->x1; x <= re->x2; x++) {
559 if (!opt) {
560 fa += ha;
561 fr += hr;
562 fg += hg;
563 fb += hb;
565 a = (u8)fa;
566 b = (u8)fb;
567 g = (u8)fg;
568 r = (u8)fr;
571 if (opacity == 0xff, 1) {
572 put_pixel(a, r, g, b, pic, pic, add);
573 } else {
574 put_pixel(a * opacity / 255, r, g, b, pic, pic, add);
576 pic += fbd.bytespp;
577 add ^= 3;
582 void icon_render(stheme_t *theme, icon *ticon, rect *re, u8 *target)
584 obj *o = container_of(ticon);
585 int y, yi, xi, wi, hi;
586 u8 *out = NULL;
587 u8 *in = NULL;
589 xi = re->x1 - ticon->x;
590 yi = re->y1 - ticon->y;
591 wi = re->x2 - re->x1 + 1;
592 hi = yi + re->y2 - re->y1 + 1;
594 for (y = ticon->y + yi; yi < hi; yi++, y++) {
595 out = target + (ticon->x + xi + y * theme->xres) * fbd.bytespp;
596 in = ticon->img->picbuf + (xi + yi * ticon->img->w) * 4;
597 rgba2fb((rgbacolor*)in, out, out, wi, y, 1, o->opacity);
601 void icon_prerender(stheme_t *theme, icon *c, bool force)
603 obj *o = container_of(c);
605 if (!c->img || !c->img->picbuf)
606 return;
608 if (c->crop) {
609 rect crn;
611 /* If the cropping rectangle is unchanged, don't render anything. */
612 rect_interpolate(&c->crop_from, &c->crop_to, &crn);
613 if (!memcmp(&crn, &c->crop_curr, sizeof(rect)) && !force)
614 return;
616 /* TODO: add optimization: repaint only a part of the icon */
617 memcpy(&c->crop_curr, &crn, sizeof(rect));
619 crn.x1 += c->x;
620 crn.x2 += c->x;
621 crn.y1 += c->y;
622 crn.y2 += c->y;
624 blit_add(theme, &o->bnd);
625 render_add(theme, o, &o->bnd);
627 blit_add(theme, &crn);
628 render_add(theme, o, &crn);
630 memcpy(&o->bnd, &crn, sizeof(rect));
631 } else {
632 blit_add(theme, &o->bnd);
633 render_add(theme, o, &o->bnd);
638 * Render (a part of) an object into a screen buffer.
640 * @param theme Theme to which the object belongs.
641 * @param o Object to be rendered.
642 * @param re The rect defining the part of the screen to be rendered.
643 * @param tg The target screen buffer.
645 void obj_render(stheme_t *theme, obj *o, rect *re, u8 *tg)
647 if (!o->visible)
648 return;
650 switch (o->type) {
652 case o_icon:
653 icon_render(theme, o->p, re, tg);
654 break;
656 case o_box:
658 box *t = o->p;
659 if (t->curr)
660 box_render(theme, t->curr, re, tg, o->opacity);
661 else
662 box_render(theme, t, re, tg, o->opacity);
663 break;
666 #if WANT_TTF
667 case o_text:
668 text_render(theme, o->p, re, tg);
669 break;
670 #endif
671 #if WANT_MNG
672 case o_anim:
673 anim_render(theme, o->p, re, tg);
674 break;
675 #endif
676 default:
677 break;
682 * Prepare an object for rendering.
684 * @param theme Theme to which the object belongs.
685 * @param o Object to be rendered.
686 * @param force Force redrawing of the whole object?
688 void obj_prerender(stheme_t *theme, obj *o, bool force)
690 if (!o->visible)
691 return;
693 switch (o->type) {
695 case o_icon:
696 icon_prerender(theme, o->p, force);
697 break;
699 case o_box:
700 box_prerender(theme, o->p, force);
701 break;
703 #if WANT_TTF
704 case o_text:
705 text_prerender(theme, o->p, force);
706 break;
707 #endif
708 #if WANT_MNG
709 case o_anim:
710 anim_prerender(theme, o->p, force);
711 break;
712 #endif
713 default:
714 break;
718 void render_objs(stheme_t *theme, u8 *target, u8 mode, bool force)
720 item *i, *j;
721 u8 *bg;
724 * First pass: mark rectangles for reblitting and rerendering
725 * via object specific rendering routines. At this stage no
726 * objects are actually rendered, but parts of the screen that
727 * have to be updated are marked for processing in the second
728 * pass.
730 for (i = theme->objs.head; i != NULL; i = i->next) {
731 obj *o = i->p;
732 if (!(o->modes & mode))
733 continue;
735 /* Only invalidated objects are updated. */
736 if (o->invalid) {
737 obj_prerender(theme, o, force);
738 o->invalid = false;
742 blit_normalize(theme);
744 if (mode & FBSPL_MODE_VERBOSE) {
745 bg = (u8*)theme->verbose_img.data;
746 } else {
747 bg = (u8*)theme->silent_img.data;
750 /* Second pass: actually render the objects. */
751 for (i = theme->blit.head; i != NULL; i = i->next) {
752 rect *re = i->p;
754 /* Blit the background image. */
755 blit(bg, re, theme->xres, target, re->x1, re->y1, theme->xres);
757 for (j = theme->objs.head; j != NULL; j = j->next) {
758 obj *o = j->p;
759 rect tmp;
761 if (!(o->modes & mode))
762 continue;
764 rect_min(&o->bnd, re, &tmp);
766 /* Skip this object if its bouding box does not intersect
767 * the target area. */
768 if (tmp.x2 < tmp.x1 || tmp.y2 < tmp.y1)
769 continue;
771 obj_render(theme, o, &tmp, target);
777 * Invalidate all objects in a given theme.
779 void invalidate_all(stheme_t *theme)
781 item *i;
783 for (i = theme->objs.head; i != NULL; i = i->next) {
784 obj *o = i->p;
785 o->invalid = true;
790 * Invalidate and set visibility of all objects comprising a textbox.
792 void invalidate_textbox(stheme_t *theme, bool active)
794 item *i;
796 for (i = theme->textbox.head; i != NULL; i = i->next) {
797 obj *o = i->p;
798 o->invalid = true;
799 obj_visibility_set(theme, o, active);
804 * Invalidate all objects that depend on a specific service.
806 void invalidate_service(stheme_t *theme, char *svc, enum ESVC state)
808 item *i;
810 for (i = theme->objs.head; i != NULL; i = i->next) {
811 obj *o = i->p;
813 switch (o->type) {
815 case o_icon:
817 icon *t = o->p;
818 if (!t->svc || strcmp(t->svc, svc))
819 continue;
821 o->invalid = true;
822 obj_visibility_set(theme, o, t->type == state);
823 break;
826 #if WANT_MNG
827 case o_anim:
829 anim *t = o->p;
830 if (!t->svc || strcmp(t->svc, svc))
831 continue;
833 o->invalid = true;
834 obj_visibility_set(theme, o, t->type == state);
835 break;
837 #endif
838 default:
839 break;
845 * Invalidate all objects that depend on the progress variable.
847 void invalidate_progress(stheme_t *theme)
849 item *i;
851 for (i = theme->objs.head; i != NULL; i = i->next) {
852 obj *o = i->p;
854 switch (o->type) {
856 case o_box:
858 box *b = o->p;
860 if (b->inter)
861 o->invalid = true;
862 break;
865 case o_icon:
867 icon *ic = o->p;
869 if (ic->crop)
870 o->invalid = true;
871 break;
873 #if WANT_TTF
874 case o_text:
876 text *t = o->p;
878 if (t->curr_progress >= 0)
879 o->invalid = true;
880 break;
882 #endif
883 #if WANT_MNG
884 case o_anim:
886 anim *t = o->p;
888 if ((t->flags & F_ANIM_METHOD_MASK) == F_ANIM_PROPORTIONAL)
889 o->invalid = true;
890 break;
892 #endif
897 void bnd_init(stheme_t *theme)
899 item *i;
901 /* Initialize the bounding rectangles */
902 for (i = theme->objs.head; i != NULL; i = i->next) {
903 obj *a = i->p;
905 switch (a->type) {
907 case o_box:
909 box *t = a->p;
911 if (t->inter) {
912 rect_bnd(&t->re, &t->inter->re, &a->bnd);
913 } else {
914 memcpy(&a->bnd, &t->re, sizeof(rect));
916 rect_sanitize(theme, &a->bnd);
917 break;
920 case o_icon:
922 icon *t = a->p;
924 if (t->crop) {
925 rect_bnd(&t->crop_from, &t->crop_to, &a->bnd);
926 a->bnd.x1 += t->x;
927 a->bnd.x2 += t->x;
928 a->bnd.y1 += t->y;
929 a->bnd.y2 += t->y;
930 } else {
931 a->bnd.x1 = t->x;
932 a->bnd.y1 = t->y;
933 a->bnd.x2 = a->bnd.x1 + t->img->w - 1;
934 a->bnd.y2 = a->bnd.y1 + t->img->h - 1;
936 rect_sanitize(theme, &a->bnd);
937 break;
939 #if WANT_TTF
940 case o_text:
941 text_bnd(theme, a->p, &a->bnd);
942 break;
943 #endif
944 #if WANT_MNG
945 case o_anim:
947 anim *t = a->p;
948 a->bnd.x1 = t->x;
949 a->bnd.y1 = t->y;
950 a->bnd.x2 = t->x + t->w - 1;
951 a->bnd.y2 = t->y + t->h - 1;
952 rect_sanitize(theme, &a->bnd);
953 break;
955 #endif
956 default:
957 break;
962 #if 0
963 int bgcache_init(stheme_t *theme)
965 item *i, *j;
967 /* Prepare the background image */
968 memcpy(theme->bgbuf, theme->silent_img.data, theme->xres * theme->yres * fbd.bytespp);
971 * If we intersect another object, we will need to allocate a background
972 * buffer to avoid unnecessary redrawing later. If we don't intersect
973 * anything, there's no need for a buffer as we will get our background
974 * directly from silent_img
976 for (i = theme->objs.head; i != NULL; i = i->next) {
977 obj *a = i->p;
979 for (j = theme->objs.head; j != i; j = j->next) {
980 obj *b = j->p;
982 if (rect_intersect(&a->bnd, &b->bnd)) {
983 a->bgcache = malloc(fbd.bytespp * (a->bnd.x2 - a->bnd.x1 + 1) * (a->bnd.y2 - a->bnd.y1 + 1));
984 blit(theme->bgbuf, &a->bnd, theme->xres, a->bgcache, 0, 0, (a->bnd.x2 - a->bnd.x1 + 1));
985 break;
989 render_obj(theme, theme->bgbuf, 's', a, true);
992 return 0;
994 #endif