summaryrefslogtreecommitdiff
path: root/src/field_screen_effect.c
blob: 19ed25950578f1d80cdb2f8fc577b629ecd0ad07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "global.h"

void SetFlashScanlineEffectWindowBoundary(u16 *dest, u32 y, s32 left, s32 right)
{
    if (y <= 160)
    {
        if (left < 0)
            left = 0;
        if (left > 255)
            left = 255;
        if (right < 0)
            right = 0;
        if (right > 255)
            right = 255;
        dest[y] = (left << 8) | right;
    }
}

/*
 * Draws a circle by approximating xy² + yx² = radius².
 *
 * error is approximately xy² - yx². Negative values mean the circle is
 * slightly too large, and positive values mean the circle is slightly
 * too small. By decreasing xy whenever the error becomes negative the
 * code slightly under-approximates the size of the circle.
 *
 * The subtractive terms compute yx² - (yx - 1)², and therefore the sum
 * is yx² - 1:
 *   yx               |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7
 *   (yx * 2) - 1     | -1 |  1 |  3 |  5 |  7 |  9 | 11 | 13
 *   yx² - (yx - 1)²  | -1 |  1 |  3 |  5 |  7 |  9 | 11 | 13
 *   cumulative error | -1 |  0 |  3 |  8 | 15 | 24 | 35 | 48
 *   yx²              |  0 |  1 |  4 |  9 | 16 | 25 | 36 | 49
 *
 * The additive terms compute xy² - (xy - 1)² - 1, and therefore the sum
 * (badly) approximates Σi² - (i - 1)², i ∈ (xy, r), consider r = 18:
 *   xy               | 18 | 17 | 16 |  15 |  14 |  13 | ... |   0
 *   xy² - (xy - 1)²  | 35 | 33 | 31 |  29 |  27 |  25 | ... |  -1
 *   2 * (xy - 1)     | 34 | 32 | 30 |  28 |  26 |  24 | ... |  -2
 *   cumulative error | 34 | 66 | 96 | 124 | 150 | 174 | ... | 304
 *   Σi² - (i - 1)²   | 35 | 68 | 99 | 128 | 155 | 180 | ... | 323
 * 18² = 324, so if the iterations ran until xy = 0 the cumulative error
 * would be xy² - r.
 *
 * The error is initialized to r, which corrects for the error in the
 * additive terms. In practice all r iterations don't occur because we
 * early-exit when yx > xy, so it's half-way between a fix for that
 * error and an approximation of the midpoint between r² and (r + 1)².
 *
 * The algorithm takes advantage of symmetry to compute boundaries in
 * both directions out from centerY (using yx for y), and also both
 * directions *in* from centerY ± radius (using xy for y). Because xy
 * doesn't change on every iteration, we will frequently overwrite
 * boundaries set in the previous iteration.
 */
void SetFlashScanlineEffectWindowBoundaries(u16 *dest, s32 centerX, s32 centerY, s32 radius)
{
    s32 xy = radius;
    s32 error = radius;
    s32 yx = 0;
    while (xy >= yx)
    {
        SetFlashScanlineEffectWindowBoundary(dest, centerY - yx, centerX - xy, centerX + xy);
        SetFlashScanlineEffectWindowBoundary(dest, centerY + yx, centerX - xy, centerX + xy);
        SetFlashScanlineEffectWindowBoundary(dest, centerY - xy, centerX - yx, centerX + yx);
        SetFlashScanlineEffectWindowBoundary(dest, centerY + xy, centerX - yx, centerX + yx);
        error -= (yx * 2) - 1;
        yx++;
        if (error < 0)
        {
            error += 2 * (xy - 1);
            xy--;
        }
    }
}