grid axes
[gba_blender] / src / polyfill.c
1 /*
2 blender for the Gameboy Advance
3 Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <string.h>
19 #include "polyfill.h"
20 #include "debug.h"
21
22 static unsigned char *fb;
23 static int fbwidth, fbheight;
24 static short scantab[2][160] __attribute__((section(".iwram")));
25
26 void polyfill_framebuffer(unsigned char *ptr, int w, int h)
27 {
28         fb = ptr;
29         fbwidth = w;
30         fbheight = h;
31 }
32
33 #define VNEXT(p)        (((p) == vlast) ? varr : (p) + 1)
34 #define VPREV(p)        ((p) == varr ? vlast : (p) - 1)
35 #define VSUCC(p, side)  ((side) == 0 ? VNEXT(p) : VPREV(p))
36
37 void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col)
38 {
39         int i, line, top, bot;
40         struct pvertex *vlast, *v, *vn;
41         int32_t x, y0, y1, dx, dy, slope, fx, fy;
42         short *tab, start, len;
43         unsigned char *fbptr;
44
45         vlast = varr + vnum - 1;
46         top = fbheight;
47         bot = 0;
48
49         for(i=0; i<vnum; i++) {
50                 v = varr + i;
51                 vn = VNEXT(v);
52
53                 if(vn->y == v->y) continue;
54
55                 if(vn->y > v->y) {
56                         tab = scantab[0];
57                 } else {
58                         tab = scantab[1];
59                         v = vn;
60                         vn = varr + i;
61                 }
62
63                 dx = vn->x - v->x;
64                 dy = vn->y - v->y;
65                 slope = (dx << 8) / dy;
66
67                 y0 = (v->y + 0x100) & 0xffffff00;       /* start from the next scanline */
68                 fy = y0 - v->y;                                         /* fractional part before the next scanline */
69                 fx = (fy * slope) >> 8;                         /* X adjust for the step to the next scanline */
70                 x = v->x + fx;                                          /* adjust X */
71                 y1 = vn->y & 0xffffff00;                        /* last scanline of the edge <= vn->y */
72
73                 line = y0 >> 8;
74                 if(line < top) top = line;
75                 if((y1 >> 8) > bot) bot = y1 >> 8;
76
77                 if(line > 0) tab += line;
78
79                 while(line <= (y1 >> 8) && line < fbheight) {
80                         if(line >= 0) {
81                                 int val = x < 0 ? 0 : x >> 8;
82                                 *tab++ = val < fbwidth ? val : fbwidth - 1;
83                         }
84                         x += slope;
85                         line++;
86                 }
87         }
88
89         fbptr = fb + top * fbwidth;
90         for(i=top; i<=bot; i++) {
91                 start = scantab[0][i];
92                 len = scantab[1][i] - start;
93
94                 if(len > 0) {
95                         memset(fbptr + start, col, len);
96                 }
97                 fbptr += fbwidth;
98         }
99 }
100
101
102 /* ----- line drawing and clipping ------ */
103 enum {
104         IN              = 0,
105         LEFT    = 1,
106         RIGHT   = 2,
107         TOP             = 4,
108         BOTTOM  = 8
109 };
110
111 static int outcode(int x, int y, int xmin, int ymin, int xmax, int ymax)
112 {
113         int code = 0;
114
115         if(x < xmin) {
116                 code |= LEFT;
117         } else if(x > xmax) {
118                 code |= RIGHT;
119         }
120         if(y < ymin) {
121                 code |= TOP;
122         } else if(y > ymax) {
123                 code |= BOTTOM;
124         }
125         return code;
126 }
127
128 #define FIXMUL(a, b)    (((a) * (b)) >> 8)
129 #define FIXDIV(a, b)    (((a) << 8) / (b))
130
131 #define LERP(a, b, t)   ((a) + FIXMUL((b) - (a), (t)))
132
133 int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, int ymax)
134 {
135         int oc_out;
136
137         int oc0 = outcode(*x0, *y0, xmin, ymin, xmax, ymax);
138         int oc1 = outcode(*x1, *y1, xmin, ymin, xmax, ymax);
139
140         long fx0, fy0, fx1, fy1, fxmin, fymin, fxmax, fymax;
141
142         if(!(oc0 | oc1)) return 1;      /* both points are inside */
143
144         fx0 = *x0 << 8;
145         fy0 = *y0 << 8;
146         fx1 = *x1 << 8;
147         fy1 = *y1 << 8;
148         fxmin = xmin << 8;
149         fymin = ymin << 8;
150         fxmax = xmax << 8;
151         fymax = ymax << 8;
152
153         for(;;) {
154                 long x, y, t;
155
156                 if(oc0 & oc1) return 0;         /* both have points with the same outbit, not visible */
157                 if(!(oc0 | oc1)) break;         /* both points are inside */
158
159                 oc_out = oc0 ? oc0 : oc1;
160
161                 if(oc_out & TOP) {
162                         t = FIXDIV(fymin - fy0, fy1 - fy0);
163                         x = LERP(fx0, fx1, t);
164                         y = fymin;
165                 } else if(oc_out & BOTTOM) {
166                         t = FIXDIV(fymax - fy0, fy1 - fy0);
167                         x = LERP(fx0, fx1, t);
168                         y = fymax;
169                 } else if(oc_out & LEFT) {
170                         t = FIXDIV(fxmin - fx0, fx1 - fx0);
171                         x = fxmin;
172                         y = LERP(fy0, fy1, t);
173                 } else {/*if(oc_out & RIGHT) {*/
174                         t = FIXDIV(fxmax - fx0, fx1 - fx0);
175                         x = fxmax;
176                         y = LERP(fy0, fy1, t);
177                 }
178
179                 if(oc_out == oc0) {
180                         fx0 = x;
181                         fy0 = y;
182                         oc0 = outcode(fx0 >> 8, fy0 >> 8, xmin, ymin, xmax, ymax);
183                 } else {
184                         fx1 = x;
185                         fy1 = y;
186                         oc1 = outcode(fx1 >> 8, fy1 >> 8, xmin, ymin, xmax, ymax);
187                 }
188         }
189
190         *x0 = fx0 >> 8;
191         *y0 = fy0 >> 8;
192         *x1 = fx1 >> 8;
193         *y1 = fy1 >> 8;
194         return 1;
195 }
196
197 void draw_line(int x0, int y0, int x1, int y1, unsigned short color)
198 {
199         int i, dx, dy, x_inc, y_inc, error;
200         unsigned char *fbptr = fb;
201
202         fbptr += y0 * fbwidth + x0;
203
204         dx = x1 - x0;
205         dy = y1 - y0;
206
207         if(dx >= 0) {
208                 x_inc = 1;
209         } else {
210                 x_inc = -1;
211                 dx = -dx;
212         }
213         if(dy >= 0) {
214                 y_inc = fbwidth;
215         } else {
216                 y_inc = -fbwidth;
217                 dy = -dy;
218         }
219
220         if(dx > dy) {
221                 error = dy * 2 - dx;
222                 for(i=0; i<=dx; i++) {
223                         *fbptr = color;
224                         if(error >= 0) {
225                                 error -= dx * 2;
226                                 fbptr += y_inc;
227                         }
228                         error += dy * 2;
229                         fbptr += x_inc;
230                 }
231         } else {
232                 error = dx * 2 - dy;
233                 for(i=0; i<=dy; i++) {
234                         *fbptr = color;
235                         if(error >= 0) {
236                                 error -= dy * 2;
237                                 fbptr += x_inc;
238                         }
239                         error += dx * 2;
240                         fbptr += y_inc;
241                 }
242         }
243 }
244