Compositing and Displaying the Image
The main-program code to do all of this is almost identical to that in
the first demo program, but this time around we've added a small
twist: the code now supports not only a user-defined background color
but also a background image of sorts. Specifically, the user has
the option of choosing one of a set of predefined background patterns
that simulate a tiled background image. The patterns currently
include gradient-filled checkerboards (three of which are shown in the
second row of Figure C-5
in the color insert), smoothly interpolated diamonds
(third row of Figure C-5),
and radial waves (Figure C-1 and
fourth row of Figure C-5);
eventually, other patterns may be defined. This approach is simple
enough that it could be generated on the fly, as the image is displayed,
but in the interests of speed and simplicity, I chose to
define a second complete image buffer in the
mainprog_init() function. The background
buffer is filled as follows for the diamond pattern (contributed by
Adam M. Costello):
hmax = (bgscale-1)/2; /* half the max weight of a color */
max = 2*hmax; /* the max weight of a color */
for (row = 0; row < rpng2_info.height; ++row) {
yidx = row % bgscale;
if (yidx > hmax)
yidx = bgscale-1 - yidx;
dest = bg_data + row*bg_rowbytes;
for (i = 0; i < rpng2_info.width; ++i) {
xidx = i % bgscale;
if (xidx > hmax)
xidx = bgscale-1 - xidx;
k = xidx + yidx;
*dest++ = (k*r1 + (max-k)*r2) / max;
*dest++ = (k*g1 + (max-k)*g2) / max;
*dest++ = (k*b1 + (max-k)*b2) / max;
}
}
With this approach, the inner display loop requires only a tiny change to
support the background image instead of just a background color:
r = *src++;
g = *src++;
b = *src++;
a = *src++;
if (bg_image) { /* NEW */
bg_red = *src2++; /* NEW */
bg_green = *src2++; /* NEW */
bg_blue = *src2++; /* NEW */
} /* NEW */
if (a == 255) {
red = r;
green = g;
blue = b;
} else if (a == 0) {
red = bg_red;
green = bg_green;
blue = bg_blue;
} else {
/* this macro (copied from png.h) composites
* the foreground and background values and
* puts the result into the first argument */
alpha_composite(red, r, a, bg_red);
alpha_composite(green, g, a, bg_green);
alpha_composite(blue, b, a, bg_blue);
}
In other words, the background color used for compositing is now changed
once per pixel. (Note that the src2 pointer is initialized just
once per call. That's the only other change to the display routine to
support the background image.) The cases in which the alpha component is either
255 or 0 are handled separately for performance reasons only; using the
alpha_composite() macro would produce identical results. But because
the macro employs multiplication, addition, and bit-shifting for every pixel
(in fact, three times per pixel) and because fully opaque and fully transparent
pixels are generally by far the most numerous, the difference in speed would
probably be noticeable. It therefore makes sense to handle the two special
cases separately. Whether full opacity or full transparency is handled first
is less obvious; I guessed that opaque pixels are likely to be more common in
images with transparency, so the 255 case is checked first.
The results, using one of the more exotic radial-wave patterns as the
background, are shown in Figure C-1 in the color
insert. The base image
consists of partially transparent icicles hanging from opaque tree branches,
seen against a completely transparent sky. The figure is a composite of the
appearance after the first PNG pass (left half) and the final pass (right
half).
|