1 /*
2  * OpenBOR - http://www.chronocrash.com
3  * -----------------------------------------------------------------------
4  * All rights reserved, see LICENSE in OpenBOR root for details.
5  *
6  * Copyright (c) 2004 - 2014 OpenBOR Team
7  */
8 
9 // Primitive drawing functions. Should really be done in ASM...
10 // Last update: 24-jun-2002
11 
12 #include "types.h"
13 #include "draw.h"
14 
15 #ifndef		NULL
16 #define		NULL	 ((void*)0)
17 #endif
18 
19 
20 #define		abso(x)		(x<0?-x:x)
21 
22 
23 // Not a particularly fast line function, but it works, and clips!
line(int sx,int sy,int ex,int ey,int colour,s_screen * screen,int alpha)24 void line(int sx, int sy, int ex, int ey, int colour, s_screen *screen, int alpha)
25 {
26 
27     int diffx, diffy;
28     int absdiffx, absdiffy;
29     int xdir, ydir;
30     int thres;
31     int d;
32     unsigned char *lut;
33 
34     // Some off-screen lines may slip through this test!
35     if(sx < 0 && ex < 0)
36     {
37         return;
38     }
39     if(sy < 0 && ey < 0)
40     {
41         return;
42     }
43     if(sx >= screen->width && ex >= screen->width)
44     {
45         return;
46     }
47     if(sy >= screen->height && ey >= screen->height)
48     {
49         return;
50     }
51 
52 
53     // Check clipping and calculate new coords if necessary
54 
55     diffx = ex - sx;
56     diffy = ey - sy;
57 
58     if(sx < 0)
59     {
60         sy -= (sx * diffy / diffx);
61         sx = 0;
62     }
63     if(sy < 0)
64     {
65         sx -= (sy * diffx / diffy);
66         sy = 0;
67     }
68     if(sx >= screen->width)
69     {
70         sy -= ((sx - screen->width) * diffy / diffx);
71         sx = screen->width - 1;
72     }
73     if(sy >= screen->height)
74     {
75         sx -= ((sy - screen->height) * diffx / diffy);
76         sy = screen->height - 1;
77     }
78 
79     if(ex < 0)
80     {
81         ey -= (ex * diffy / diffx);
82         ex = 0;
83     }
84     if(ey < 0)
85     {
86         ex -= (ey * diffx / diffy);
87         ey = 0;
88     }
89     if(ex >= screen->width)
90     {
91         ey -= ((ex - screen->width) * diffy / diffx);
92         ex = screen->width - 1;
93     }
94     if(ey >= screen->height)
95     {
96         ex -= ((ey - screen->height) * diffx / diffy);
97         ey = screen->height - 1;
98     }
99 
100 
101     // Second test: the lines that passed test 1 won't pass this time!
102     if(sx < 0 || ex < 0)
103     {
104         return;
105     }
106     if(sy < 0 || ey < 0)
107     {
108         return;
109     }
110     if(sx >= screen->width || ex >= screen->width)
111     {
112         return;
113     }
114     if(sy >= screen->height || ey >= screen->height)
115     {
116         return;
117     }
118 
119 
120     // Recalculate directions
121     diffx = ex - sx;
122     diffy = ey - sy;
123 
124     absdiffx = abso(diffx);
125     absdiffy = abso(diffy);
126 
127     sy *= screen->width;
128     ey *= screen->width;
129 
130     lut = alpha > 0 ? blendtables[alpha - 1] : NULL;
131 
132     if(lut)
133     {
134         lut += (colour << 8);
135     }
136 
137     if(absdiffx > absdiffy)
138     {
139         // Draw a flat line
140         thres = absdiffx >> 1;
141         xdir = 1;
142         if(diffx < 0)
143         {
144             xdir = -xdir;
145         }
146         ydir = screen->width;
147         if(diffy < 0)
148         {
149             ydir = -ydir;
150         }
151         while(sx != ex)
152         {
153             d = sx + sy;
154             screen->data[d] = (lut && screen->data[d]) ? (lut[screen->data[d]]) : colour;
155             sx += xdir;
156             if((thres -= absdiffy) <= 0)
157             {
158                 sy += ydir;
159                 thres += absdiffx;
160             }
161         }
162         d = ex + ey;
163         screen->data[d] = (lut && screen->data[d]) ? (lut[screen->data[d]]) : colour;
164         return;
165     }
166 
167     // Draw a high line
168     thres = absdiffy >> 1;
169     xdir = 1;
170     if(diffx < 0)
171     {
172         xdir = -1;
173     }
174     ydir = screen->width;
175     if(diffy < 0)
176     {
177         ydir = -ydir;
178     }
179     while(sy != ey)
180     {
181         d = sx + sy;
182         screen->data[d] = (lut && screen->data[d]) ? (lut[screen->data[d]]) : colour;;
183         sy += ydir;
184         if((thres -= absdiffx) <= 0)
185         {
186             sx += xdir;
187             thres += absdiffy;
188         }
189     }
190     d = ex + ey;
191     screen->data[d] = (lut && screen->data[d]) ? (lut[screen->data[d]]) : colour;
192 }
193 
194 
195 
196 
197 
drawbox(int x,int y,int width,int height,int colour,s_screen * screen,int alpha)198 void drawbox(int x, int y, int width, int height, int colour, s_screen *screen, int alpha)
199 {
200     unsigned char *cp;
201     unsigned char *lut;
202 
203     if(width <= 0)
204     {
205         return;
206     }
207     if(height <= 0)
208     {
209         return;
210     }
211     if(screen == NULL)
212     {
213         return;
214     }
215 
216     if(x < 0)
217     {
218         if((width += x) <= 0)
219         {
220             return;
221         }
222         x = 0;
223     }
224     else if(x >= screen->width)
225     {
226         return;
227     }
228     if(y < 0)
229     {
230         if((height += y) <= 0)
231         {
232             return;
233         }
234         y = 0;
235     }
236     else if(y >= screen->height)
237     {
238         return;
239     }
240     if(x + width > screen->width)
241     {
242         width = screen->width - x;
243     }
244     if(y + height > screen->height)
245     {
246         height = screen->height - y;
247     }
248 
249     cp = (unsigned char *)screen->data + y * screen->width + x;
250     lut = alpha > 0 ? blendtables[alpha - 1] : NULL;
251     if(lut)
252     {
253         lut += (colour << 8);
254     }
255     while(--height >= 0)
256     {
257         for(x = 0; x < width; x++)
258         {
259             *cp = (lut && *cp) ? (lut[((int)(*cp)) & 0xFF]) : colour;
260             ++cp;
261         }
262         cp += screen->width - width;
263     }
264 }
265 
266 
267 
268 // Putpixel used by circle function
_putpixel(int x,int y,int colour,s_screen * screen,int alpha)269 void _putpixel(int x, int y, int colour, s_screen *screen, int alpha)
270 {
271     int pixind;
272     unsigned char *lut;
273     if((unsigned)x > screen->width || (unsigned)y > screen->height)
274     {
275         return;
276     }
277     pixind = x + y * screen->width;
278     lut = alpha > 0 ? blendtables[alpha - 1] : NULL;
279     if(lut)
280     {
281         lut += (colour << 8);
282     }
283     screen->data[pixind] = (lut && screen->data[pixind]) ? (lut[(int)(screen->data[pixind]) & 0xFF]) : colour;
284 }
285 
286 
287 
288 
289 // Code to draw a circle.
290 // I ripped this, not sure how it works...
291 // It seems it devides the circle into 8 parts, which are drawn
292 // simultaneously.
293 // Not much optimization, though, since every pixel is clipped
294 // separately.
295 
circle(int x,int y,int rad,int col,s_screen * screen,int alpha)296 void circle(int x, int y, int rad, int col, s_screen *screen, int alpha)
297 {
298     int cx = 0;				// 'Circle X'
299     int cy = rad;				// 'Circle Y'
300     int df = 1 - rad;
301     int d_e = 3;
302     int d_se = -2 * rad + 5;
303 
304     do
305     {
306         _putpixel(x + cx, y + cy, col, screen, alpha);
307         if(cx)
308         {
309             _putpixel(x - cx, y + cy, col, screen, alpha);
310         }
311         if(cy)
312         {
313             _putpixel(x + cx, y - cy, col, screen, alpha);
314         }
315         if(cx && cy)
316         {
317             _putpixel(x - cx, y - cy, col, screen, alpha);
318         }
319 
320         if(cx != cy)
321         {
322             _putpixel(x + cy, y + cx, col, screen, alpha);
323             if(cx)
324             {
325                 _putpixel(x + cy, y - cx, col, screen, alpha);
326             }
327             if(cy)
328             {
329                 _putpixel(x - cy, y + cx, col, screen, alpha);
330             }
331             if(cx && cy)
332             {
333                 _putpixel(x - cy, y - cx, col, screen, alpha);
334             }
335         }
336 
337         if(df < 0)
338         {
339             df   += d_e;
340             d_e  += 2;
341             d_se += 2;
342         }
343         else
344         {
345             df   += d_se;
346             d_e  += 2;
347             d_se += 4;
348             --cy;
349         }
350         ++cx;
351     }
352     while(cx <= cy);
353 }
354 
355 
356 
draw_init(s_drawmethod * drawmethod)357 static int draw_init( s_drawmethod *drawmethod)
358 {
359     int alpha = 0;
360     drawmethod_global_init(drawmethod);
361 
362     if (drawmethod && drawmethod->flag)
363     {
364         alpha = drawmethod->alpha;
365     }
366 
367     return alpha;
368 }
369 
370 //======================== root methods ==================================
371 
putbox(int x,int y,int width,int height,int colour,s_screen * screen,s_drawmethod * drawmethod)372 void putbox(int x, int y, int width, int height, int colour, s_screen *screen, s_drawmethod *drawmethod)
373 {
374     int alpha = draw_init(drawmethod);
375 
376     switch(screen->pixelformat)
377     {
378     case PIXEL_8:
379         drawbox(x, y, width, height, colour, screen, alpha);
380         break;
381     case PIXEL_16:
382         drawbox16(x, y, width, height, colour, screen, alpha);
383         break;
384     case PIXEL_32:
385         drawbox32(x, y, width, height, colour, screen, alpha);
386         break;
387     }
388 }
389 
putline(int sx,int sy,int ex,int ey,int colour,s_screen * screen,s_drawmethod * drawmethod)390 void putline(int sx, int sy, int ex, int ey, int colour, s_screen *screen, s_drawmethod *drawmethod)
391 {
392     int alpha = draw_init(drawmethod);
393 
394     switch(screen->pixelformat)
395     {
396     case PIXEL_8:
397         line(sx, sy, ex, ey, colour, screen, alpha);
398         break;
399     case PIXEL_16:
400         line16(sx, sy, ex, ey, colour, screen, alpha);
401         break;
402     case PIXEL_32:
403         line32(sx, sy, ex, ey, colour, screen, alpha);
404         break;
405     }
406 }
407 
putpixel(unsigned x,unsigned y,int colour,s_screen * screen,s_drawmethod * drawmethod)408 void putpixel(unsigned x, unsigned y, int colour, s_screen *screen, s_drawmethod *drawmethod)
409 {
410     int alpha = draw_init(drawmethod);
411     switch(screen->pixelformat)
412     {
413     case PIXEL_8:
414         _putpixel(x, y, colour, screen, alpha);
415         break;
416     case PIXEL_16:
417         _putpixel16(x, y, colour, screen, alpha);
418         break;
419     case PIXEL_32:
420         _putpixel32(x, y, colour, screen, alpha);
421         break;
422     }
423 }
424 
425 
426