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
14 * This is necessary to get FD_SET and FD_ZERO on platforms other than x86.
19 #include <linux/posix_types.h>
25 #include <sys/types.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? */
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) {
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;
58 if (fbd
.var
.bits_per_pixel
== 16) {
60 } else if (fbd
.var
.bits_per_pixel
== 24) {
61 i
= *(u32
*)src
& 0xffffff;
62 } else if (fbd
.var
.bits_per_pixel
== 32) {
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;
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);
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) {
97 } else if (fbd
.var
.bits_per_pixel
== 24) {
98 if (endianess
== little
) {
99 *(u16
*)dst
= i
& 0xffff;
100 dst
[2] = (i
>> 16) & 0xff;
102 *(u16
*)dst
= (i
>> 8) & 0xffff;
105 } else if (fbd
.var
.bits_per_pixel
== 32) {
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
)
115 rgbcolor
* rgb
= (rgbcolor
*)data
;
117 add
^= (0 ^ y
) & 1 ? 1 : 3;
119 for (i
= 0; i
< len
; i
++) {
122 put_pixel(data
->a
, data
->r
, data
->g
, data
->b
, bg
, out
, add
);
124 put_pixel(opacity
* data
->a
/ 255, data
->r
, data
->g
, data
->b
, bg
, out
, add
);
127 put_pixel(opacity
, rgb
->r
, rgb
->g
, rgb
->b
, bg
, out
, add
);
137 void blit(u8
*src
, rect
*bnd
, int src_w
, u8
*dst
, int x
, int y
, int dst_w
)
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
)
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
)
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
)
232 else if (re
->x1
>= theme
->xres
)
233 re
->x1
= theme
->xres
- 1;
237 else if (re
->x2
>= theme
->xres
)
238 re
->x2
= theme
->xres
- 1;
242 else if (re
->y1
>= theme
->yres
)
243 re
->y1
= theme
->yres
- 1;
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
));
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
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
)
287 for (i
= theme
->blit
.head
; i
; i
= i
->next
) {
291 for (j
= i
->next
; j
; j
= j
->next
) {
294 if (rect_contains(a
, b
)) {
295 list_del(&theme
->blit
, prev
, j
);
298 } else if (rect_contains(b
, a
)) {
300 list_del(&theme
->blit
, 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);
315 int obj_opacity_steps(int time
) {
321 t
= 20 * time
/ 1000;
329 void obj_visibility_set(stheme_t
*theme
, obj
*o
, bool visible
)
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
338 if (!visible
&& o
->visible
) {
339 blit_add(theme
, &o
->bnd
);
340 render_add(theme
, o
, &o
->bnd
);
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
);
349 o
->visible
= visible
;
352 } else if (visible
&& !o
->visible
) {
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; \
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
));
403 box_interpolate(b
, b
->inter
, tb
);
405 /* No change since last time? */
406 if (!memcmp(tb
, b
->curr
, sizeof(box
)) && !force
) {
412 * In the case of horizontal and vertical gradients, or solid boxes,
413 * optimize the rendering process by only rendering the new part of
416 if (b
->attr
& (BOX_VGRAD
| BOX_SOLID
) && b
->curr
->re
.y1
== tb
->re
.y1
&& b
->curr
->re
.y2
== tb
->re
.y2
&& !force
) {
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
) {
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. */
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
);
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
;
490 float hr
, hg
, hb
, ha
, fr
, fg
, fb
, fa
;
491 int r1
, r2
, g1
, g2
, b1
, b2
, a1
, a2
;
494 int b_width
= box
->re
.x2
- box
->re
.x1
+ 1;
495 int b_height
= box
->re
.y2
- box
->re
.y1
+ 1;
502 for (y
= re
->y1
; y
<= re
->y2
; y
++) {
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:
512 add
= (box
->re
.x1
& 1);
513 add
^= (add
^ y
) & 1 ? 1 : 3;
515 if (box
->attr
& BOX_SOLID
) {
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
) {
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
++) {
571 if (opacity
== 0xff, 1) {
572 put_pixel(a
, r
, g
, b
, pic
, pic
, add
);
574 put_pixel(a
* opacity
/ 255, r
, g
, b
, pic
, pic
, add
);
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
;
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
)
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
)
616 /* TODO: add optimization: repaint only a part of the icon */
617 memcpy(&c
->crop_curr
, &crn
, sizeof(rect
));
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
));
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
)
653 icon_render(theme
, o
->p
, re
, tg
);
660 box_render(theme
, t
->curr
, re
, tg
, o
->opacity
);
662 box_render(theme
, t
, re
, tg
, o
->opacity
);
668 text_render(theme
, o
->p
, re
, tg
);
673 anim_render(theme
, o
->p
, re
, tg
);
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
)
696 icon_prerender(theme
, o
->p
, force
);
700 box_prerender(theme
, o
->p
, force
);
705 text_prerender(theme
, o
->p
, force
);
710 anim_prerender(theme
, o
->p
, force
);
718 void render_objs(stheme_t
*theme
, u8
*target
, u8 mode
, bool force
)
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
730 for (i
= theme
->objs
.head
; i
!= NULL
; i
= i
->next
) {
732 if (!(o
->modes
& mode
))
735 /* Only invalidated objects are updated. */
737 obj_prerender(theme
, o
, force
);
742 blit_normalize(theme
);
744 if (mode
& FBSPL_MODE_VERBOSE
) {
745 bg
= (u8
*)theme
->verbose_img
.data
;
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
) {
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
) {
761 if (!(o
->modes
& mode
))
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
)
771 obj_render(theme
, o
, &tmp
, target
);
777 * Invalidate all objects in a given theme.
779 void invalidate_all(stheme_t
*theme
)
783 for (i
= theme
->objs
.head
; i
!= NULL
; i
= i
->next
) {
790 * Invalidate and set visibility of all objects comprising a textbox.
792 void invalidate_textbox(stheme_t
*theme
, bool active
)
796 for (i
= theme
->textbox
.head
; i
!= NULL
; i
= i
->next
) {
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
)
810 for (i
= theme
->objs
.head
; i
!= NULL
; i
= i
->next
) {
818 if (!t
->svc
|| strcmp(t
->svc
, svc
))
822 obj_visibility_set(theme
, o
, t
->type
== state
);
830 if (!t
->svc
|| strcmp(t
->svc
, svc
))
834 obj_visibility_set(theme
, o
, t
->type
== state
);
845 * Invalidate all objects that depend on the progress variable.
847 void invalidate_progress(stheme_t
*theme
)
851 for (i
= theme
->objs
.head
; i
!= NULL
; i
= i
->next
) {
878 if (t
->curr_progress
>= 0)
888 if ((t
->flags
& F_ANIM_METHOD_MASK
) == F_ANIM_PROPORTIONAL
)
897 void bnd_init(stheme_t
*theme
)
901 /* Initialize the bounding rectangles */
902 for (i
= theme
->objs
.head
; i
!= NULL
; i
= i
->next
) {
912 rect_bnd(&t
->re
, &t
->inter
->re
, &a
->bnd
);
914 memcpy(&a
->bnd
, &t
->re
, sizeof(rect
));
916 rect_sanitize(theme
, &a
->bnd
);
925 rect_bnd(&t
->crop_from
, &t
->crop_to
, &a
->bnd
);
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
);
941 text_bnd(theme
, a
->p
, &a
->bnd
);
950 a
->bnd
.x2
= t
->x
+ t
->w
- 1;
951 a
->bnd
.y2
= t
->y
+ t
->h
- 1;
952 rect_sanitize(theme
, &a
->bnd
);
963 int bgcache_init(stheme_t
*theme
)
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
) {
979 for (j
= theme
->objs
.head
; j
!= i
; j
= j
->next
) {
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));
989 render_obj(theme
, theme
->bgbuf
, 's', a
, true);