1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, graphics.draw module
5  */
6 
7 module PixelPerfectEngine.graphics.draw;
8 
9 import std.stdio;
10 import std.math;
11 import std.conv;
12 
13 import PixelPerfectEngine.graphics.bitmap;
14 import CPUblit.composing;
15 import CPUblit.draw;
16 public import PixelPerfectEngine.graphics.fontsets;
17 public import PixelPerfectEngine.graphics.common;
18 //import system.etc;
19 /**
20  * Draws into a 16bit bitmap.
21  * TODO: Make it usable on 8 and 32bit bitmaps too.
22  */
23 public class BitmapDrawer{
24 	public Bitmap16Bit output;
25 	public ushort brushTransparency;
26 	static const ushort[8] transparencytester8 = [0,0,0,0,0,0,0,0];
27 	static const ushort[4] transparencytester4 = [0,0,0,0];
28 	///Creates the object alongside its output.
29 	public this(int x, int y){
30 		output = new Bitmap16Bit(x, y);
31 		
32 	}
33 	///Draws a single line. 
34 	public void drawLine(int xa, int xb, int ya, int yb, ushort color){
35 		if(xb < xa){
36 			int k = xa;
37 			xa = xb;
38 			xb = k;
39 		}
40 		if(yb < ya){
41 			int k = ya;
42 			ya = yb;
43 			yb = k;
44 		}
45 		int dx = xb - xa;
46 		int dy = yb - ya;
47 		if(!dx || !dy){
48 			if(!dy){
49 				for(int x = xa; x <= xb; x++){
50 					output.writePixel(x,ya,color);
51 				}
52 			}else{
53 				for(int y = ya; y <= yb; y++){
54 					output.writePixel(xa,y,color);
55 				}
56 			}
57 		}else if(dx>=dy){
58 			int D = 2*dy - dx;
59 			int y = ya;
60 			for(int x = xa; x <= xb; x++){
61 				output.writePixel(x,y,color);
62 				if(D > 0){
63 					y += 1;
64 					D -= 2*dx;
65 				}
66 				D += 2*dx;
67 			}
68 		}else{
69 			int D = 2*dx - dy;
70 			int x = xa;
71 			for(int y = ya; y <= yb; y++){
72 				output.writePixel(x,y,color);
73 				if(D > 0){
74 					x += 1;
75 					D -= 2*dy;
76 				}
77 				D += 2*dy;
78 			}
79 		}
80 	}
81 	///Draws a line using a brush.
82 	public void drawLine(int xa, int xb, int ya, int yb, Bitmap16Bit brush){
83 		if(xa == xb){
84 			
85 			if(ya < yb){
86 				for(int j ; j < (yb - ya) ; j++){
87 					insertBitmap(xa, ya + j, brush);
88 				}
89 			}else{
90 				for(int j ; j > (yb - ya) ; j--){
91 					insertBitmap(xa, ya + j, brush);
92 				}
93 			}
94 			xa++;
95 			xb++;
96 			
97 		}else if(ya == yb){
98 			
99 			if(xa > xb){
100 				for(int j ; j < (xa - xb) ; j++){
101 					insertBitmap(xa + j, ya, brush);
102 				}
103 			}else{
104 				for(int j ; j > (xa - xb) ; j--){
105 					insertBitmap(xa + j, ya, brush);
106 				}
107 			}
108 			ya++;
109 			yb++;
110 			
111 		}else{
112 			if(xa < xb){
113 				if(ya < yb){
114 					int xy = to!int(sqrt(to!double((xb - xa) * (xb - xa)) + ((yb - ya) * (yb - ya))));
115 					
116 					for(int j ; j < xb - xa ; j++){
117 						int y = to!int(sqrt(to!double(xy * xy) - ((xa + j)*(xa + j))));
118 						insertBitmap(xa + j, ya + y, brush);
119 					}
120 					
121 				}else{
122 					int xy = to!int(sqrt(to!double((xb - xa) * (xb - xa)) + ((ya - yb) * (ya - yb))));
123 					
124 					for(int j ; j < xb - xa ; j++){
125 						int y = to!int(sqrt(to!double(xy * xy) - ((xa + j)*(xa + j))));
126 						insertBitmap(xa + j, ya - y, brush);
127 					}
128 					
129 				}
130 			}else{
131 				if(ya < yb){
132 					int xy = to!int(sqrt(to!double((xa - xb) * (xa - xb)) + ((yb - ya) * (yb - ya))));
133 					
134 					for(int j ; j > xb - xa ; j--){
135 						int y = to!int(sqrt(to!double(xy * xy) - ((xa + j)*(xa + j))));
136 						insertBitmap(xa + j, ya + y, brush);
137 					}
138 					
139 				}else{
140 					int xy = to!int(sqrt(to!double((xa - xb) * (xa - xb)) + ((ya - yb) * (ya - yb))));
141 					
142 					for(int j ; j > xb - xa ; j--){
143 						int y = to!int(sqrt(to!double(xy * xy) - ((xa + j)*(xa + j))));
144 						insertBitmap(xa + j, ya - y, brush);
145 					}
146 					
147 				}
148 			}
149 		}
150 	}
151 	///Inserts a bitmap using blitter.
152 	public void insertBitmap(int x, int y, Bitmap16Bit bitmap){
153 		ushort* psrc = bitmap.getPtr, pdest = output.getPtr;
154 		pdest += x + output.width * y;
155 		int length = bitmap.width;
156 		for(int iy ; iy < bitmap.height ; iy++){
157 			blitter16bit(psrc,pdest,length);
158 			psrc += length;
159 			pdest += output.width;
160 		}
161 	}
162 	///Inserts a midsection of the bitmap defined by slice
163 	public void insertBitmapSlice(int x, int y, Bitmap16Bit bitmap, Coordinate slice){
164 		ushort* psrc = bitmap.getPtr, pdest = output.getPtr;
165 		pdest += x + output.width * y;
166 		int bmpWidth = bitmap.width;
167 		psrc += slice.left + bmpWidth * slice.top;
168 		int length = slice.width;
169 		for(int iy ; iy < slice.height ; iy++){
170 			blitter16bit(psrc,pdest,length);
171 			psrc += bmpWidth;
172 			pdest += output.width;
173 		}
174 	}
175 	///Draws a rectangle.
176 	public void drawRectangle(int xa, int xb, int ya, int yb, ushort color){
177 		drawLine(xa, xa, ya, yb, color);
178 		drawLine(xb, xb, ya, yb, color);
179 		drawLine(xa, xb, ya, ya, color);
180 		drawLine(xa, xb, yb, yb, color);
181 	}
182 	
183 	public void drawRectangle(int xa, int xb, int ya, int yb, Bitmap16Bit brush){
184 		xa = xa + brush.width;
185 		ya = ya + brush.height;
186 		xb = xb - brush.width;
187 		yb = yb - brush.height;
188 		drawLine(xa, xa, ya, yb, brush);
189 		drawLine(xb, xb, ya, yb, brush);
190 		drawLine(xa, xb, ya, ya, brush);
191 		drawLine(xa, xb, yb, yb, brush);
192 	}
193 	///Draws a filled rectangle.
194 	public void drawFilledRectangle(int xa, int xb, int ya, int yb, ushort color){
195 		//writeln(xa); writeln(ya); writeln(xb); writeln(yb);
196 		
197 		ushort[8] colorvect = [color, color, color, color, color, color, color, color];
198 		ushort* p = output.getPtr;
199 		int pitch = output.width;
200 		for(int y = ya ; y < yb ; y++){
201 			ushort* p0 = p + xa + y * pitch;
202 			
203 			int x = xa;
204 			//writeln(x);
205 			while( x < xb - 7 ){
206 				ushort[8]* p1 = cast(ushort[8]*)p0;
207 				/*asm{
208 				 movups	XMM0, colorvect;
209 				 movups	[p0], XMM0;
210 				 }*/
211 				*p1 = colorvect;
212 				p0 += 8;
213 				x += 8;
214 				//writeln(x);
215 				//output.writePixel(x, y, color);
216 			}
217 			if(xb - x > 3){
218 				ushort[4]* p1 = cast(ushort[4]*)p0;
219 				/*asm{
220 				 movups	XMM0, colorvect;
221 				 movq	[p0], XMM0;
222 				 }*/
223 				*p1 = [color,color,color,color];
224 				x += 4;
225 				p0 += 4;
226 				//writeln(x);
227 			}
228 			if(xb - x > 1){
229 				ushort[2]* p1 = cast(ushort[2]*)p0;
230 				/*asm{
231 				 movups	XMM0, colorvect;
232 				 movd	[p0], XMM0;
233 				 }*/
234 				*p1 = [color,color];
235 				x += 2;
236 				p0 += 2;
237 				//writeln(x);
238 			}
239 			if(xb - x == 1){
240 				*p0 = color;
241 				//writeln(x);
242 			}
243 		}
244 	}
245 	///Fills the area with a pattern.
246 	public void patternFill(int xa, int ya, int xb, int yb, Bitmap16Bit pattern){
247 		
248 	}
249 	///Draws texts. (deprecated, will be removed after Version 1.0.0)
250 	public deprecated void drawText(int x, int y, wstring text, Bitmap16Bit[wchar] fontSet, int style = 0){
251 		int length;
252 		for(int i ; i < text.length ; i++){
253 			length += fontSet[text[i]].width;
254 		}
255 		//writeln(text);
256 		if(style == 0){
257 			x = x - (length / 2);
258 			y -= fontSet['a'].height / 2;
259 		}
260 		foreach(wchar c ; text){
261 			
262 			insertBitmap(x, y, fontSet[c]);
263 			x = x + fontSet[c].width;
264 		}
265 	}
266 	///Draws text to the given point. Styles: 0 = centered, 1 = left, 2 = right
267 	public void drawText(int x, int y, wstring text, Fontset!(Bitmap16Bit) fontset, int style = 0){
268 		int length = fontset.getTextLength(text);
269 		//writeln(text);
270 		if(style == 0){
271 			x = x - (length / 2);
272 			y -= fontset.getSize() / 2;
273 		}else if(style == 2){
274 			y -= fontset.getSize();
275 		}
276 		foreach(wchar c ; text){
277 			insertBitmap(x, y, fontset.letters[c]);
278 			x = x + fontset.letters[c].width;
279 		}
280 	}
281 	///Draws colored text from monocromatic font.
282 	public void drawColorText(int x, int y, wstring text, Fontset!(Bitmap16Bit) fontset, ushort color, int style = 0){
283 		//color = 1;
284 		ushort[8] colorvect = [color, color, color, color, color, color, color, color];
285 		int length = fontset.getTextLength(text);
286 		//writeln(text);
287 		if(style == 0){
288 			x = x - (length / 2);
289 			y -= fontset.getSize() / 2;
290 		}else if(style == 2){
291 			x -= length;
292 		}
293 		foreach(wchar c ; text){
294 			
295 			insertColorLetter(x, y, fontset.letters[c], colorvect);
296 			x = x + fontset.letters[c].width;
297 		}
298 	}
299 	public void insertColorLetter(int x, int y, Bitmap16Bit bitmap, ushort[8] colorvect){
300 		version(NO_SSE2){
301 			ushort[4] colortester = [1,1,1,1], colorvect4 = [colorvect[0],colorvect[0],colorvect[0],colorvect[0]];
302 			ushort* psrc = bitmap.getPtr, pdest = output.getPtr;
303 			int pitch = output.width;
304 			for(int iy ; iy < bitmap.height ; iy++){
305 				int ix = bitmap.width / 4; 
306 				int ix2 = bitmap.width - ix * 2;
307 				int offsetY = bitmap.width * iy;
308 				ushort[4]* psrc2 = cast(ushort[4]*)(psrc + offsetY), pdest2 = cast(ushort[4]*)(pdest + x + ((iy + y) * pitch));
309 				asm{
310 					mov		EDI, pdest2[EBP];
311 					mov		ESI, psrc2[EBP];
312 					movq	MM5, colorvect4;
313 					movq	MM6, colortester;
314 					mov		ECX, ix;
315 					//cmp		ECX, 0;
316 					jecxz	blt4px;
317 				loopstart:		//using 8 pixel blitter for the most part
318 				
319 					movq	MM0, [ESI];
320 					movq	MM1, [EDI];
321 					movq	MM4, transparencytester4;
322 					pcmpeqw	MM4, MM0;
323 					pcmpeqw	MM0, MM6;
324 					pand	MM0, MM5;
325 					
326 					pand	MM1, XMM4;
327 					por		MM1, XMM0;
328 					movq	[EDI], XMM1;
329 				
330 					add		ESI, 8;
331 					add		EDI, 8;
332 					loop	loopstart;
333 				
334 					//4 pixel blitter if needed
335 				blt2px:
336 					mov		ECX, ix2;
337 					cmp		ECX, 2;
338 					jb		blt1px;
339 					sub		ECX, 2;
340 					movd	XMM0, [ESI];
341 					movd	XMM1, [EDI];
342 					movq	XMM4, transparencytester4;
343 					pcmpeqw	XMM4, XMM0;
344 					pcmpeqw	XMM0, XMM6;
345 					pand	XMM0, XMM5;
346 				
347 					pand	XMM1, XMM4;
348 					por		XMM1, XMM0;
349 					movd	[EDI], XMM1;
350 				
351 					add		ESI, 4;
352 					add		EDI, 4;
353 					//1 pixel "blitter" if needed
354 				blt1px:
355 					jecxz	end;
356 					mov		AX, [ESI];
357 					cmp		AX, 0;
358 					cmovnz	AX, colorvect[0];
359 					cmovz	AX, [EDI];
360 					mov		[EDI], AX;
361 				end:;
362 				}
363 				
364 			}
365 		}else version(X86){
366 			ushort[8] colortester = [1,1,1,1,1,1,1,1];
367 			ushort* psrc = bitmap.getPtr, pdest = output.getPtr;
368 			int pitch = output.width;
369 			for(int iy ; iy < bitmap.height ; iy++){
370 				int ix = bitmap.width / 8; 
371 				int ix4 = bitmap.width - ix * 8;
372 				int offsetY = bitmap.width * iy;
373 				ushort[8]* psrc2 = cast(ushort[8]*)(psrc + offsetY), pdest2 = cast(ushort[8]*)(pdest + x + ((iy + y) * pitch));
374 				asm{
375 					mov		EDI, pdest2[EBP];
376 					mov		ESI, psrc2[EBP];
377 					movups	XMM5, colorvect;
378 					movups	XMM6, colortester;
379 					mov		ECX, ix;
380 					//cmp		ECX, 0;
381 					jecxz	blt4px;
382 				loopstart:		//using 8 pixel blitter for the most part
383 				
384 					movups	XMM0, [ESI];
385 					movups	XMM1, [EDI];
386 					movups	XMM4, transparencytester8;
387 					pcmpeqw	XMM4, XMM0;
388 					pcmpeqw	XMM0, XMM6;
389 					pand	XMM0, XMM5;
390 					
391 					pand	XMM1, XMM4;
392 					por		XMM1, XMM0;
393 					movups	[EDI], XMM1;
394 				
395 					add		ESI, 16;
396 					add		EDI, 16;
397 					loop	loopstart;
398 				
399 					//4 pixel blitter if needed
400 				blt4px:
401 					mov		ECX, ix4;
402 					cmp		ECX, 4;
403 					jb		blt2px;
404 					sub		ECX, 4;
405 					movq	XMM0, [ESI];
406 					movq	XMM1, [EDI];
407 					movups	XMM4, transparencytester8;
408 					pcmpeqw	XMM4, XMM0;
409 					pcmpeqw	XMM0, XMM6;
410 					pand	XMM0, XMM5;
411 				
412 					pand	XMM1, XMM4;
413 					por		XMM1, XMM0;
414 					movq	[EDI], XMM1;
415 				
416 					add		ESI, 8;
417 					add		EDI, 8;
418 					//2 pixel blitter if needed
419 				blt2px:
420 					cmp		ECX, 2;
421 					jb		blt1px;
422 					sub		ECX, 2;
423 					movd	XMM0, [ESI];
424 					movd	XMM1, [EDI];
425 					movups	XMM4, transparencytester8;
426 					pcmpeqw	XMM4, XMM0;
427 					pcmpeqw	XMM0, XMM6;
428 					pand	XMM0, XMM5;
429 				
430 					pand	XMM1, XMM4;
431 					por		XMM1, XMM0;
432 					movd	[EDI], XMM1;
433 				
434 					add		ESI, 4;
435 					add		EDI, 4;
436 					//1 pixel "blitter" if needed
437 				blt1px:
438 					jecxz	end;
439 					mov		AX, [ESI];
440 					cmp		AX, 0;
441 					cmovnz	AX, colorvect[0];
442 					cmovz	AX, [EDI];
443 					mov		[EDI], AX;
444 				end:;
445 				}
446 				
447 			}
448 		}else version(X86_64){
449 			ushort[8] colortester = [1,1,1,1,1,1,1,1];
450 			ushort* psrc = bitmap.getPtr, pdest = output.getPtr;
451 			int pitch = output.width;
452 			for(int iy ; iy < bitmap.height ; iy++){
453 				int ix = bitmap.width / 8; 
454 				int ix4 = bitmap.width - ix * 8;
455 				int offsetY = bitmap.width * iy;
456 				ushort[8]* psrc2 = cast(ushort[8]*)(psrc + offsetY), pdest2 = cast(ushort[8]*)(pdest + x + ((iy + y) * pitch));
457 				asm{
458 					mov		RDI, pdest2[RBP];
459 					mov		RSI, psrc2[RBP];
460 					movups	XMM5, colorvect;
461 					movups	XMM6, colortester;
462 					mov		ECX, ix;
463 					//cmp		ECX, 0;
464 					jecxz	blt4px;
465 				loopstart:		//using 8 pixel blitter for the most part
466 				
467 					movups	XMM0, [RSI];
468 					movups	XMM1, [RDI];
469 					movups	XMM4, transparencytester8;
470 					pcmpeqw	XMM4, XMM0;
471 					pcmpeqw	XMM0, XMM6;
472 					pand	XMM0, XMM5;
473 					
474 					pand	XMM1, XMM4;
475 					por		XMM1, XMM0;
476 					movups	[RDI], XMM1;
477 				
478 					add		RSI, 16;
479 					add		RDI, 16;
480 					loop	loopstart;
481 				
482 					//4 pixel blitter if needed
483 				blt4px:
484 					mov		ECX, ix4;
485 					cmp		ECX, 4;
486 					jb		blt2px;
487 					sub		ECX, 4;
488 					movq	XMM0, [RSI];
489 					movq	XMM1, [RDI];
490 					movups	XMM4, transparencytester8;
491 					pcmpeqw	XMM4, XMM0;
492 					pcmpeqw	XMM0, XMM6;
493 					pand	XMM0, XMM5;
494 				
495 					pand	XMM1, XMM4;
496 					por		XMM1, XMM0;
497 					movq	[RDI], XMM1;
498 				
499 					add		RSI, 8;
500 					add		RDI, 8;
501 					//2 pixel blitter if needed
502 				blt2px:
503 					cmp		ECX, 2;
504 					jb		blt1px;
505 					sub		ECX, 2;
506 					movd	XMM0, [RSI];
507 					movd	XMM1, [RDI];
508 					movups	XMM4, transparencytester8;
509 					pcmpeqw	XMM4, XMM0;
510 					pcmpeqw	XMM0, XMM6;
511 					pand	XMM0, XMM5;
512 				
513 					pand	XMM1, XMM4;
514 					por		XMM1, XMM0;
515 					movd	[RDI], XMM1;
516 				
517 					add		RSI, 4;
518 					add		RDI, 4;
519 					//1 pixel "blitter" if needed
520 				blt1px:
521 					jecxz	end;
522 					mov		AX, [RSI];
523 					cmp		AX, 0;
524 					cmovnz	AX, colorvect[0];
525 					cmovz	AX, [RDI];
526 					mov		[RDI], AX;
527 				end:;
528 				}
529 				
530 			}
531 		}else{
532 			for(int iy; iy < bitmap.height; iy++){
533 				for(int ix; ix < bitmap.width; ix++){
534 					ushort c = bitmap.readPixel(ix, iy);
535 					if(c)
536 						output.writePixel(x + ix, y + iy, colorvect[0]);
537 				}
538 			}
539 		}
540 	}
541 }
542