Exit the IDE, make a copy of the previous chapter folder and rename it to ch05. Go to the ch05 folder and double-click on the project file to bring the IDE.

Note that the routines presented in this chapter do not make the slightest attempt of being efficient. Faster ways of doing things that we'll be doing here exist, and most of them are very easy to implement. The way things are presented here should make them relatively easy to comprehend.

Add the following after the `int *gFrameBuffer;`

bit:

```
int *gTempBuffer;
```

Next, copy and paste the following after the `update()`

function:

```
unsigned int blend_avg(int source, int target)
{
unsigned int sourcer = ((unsigned int)source >> 0) & 0xff;
unsigned int sourceg = ((unsigned int)source >> 8) & 0xff;
unsigned int sourceb = ((unsigned int)source >> 16) & 0xff;
unsigned int targetr = ((unsigned int)target >> 0) & 0xff;
unsigned int targetg = ((unsigned int)target >> 8) & 0xff;
unsigned int targetb = ((unsigned int)target >> 16) & 0xff;
targetr = (sourcer + targetr) / 2;
targetg = (sourceg + targetg) / 2;
targetb = (sourceb + targetb) / 2;
return (targetr << 0) |
(targetg << 8) |
(targetb << 16) |
0xff000000;
}
unsigned int blend_mul(int source, int target)
{
unsigned int sourcer = ((unsigned int)source >> 0) & 0xff;
unsigned int sourceg = ((unsigned int)source >> 8) & 0xff;
unsigned int sourceb = ((unsigned int)source >> 16) & 0xff;
unsigned int targetr = ((unsigned int)target >> 0) & 0xff;
unsigned int targetg = ((unsigned int)target >> 8) & 0xff;
unsigned int targetb = ((unsigned int)target >> 16) & 0xff;
targetr = (sourcer * targetr) >> 8;
targetg = (sourceg * targetg) >> 8;
targetb = (sourceb * targetb) >> 8;
return (targetr << 0) |
(targetg << 8) |
(targetb << 16) |
0xff000000;
}
unsigned int blend_add(int source, int target)
{
unsigned int sourcer = ((unsigned int)source >> 0) & 0xff;
unsigned int sourceg = ((unsigned int)source >> 8) & 0xff;
unsigned int sourceb = ((unsigned int)source >> 16) & 0xff;
unsigned int targetr = ((unsigned int)target >> 0) & 0xff;
unsigned int targetg = ((unsigned int)target >> 8) & 0xff;
unsigned int targetb = ((unsigned int)target >> 16) & 0xff;
targetr += sourcer;
targetg += sourceg;
targetb += sourceb;
if (targetr > 0xff) targetr = 0xff;
if (targetg > 0xff) targetg = 0xff;
if (targetb > 0xff) targetb = 0xff;
return (targetr << 0) |
(targetg << 8) |
(targetb << 16) |
0xff000000;
}
void scaleblit()
{
int yofs = 0;
for (int i = 0; i < WINDOW_HEIGHT; i++)
{
for (int j = 0; j < WINDOW_WIDTH; j++)
{
int c =
(int)((i * 0.95) + (WINDOW_HEIGHT * 0.025)) * WINDOW_WIDTH +
(int)((j * 0.95) + (WINDOW_WIDTH * 0.025));
gFrameBuffer[yofs + j] =
blend_avg(gFrameBuffer[yofs + j], gTempBuffer[c]);
}
yofs += WINDOW_WIDTH;
}
}
```

What we have here are three functions that take in two pixel values, do something with them, and return the result. Way more efficient ways of doing all of these three common blending operations exist.

The "avg" blend calculates the average of two pixel values. The operation is performed per color component. One cheaper way of doing this is to first use the bitwise AND operator to clear out the least significant bits of all of the color components, shift both colors right one bit, and then add them together. The average blend could be used, for instance, for transparent materials like stained glass.

The "mul" blend calculates the multiplication result of two pixel values. The operation is, again, performed per color component. The multiplication blend is typically used for shadows. If you multiply something by 1, you get the same value. Multiplying with a lower value gets you lower (and thus, darker) result. Note that full white (0xff, 255) isn't exactly '1' in this calculation, and thus you will always get slightly darker pixels when using this blend `((255*255)/256 = 254)`

. (You may wish to dig up Jim Blinn's "Dirty Pixels" if you're interested in solving this problem. It's way outside the scope of this tutorial, however).

The "add" blend calculates the saturated sum result of two pixel values. The Additive blend again performs the operation for each color component separately. The color is guarded against overflow; i.e. adding pixels together will eventually give you full white color. The additive blend is typically used for lighting effects such as glares, sparkles, explosions, lightsabers and laser weapons.

The only super common blend operation not implemented here is alpha blend, which fades between two colors based on an alpha value.

Let's look at the `scaleblit()`

a bit later on.

Next, replace the `init()`

function with the following:

```
void init()
{
gTempBuffer = new int[WINDOW_WIDTH * WINDOW_HEIGHT];
for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT; i++)
{
gFrameBuffer[i] = 0xff000000;
gTempBuffer[i] = 0xff000000;
}
}
```

And it's time to replace the whole render() function again:

```
void render(Uint64 aTicks)
{
for (int i = 0; i < 128; i++)
{
int d = (int)aTicks + i * 4;
drawsprite((int)(WINDOW_WIDTH / 2 + sin(d * 0.0034) * sin(d * 0.0134) * (WINDOW_WIDTH / 2 - 20)),
(int)(WINDOW_HEIGHT / 2 + sin(d * 0.0033) * sin(d * 0.0234) * (WINDOW_HEIGHT / 2 - 20)),
((int)(sin((aTicks * 0.2 + i) * 0.234897) * 127 + 128) << 16) |
((int)(sin((aTicks * 0.2 + i) * 0.123489) * 127 + 128) << 8) |
((int)(sin((aTicks * 0.2 + i) * 0.312348) * 127 + 128) << 0));
}
memcpy(gTempBuffer, gFrameBuffer, sizeof(int) * WINDOW_WIDTH * WINDOW_HEIGHT);
scaleblit();
}
```

Here we're drawing the little colorful "worm" with `drawsprite()`

again, but we're not clearing the screen before doing so. Afterwards, we copy the currently active image to the `gTempBuffer[]`

array, and then we're calling `scaleblit()`

.

Now the program is ready for compile and run. Try it out.

So what does the `scaleblit()`

do? In a very inefficient way, it takes 95% of the image, stretches it over the whole image (using point sampling), and blits it back, blending it with the "average" blend mode.

"Blit", or "blt", is short for "bit block transfer", which is a fancy way of saying "copy (graphics) data from one place to another". You may have heard of the Amiga blitter chip? Now you know what it means.

Since we're doing this over and over again to the same image, the result is constantly zooming the data. Also, since we're doing the blit using blending, the colors keep blending together, smoothing out.

Note that the strength of the blur effect is very dependent on the speed of the machine it's run on, and there's also a difference in speed between debug and release builds. Like with the snow simulation, modern computers are so fast that you probably get the effect in 60Hz (or whatever your screen refresh is).

Additional things to try:

- Replace the average blend with additive, or multiplicative blend. Also try without blending.
- If using multiplicative blend, change screen clearing in
`init()`

to fill the screen with some bright color, like white. - Create sprite drawing routine that draws the sprites in additive mode. Comment out the
`memcpy()`

and`scaleblit()`

in`render()`

, draw sprites additively, and make the colors dark (replace`*127 + 128`

with`*12 + 12`

). - Optimize the average blit.
- Try different zooming levels. Also, note that you can use different zooming level for horizontal and vertical pixels.
- Animate the zooming level with time.
- Turn the average blend into a macro.
- Implement alpha blend (
`dst = (src1 * alpha) + (src2 * (1 - alpha))`

). - Instead of our default sprite, try loading a small image and drawing it instead. Draw it with average blending.

Next up: 06 - Primitives and Clipping..

Any comments etc. can be emailed to me.