1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
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 <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  *  $Source: n:/project/lib/src/2d/RCS/fl8clin.h $
21  *  $Revision: 1.1 $
22  *  $Author: lmfeeney $
23  *  $Date: 1994/06/11 00:49:17 $
24  */
25 
26 /* This file is an UNCOMPILABLE code fragment */
27 
28 /* Draw a gouraud-shaded line as specified by endpoint rgb value...
29    weird precision bugs abound due to precision errors
30 
31    5/94 These have been (completely?) corrected.  See test/clintest.c
32 
33    See also note.txt for argument for correctness and precision safety
34    of current algorithm.
35 */
36 
37 /* NB: directionality policy -- reversible
38    Lines are drawn in order of increasing x or increasing y,
39    for lines that have greater x or y extent, repsectively.
40    This is done for all lines, including horiz., vert., and
41    45' lines (increasing x).
42 */
43 
44 /* NB: endpoint policy -- inclusive
45 
46    The left and top endpoints are inclusive, i.e. 'trunc'-ed.  The
47    right and bottom endpoints exclude the ceiling.  This is
48    calculated by subtracting epsilon (i.e. 1/65536) from its
49    fixed-point representation, then trunc'ing.
50 
51    This makes sense if you note that open interval on right
52         < ceil (x)
53    is the same as
54         <= trunc (x - e)
55 
56   This means that it might not be necessary to go through the ugliness
57   of swapping endpoints, but I'm not convinced that there aren't
58   precision problems involved.  Since wire-poly's overdraw, it's
59   really necessary to ensure that the same points are always drawn.
60 */
61 
62 fix x0, y0, x1, y1;
63 fix dx, dy; /* deltas in x and y */
64 fix t;      /* tmp */
65 
66 uchar r0, g0, b0, r1, g1, b1; /* rgb values of endpt colors */
67 fix r, g, b;                  /* current intensities */
68 fix dr, dg, db;               /* deltas for each of rgb */
69 long i;                       /* color index */
70 
71 uchar *p; /* ptr into canvas */
72 
73 x0 = v0->x;
74 y0 = v0->y;
75 x1 = v1->x;
76 y1 = v1->y;
77 
78 r0 = (uchar)(v0->u);
79 g0 = (uchar)(v0->v);
80 b0 = (uchar)(v0->w);
81 r1 = (uchar)(v1->u);
82 g1 = (uchar)(v1->v);
83 b1 = (uchar)(v1->w);
84 
85 /* set endpoints
86    note that this cannot go negative or change octant, since the ==
87    case is excluded */
88 
89 if (x0 < x1) {
90     x1 -= 1; /* e.g. - epsilon */
91 } else if (x0 > x1) {
92     x0 -= 1;
93 }
94 
95 if (y0 < y1) {
96     y1 -= 1;
97 } else if (y0 > y1) {
98     y0 -= 1;
99 }
100 
101 dx = fix_trunc(x1) - fix_trunc(x0); /* x extent in pixels, (macro is flakey) */
102 dx = fix_abs(dx);
103 dy = fix_trunc(y1) - fix_trunc(y0); /* y extent in pixels */
104 dy = fix_abs(dy);
105 
106 if (dx == 0 && dy == 0)
107     return;
108 
109 /* three cases: absolute value dx < = > dy
110 
111    along the longer dimension, the fixpoint x0 (or y0) is treated
112    as an int
113 
114    the points are swapped if needed and the rgb initial and deltas
115    are calculated accordingly
116 
117    there are two or three sub-cases - a horizontal or vertical line,
118    and the dx or dy being added or subtracted.  dx and dy
119    are kept as absolute values and +/- is managed in
120    two separate inner loops if it is a y change, since you need to
121    manage the canvas pointer
122 
123    if y is being changed by 'dy' and x is being incremented, do a
124    FunkyBitCheck (TM) to see whether the integer part of y has changed
125    and if it has, resetting the the canvas pointer to the next row
126 
127    if x is being changed by 'dx' and y is being incremented, just
128    add or subtract row to increment y in the canvas
129 
130    the endpoints are walked inclusively in all cases, see above
131 
132    45' degree lines are explicitly special cased -- because it
133    all runs as integers, but it's probably not frequent enough
134    to justify the check
135 
136  */
137 
138 if (dx > dy) {
139 
140     x0 = fix_int(x0);
141     x1 = fix_int(x1);
142 
143     if (x0 < x1) {
144         r = fix_make(r0, 0);
145         g = fix_make(g0, 0);
146         b = fix_make(b0, 0);
147         dr = fix_div(fix_make_nof(r1 - r0), dx);
148         dg = fix_div(fix_make_nof(g1 - g0), dx);
149         db = fix_div(fix_make_nof(b1 - b0), dx);
150 
151         p = grd_bm.bits + grd_bm.row * (fix_int(y0)); /* set canvas ptr */
152 
153     } else {
154         t = x0;
155         x0 = x1;
156         x1 = t;
157         t = y0;
158         y0 = y1;
159         y1 = t;
160 
161         r = fix_make(r1, 0);
162         g = fix_make(g1, 0);
163         b = fix_make(b1, 0);
164         dr = fix_div(fix_make_nof(r0 - r1), dx);
165         dg = fix_div(fix_make_nof(g0 - g1), dx);
166         db = fix_div(fix_make_nof(b0 - b1), dx);
167 
168         p = grd_bm.bits + grd_bm.row * (fix_int(y0));
169     }
170 
171     if ((fix_int(y0)) == (fix_int(y1))) {
172         while (x0 <= x1) {
173             i = macro_get_ipal(r, g, b);
174             macro_plot_rgb(x0, p, i);
175             x0++;
176             r += dr;
177             g += dg;
178             b += db;
179         }
180     } else if (y0 < y1) {
181         dy = fix_div((y1 - y0), dx);
182         while (x0 <= x1) {
183             i = macro_get_ipal(r, g, b);
184             macro_plot_rgb(x0, p, i);
185             x0++;
186             y0 += dy;
187             p += (grd_bm.row & (-(fix_frac(y0) < dy)));
188             r += dr;
189             g += dg;
190             b += db;
191         }
192     } else {
193         dy = fix_div((y0 - y1), dx);
194         while (x0 <= x1) {
195             i = macro_get_ipal(r, g, b);
196             macro_plot_rgb(x0, p, i);
197             x0++;
198             p -= (grd_bm.row & (-(fix_frac(y0) < dy)));
199             y0 -= dy;
200             r += dr;
201             g += dg;
202             b += db;
203         }
204     }
205 }
206 
207 else if (dy > dx) {
208 
209     y0 = fix_int(y0);
210     y1 = fix_int(y1);
211 
212     if (y0 < y1) {
213         r = fix_make(r0, 0);
214         g = fix_make(g0, 0);
215         b = fix_make(b0, 0);
216         dr = fix_div(fix_make_nof(r1 - r0), dy);
217         dg = fix_div(fix_make_nof(g1 - g0), dy);
218         db = fix_div(fix_make_nof(b1 - b0), dy);
219 
220         p = grd_bm.bits + grd_bm.row * y0;
221 
222     } else {
223         t = x0;
224         x0 = x1;
225         x1 = t;
226         t = y0;
227         y0 = y1;
228         y1 = t;
229 
230         r = fix_make(r1, 0);
231         g = fix_make(g1, 0);
232         b = fix_make(b1, 0);
233         dr = fix_div(fix_make_nof(r0 - r1), dy);
234         dg = fix_div(fix_make_nof(g0 - g1), dy);
235         db = fix_div(fix_make_nof(b0 - b1), dy);
236 
237         p = grd_bm.bits + grd_bm.row * y0;
238     }
239 
240     if ((fix_int(x0)) == (fix_int(x1))) {
241         x0 = fix_int(x0);
242         while (y0 <= y1) {
243             i = macro_get_ipal(r, g, b);
244             macro_plot_rgb(x0, p, i);
245             y0++;
246             p += grd_bm.row;
247             r += dr;
248             g += dg;
249             b += db;
250         }
251     } else {
252         dx = fix_div((x1 - x0), dy);
253         while (y0 <= y1) {
254             i = macro_get_ipal(r, g, b);
255             macro_plot_rgb(fix_fint(x0), p, i);
256             x0 += dx;
257             y0++;
258             p += grd_bm.row;
259             r += dr;
260             g += dg;
261             b += db;
262         }
263     }
264 } else { /* dy == dx, walk the x axis, all integers */
265 
266     x0 = fix_int(x0);
267     x1 = fix_int(x1);
268     y0 = fix_int(y0);
269     y1 = fix_int(y1);
270 
271     if (x0 < x1) {
272         r = fix_make(r0, 0);
273         g = fix_make(g0, 0);
274         b = fix_make(b0, 0);
275         dr = fix_div(fix_make_nof(r1 - r0), dx);
276         dg = fix_div(fix_make_nof(g1 - g0), dx);
277         db = fix_div(fix_make_nof(b1 - b0), dx);
278 
279         p = grd_bm.bits + grd_bm.row * y0; /* set canvas ptr */
280 
281     } else {
282         t = x0;
283         x0 = x1;
284         x1 = t;
285         t = y0;
286         y0 = y1;
287         y1 = t;
288 
289         r = fix_make(r1, 0);
290         g = fix_make(g1, 0);
291         b = fix_make(b1, 0);
292         dr = fix_div(fix_make_nof(r0 - r1), dx);
293         dg = fix_div(fix_make_nof(g0 - g1), dx);
294         db = fix_div(fix_make_nof(b0 - b1), dx);
295 
296         p = grd_bm.bits + grd_bm.row * y0;
297     }
298 
299     if (y0 < y1) {
300         while (y0 <= y1) {
301             i = macro_get_ipal(r, g, b);
302             macro_plot_rgb(x0, p, i);
303             x0++;
304             y0++;
305             p += grd_bm.row;
306             r += dr;
307             g += dg;
308             b += db;
309         }
310     } else {
311         while (y0 >= y1) {
312             i = macro_get_ipal(r, g, b);
313             macro_plot_rgb(x0, p, i);
314             x0++;
315             y0--;
316             p -= grd_bm.row;
317             r += dr;
318             g += dg;
319             b += db;
320         }
321     }
322 }
323