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