1 // vector.cpp, by iq_132.  aa mods by dink
2 #include "tiles_generic.h"
3 #include "math.h"
4 
5 #define TABLE_SIZE  0x10000 // excessive?
6 
7 struct vector_line {
8 	INT32 x;
9 	INT32 y;
10 	INT32 color;
11 	UINT8 intensity;
12 };
13 
14 static struct vector_line *vector_table;
15 struct vector_line *vector_ptr; // pointer
16 static INT32 vector_cnt;
17 static UINT32 *pBitmap = NULL;
18 static UINT32 *pPalette = NULL;
19 
20 static INT32 clip_xmin, clip_xmax; // clipping for the final blit
21 static INT32 clip_ymin, clip_ymax;
22 
23 static float vector_scaleX      = 1.00;
24 static float vector_scaleY      = 1.00;
25 static INT32 vector_scaleX_int  = 0;
26 static INT32 vector_scaleY_int  = 0;
27 static INT32 vector_offsetX     = 0;
28 static INT32 vector_offsetY     = 0;
29 static float vector_gamma_corr  = 1.2;
30 static float vector_intens      = 1.0;
31 static INT32 vector_antialias   = 1;
32 static INT32 vector_beam        = 0x0001f65e; // 16.16 beam width
33 
34 #define CLAMP8(x) do { if (x > 0xff) x = 0xff; if (x < 0) x = 0; } while (0)
35 
36 static UINT8 gammaLUT[256];
37 static UINT32 *cosineLUT;
38 
vector_set_clip(INT32 xmin,INT32 xmax,INT32 ymin,INT32 ymax)39 void vector_set_clip(INT32 xmin, INT32 xmax, INT32 ymin, INT32 ymax)
40 {
41 	clip_xmin = xmin;
42 	clip_xmax = xmax;
43 	clip_ymin = ymin;
44 	clip_ymax = ymax;
45 }
46 
vector_set_gamma(float gamma_corr)47 void vector_set_gamma(float gamma_corr)
48 {
49 	for (INT32 i = 0; i < 256; i++)	{
50 		INT32 gamma = pow((float)i / 255.0, 1.0 / vector_gamma_corr) * (float)((1 << 8) - 1) + 0.5;
51 		CLAMP8(gamma);
52 		gammaLUT[i] = gamma;
53 	}
54 }
55 
vector_set_offsets(INT32 x,INT32 y)56 void vector_set_offsets(INT32 x, INT32 y)
57 {
58 	vector_offsetX = x;
59 	vector_offsetY = y;
60 }
61 
vector_set_scale(INT32 x,INT32 y)62 void vector_set_scale(INT32 x, INT32 y)
63 {
64 	vector_scaleX_int = x;
65 	vector_scaleY_int = y;
66 
67 	if (x == 0 || x == -1)
68 		vector_scaleX = 1.00;
69 	else
70 		vector_scaleX = (float)nScreenWidth / x;
71 
72 	if (y == 0 || y == -1)
73 		vector_scaleY = 1.00;
74 	else
75 		vector_scaleY = (float)nScreenHeight / y;
76 }
77 
vector_rescale(INT32 x,INT32 y)78 void vector_rescale(INT32 x, INT32 y)
79 {
80 	if(BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL)
81 		BurnDrvSetVisibleSize(y, x);
82 	else
83 		BurnDrvSetVisibleSize(x, y);
84 	Reinitialise();
85 	GenericTilesExit();
86 	GenericTilesInit(); // create pTransDraw w/ new size
87 	BurnFree(pBitmap);
88 	pBitmap = (UINT32*)BurnMalloc(nScreenWidth * nScreenHeight * sizeof(INT32));
89 
90 	vector_set_clip(0, nScreenWidth, 0, nScreenHeight);
91 
92 	vector_set_scale(vector_scaleX_int, vector_scaleY_int);
93 
94 	// This is bit hacky, but thicker lines are more enjoyable at 1080p -barbudreadmon
95 	vector_intens = (y == 1080 ? 2.0 : 1.0);
96 }
97 
vector_add_point(INT32 x,INT32 y,INT32 color,INT32 intensity)98 void vector_add_point(INT32 x, INT32 y, INT32 color, INT32 intensity)
99 {
100 	if (vector_cnt + 1 > (TABLE_SIZE - 2)) return;
101 	vector_ptr->x = (vector_antialias == 0) ? (((x + 0x8000) >> 16) + vector_offsetX) : (x + (vector_offsetX << 16));
102 	vector_ptr->y = (vector_antialias == 0) ? (((y + 0x8000) >> 16) + vector_offsetY) : (y + (vector_offsetY << 16));
103 	vector_ptr->color = color;
104 
105 	intensity *= vector_intens; // intensity correction
106 	CLAMP8(intensity);
107 	vector_ptr->intensity = (vector_antialias == 0) ? gammaLUT[intensity] : intensity;
108 
109 	vector_cnt++;
110 	vector_ptr++;
111 	vector_ptr->color = -1; // mark it as the last one to save some cycles later...
112 }
113 
vector_draw_pixel(INT32 x,INT32 y,INT32 pixel)114 static inline void vector_draw_pixel(INT32 x, INT32 y, INT32 pixel)
115 {
116 	if (x >= 0 && x < nScreenWidth && y >= 0 && y < nScreenHeight)
117 	{
118 		INT32 coords = y * nScreenWidth + x;
119 		UINT32 d = pBitmap[coords];
120 		pixel = pPalette[pixel];
121 
122 		if (d) { // if something is already there, mix it.
123 			INT32 r = ((d >> 16) & 0xff) + ((pixel >> 16) & 0xff);
124 			INT32 g = ((d >>  8) & 0xff) + ((pixel >>  8) & 0xff);
125 			INT32 b = (d & 0xff) + (pixel & 0xff);
126 			CLAMP8(r); CLAMP8(g); CLAMP8(b);
127 
128 			pBitmap[coords] = (r << 16) | (g << 8) | b;
129 		}
130 		else
131 		{
132 			pBitmap[y * nScreenWidth + x] = pixel;
133 		}
134 	}
135 }
136 
divop(INT32 dividend,INT32 divisor)137 static inline INT32 divop(INT32 dividend, INT32 divisor)
138 {
139 	if (!(divisor >>= 12)) return (1 << 16); // avoid division by zero
140 
141 	dividend = (dividend << 4) / divisor;
142 
143 	if (dividend > (1 << 16)) return (1 << 16); // safety net
144 	else if (dividend < -(1 << 16)) return -(1 << 16);
145 
146 	return dividend;
147 }
148 
vec_mult(INT32 parm1,INT32 parm2)149 static inline INT32 vec_mult(INT32 parm1, INT32 parm2) // stolen from mame, we need to re-write this.
150 {
151 	INT32 temp, result;
152 
153 	temp     = abs(parm1);
154 	result   = (temp&0x0000ffff) * (parm2&0x0000ffff);
155 	result >>= 16;
156 	result  += (temp&0x0000ffff) * (parm2>>16       );
157 	result  += (temp>>16       ) * (parm2&0x0000ffff);
158 	result >>= 16;
159 	result  += (temp>>16       ) * (parm2>>16       );
160 
161 	if( parm1 < 0 )
162 		return(-result);
163 	else
164 		return( result);
165 }
166 
lineSimple(INT32 x0,INT32 y0,INT32 x1,INT32 y1,INT32 color,INT32 intensity)167 static void lineSimple(INT32 x0, INT32 y0, INT32 x1, INT32 y1, INT32 color, INT32 intensity)
168 {
169 	color = color * 256 + intensity;
170 	UINT32 p = pPalette[color];
171 	if (p == 0) return; // safe to assume we can't draw black??
172 	INT32 straight = 0;
173 
174 	if (x0 == x1 || y0 == y1) straight = 1;
175 
176 	if (vector_antialias == 0) {
177 		// http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm
178 
179 		INT32 dx = abs(x1 - x0);
180 		INT32 dy = abs(y1 - y0);
181 		INT32 sx = x0 < x1 ? 1 : -1;
182 		INT32 sy = y0 < y1 ? 1 : -1;
183 		INT32 err = (dx>dy ? dx : -dy)/2, e2;
184 
185 		while (1)
186 		{
187 			vector_draw_pixel(x0, y0, color);
188 
189 			if (x0 == x1 && y0 == y1) break;
190 
191 			e2 = err;
192 
193 			if (e2 >-dx) { err -= dy; x0 += sx; }
194 			if (e2 < dy) { err += dx; y0 += sy; }
195 		}
196 	} else {
197 		// anti-aliased
198 
199 		INT32 dx = abs(x1 - x0);
200 		INT32 dy = abs(y1 - y0);
201 		INT32 width = vector_beam;
202 		INT32 sx, sy, aa, yy, xx;
203 		INT32 der = 0xff - intensity;
204 		der = ((double)der / 1.2);
205 
206 		if (dx >= dy) {
207 			sx = x0 <= x1 ? 1 : -1;
208 			sy = divop(y1 - y0, dx);
209 			if (sy < 0)
210 				dy--;
211 			x0 >>= 16;
212 			xx = x1 >> 16;
213 
214 			width = vec_mult(vector_beam << 4, cosineLUT[abs(sy) >> 5]);
215 			y0 -= width >> 1;
216 
217 			while (1) {
218 				dx = width;
219 				dy = y0 >> 16;
220 				aa = (0xff - (0xff & (y0 >> 8))) - der;
221 				CLAMP8(aa);
222 				vector_draw_pixel(x0, dy++, (color & 0xff00) + gammaLUT[aa]);
223 				dx -= 0x10000 - (0xffff & y0);
224 				aa = ((dx >> 8) & 0xff) - der;
225 				dx >>= 16;
226 				while (dx--)
227 					vector_draw_pixel(x0, dy++, (color & 0xff00) + gammaLUT[color & 0xff]);
228 				CLAMP8(aa);
229 				vector_draw_pixel(x0, dy, (color & 0xff00) + gammaLUT[aa]);
230 				if (x0 == xx) break;
231 				x0 += sx;
232 				y0 += sy;
233 			}
234 		} else {
235 			sy = y0 <= y1 ? 1 : -1;
236 			sx = divop(x1 - x0, dy);
237 			if (sx < 0)
238 				dx--;
239 			y0 >>= 16;
240 			yy = y1 >> 16;
241 
242 			width = vec_mult(vector_beam << 4, cosineLUT[abs(sx) >> 5]);
243 			x0 -= width >> 1;
244 
245 			while (1) {
246 				dy = width;
247 				dx = x0 >> 16;
248 				aa = (0xff - (0xff & (x0 >> 8))) - der;
249 				CLAMP8(aa);
250 				vector_draw_pixel(dx++, y0, (color & 0xff00) + gammaLUT[aa]);
251 				dy -= 0x10000 - (0xffff & x0);
252 				aa = ((dy >> 8) & 0xff) - der;
253 				dy >>= 16;
254 				while (dy--)
255 					vector_draw_pixel(dx++, y0, (color & 0xff00) + gammaLUT[color & 0xff]);
256 				CLAMP8(aa);
257 				vector_draw_pixel(dx, y0, (color & 0xff00) + gammaLUT[aa]);
258 				if (y0 == yy) break;
259 				y0 += sy;
260 				x0 += sx;
261 			}
262 		}
263 	}
264 }
265 
draw_vector(UINT32 * palette)266 void draw_vector(UINT32 *palette)
267 {
268 	struct vector_line *ptr = &vector_table[0];
269 
270 	INT32 prev_x = 0, prev_y = 0;
271 
272 	memset (pBitmap, 0, nScreenWidth * nScreenHeight * sizeof(INT32));
273 	pBurnDrvPalette = pPalette = palette;
274 
275 	for (INT32 i = 0; i < vector_cnt && i < TABLE_SIZE; i++, ptr++)
276 	{
277 		if (ptr->color == -1) break;
278 
279 		INT32 curr_y = ptr->y * vector_scaleY;
280 		INT32 curr_x = ptr->x * vector_scaleX;
281 
282 		if (ptr->intensity != 0) { // intensity 0 means turn off the beam...
283 			lineSimple(curr_x, curr_y, prev_x, prev_y, ptr->color, ptr->intensity);
284 		}
285 
286 		prev_x = curr_x;
287 		prev_y = curr_y;
288 	}
289 
290 	// copy to the screen, only draw pixels that aren't black
291 	// should be safe for any bit depth with putpix
292 	{
293 		memset (pBurnDraw, 0, nScreenWidth * nScreenHeight * nBurnBpp);
294 
295 		for (INT32 y = 0; y < nScreenHeight; y++)
296 		{
297 			if (y < clip_ymin || y > clip_ymax) continue;
298 			UINT32 idx = (y * nScreenWidth);
299 
300 			for (INT32 x = 0; x < nScreenWidth; x++)
301 			{
302 				if (x < clip_xmin || x > clip_xmax) continue;
303 
304 				UINT32 p = pBitmap[idx + x];
305 
306 				if (p) {
307 					PutPix(pBurnDraw + (idx + x) * nBurnBpp, BurnHighCol((p >> 16) & 0xff, (p >> 8) & 0xff, p & 0xff, 0));
308 				}
309 			}
310 		}
311 	}
312 }
313 
vector_reset()314 void vector_reset()
315 {
316 	vector_cnt = 0;
317 	vector_ptr = &vector_table[0];
318 	vector_ptr->color = -1;
319 }
320 
vector_init()321 void vector_init()
322 {
323 	GenericTilesInit();
324 
325 	vector_set_clip(0, nScreenWidth, 0, nScreenHeight);
326 
327 	pBitmap = (UINT32*)BurnMalloc(nScreenWidth * nScreenHeight * sizeof(INT32));
328 
329 	vector_table = (struct vector_line*)BurnMalloc(TABLE_SIZE * sizeof(vector_line));
330 
331 	memset (vector_table, 0, TABLE_SIZE * sizeof(vector_line));
332 
333 	vector_set_scale(-1, -1); // default 1x
334 	vector_set_offsets(0, 0);
335 	vector_set_gamma(vector_gamma_corr);
336 
337 	cosineLUT = (UINT32*)BurnMalloc(2049 * sizeof(UINT32));
338 	for (INT32 i = 0; i < 2049; i++) {
339 		cosineLUT[i] = (INT32)((double)(1.0 / cos(atan((double)i / 2048.0))) * 0x10000000 + 0.5);
340 	}
341 
342 	vector_reset();
343 }
344 
vector_exit()345 void vector_exit()
346 {
347 	GenericTilesExit();
348 
349 	if (pBitmap) {
350 		BurnFree (pBitmap);
351 	}
352 
353 	pPalette = NULL;
354 
355 	BurnFree (vector_table);
356 	vector_ptr = NULL;
357 
358 	BurnFree (cosineLUT);
359 }
360 
vector_scan(INT32 nAction)361 INT32 vector_scan(INT32 nAction)
362 {
363 	struct BurnArea ba;
364 
365 	if (nAction & ACB_VOLATILE) {
366 		memset(&ba, 0, sizeof(ba));
367 
368 		ba.Data   = (UINT8*)vector_table;
369 		ba.nLen   = TABLE_SIZE * sizeof(vector_line);
370 		ba.szName = "Vector Table";
371 		BurnAcb(&ba);
372 
373 		SCAN_VAR(vector_cnt);
374 	}
375 
376 	if (nAction & ACB_WRITE) {
377 		vector_ptr = &vector_table[vector_cnt];
378 	}
379 
380 	return 0;
381 }
382