1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, graphics.layers module
5  */
6 module PixelPerfectEngine.graphics.layers;
7 
8 public import PixelPerfectEngine.graphics.bitmap;
9 public import PixelPerfectEngine.graphics.common;
10 import PixelPerfectEngine.graphics.transformFunctions;
11 import PixelPerfectEngine.system.binarySearchTree;
12 import PixelPerfectEngine.system.etc;
13 import std.parallelism;
14 //import std.container.rbtree;
15 //import system.etc;
16 import PixelPerfectEngine.system.exc;
17 //import std.algorithm;
18 import bindbc.sdl;
19 import core.stdc.stdlib;
20 //import std.range;
21 import CPUblit.composing;
22 import CPUblit.colorlookup;
23 import CPUblit.transform;
24 import conv = std.conv;
25 
26 version(LDC){
27 	import inteli.emmintrin;
28 }
29 @nogc void localBlt(uint* src, uint* dest, size_t length){
30 	blitter(src, dest, length);
31 }
32 /**
33  * The basis of all layer classes, containing functions for rendering.
34  * TODO: Move rendering functions to an external library.
35  */
36 abstract class Layer {
37 	protected @nogc void function(uint* src, uint* dest, size_t length) mainRenderingFunction;		///Used to get around some readability issues. (void* src, void* dest, int length)
38 	protected @nogc void function(ushort* src, uint* dest, uint* palette, size_t length) mainColorLookupFunction;
39 	//protected @nogc void function(uint* src, int length) mainHorizontalMirroringFunction;
40 	protected @nogc void function(ubyte* src, uint* dest, uint* palette, size_t length) main8BitColorLookupFunction;
41 	protected @nogc void function(ubyte* src, uint* dest, uint* palette, size_t length, int offset) main4BitColorLookupFunction;
42 	protected LayerRenderingMode renderMode;
43 
44 	// scrolling position
45 	protected int sX, sY, rasterX, rasterY;
46 
47 	/// Sets the main rasterizer
48 	public void setRasterizer(int rX, int rY){
49 		//frameBuffer = frameBufferP;
50 		rasterX=rX;
51 		rasterY=rY;
52 
53 	}
54 	///Sets the rendering mode
55 	@nogc public void setRenderingMode(LayerRenderingMode mode){
56 		renderMode = mode;
57 		switch(mode){
58 			case LayerRenderingMode.ALPHA_BLENDING:
59 				//mainRenderingFunction = &alphaBlend;
60 				mainRenderingFunction = &alphaBlend32bit;
61 				break;
62 			case LayerRenderingMode.BLITTER:
63 				mainRenderingFunction = &localBlt;
64 				break;
65 			default:
66 				mainRenderingFunction = &copy32bit;
67 		}
68 		mainColorLookupFunction = &colorLookup!(ushort,uint);
69 		//mainHorizontalMirroringFunction = &flipHorizontal;
70 		main8BitColorLookupFunction = &colorLookup!(ubyte,uint);
71 		main4BitColorLookupFunction = &colorLookup4Bit!uint;
72 	}
73 	///Absolute scrolling.
74 	@nogc @safe public void scroll(int x, int y){
75 		sX=x;
76 		sY=y;
77 	}
78 	///Relative scrolling. Positive values scrolls the layer left and up, negative values scrolls the layer down and right.
79 	@nogc @safe public void relScroll(int x, int y){
80 		sX=sX+x;
81 		sY=sY+y;
82 	}
83 	///Getter for the X scroll position.
84 	@nogc @safe public int getSX(){
85 		return sX;
86 	}
87 	///Getter for the Y scroll position.
88 	@nogc @safe public int getSY(){
89 		return sY;
90 	}
91 	/// Override this to enable output to the raster
92 	public abstract void updateRaster(void* workpad, int pitch, Color* palette);
93 	///Standard algorithm for horizontal mirroring
94 	///Will be deprecated in later versions and instead external functions will be used.
95 	@nogc protected void flipHorizontal(uint* src, int length){
96 		uint s;
97 		uint* dest = src + length;
98 		for(int i ; i < length ; i++){
99 			s = *src;
100 			*src = *dest;
101 			*dest = s;
102 			src++;
103 			dest--;
104 		}
105 	}
106 }
107 
108 /**
109  * Sets the rendering mode of the TileLayer.
110  *
111  * COPY is the fastest, but overrides any kind of transparency keying. It directly writes into the framebuffer. Should only be used for certain applications, like bottom layers.
112  * BLITTER uses a custom BitBlT algorithm for the SSE2 instruction set. Automatically generates the copying mask depending on the alpha-value. Any alpha-value that's non-zero will cause a non-transparent pixel, and all zeros are completely transparent. Gradual transparency in not avaliable.
113  * ALPHA_BLENDING uses SSE2 for alpha blending. The slowest (although speed loss might be minimal due to memory access times), but allows gradual transparencies.
114  */
115 public enum LayerRenderingMode{
116 	COPY,
117 	BLITTER,
118 	ALPHA_BLENDING
119 }
120 /**
121  * Tile interface, defines common functions.
122  */
123 public interface ITileLayer{
124 	public MappingElement[] getMapping();
125 	/// Reads the mapping element from the given area.
126 	@nogc public MappingElement readMapping(int x, int y);
127 	/// Writes the given element into the mapping at the given location.
128 	@nogc public void writeMapping(int x, int y, MappingElement w);
129 	/// Loads the mapping, primarily used for deserialization.
130 	public void loadMapping(int x, int y, MappingElement[] mapping);
131 	/// Removes the tile from the display list with the given ID.
132 	public void removeTile(wchar id);
133 	/// Returns the tile ID from the location by pixel.
134 	@nogc public MappingElement tileByPixel(int x, int y);
135 	/// Returns the width of the tiles.
136 	@nogc public int getTileWidth();
137 	/// Returns the height of the tiles.
138 	@nogc public int getTileHeight();
139 	/// Returns the width of the mapping.
140 	@nogc public int getMX();
141 	/// Returns the height of the mapping.
142 	@nogc public int getMY();
143 	/// Returns the total width of the tile layer.
144 	@nogc public int getTX();
145 	/// Returns the total height of the tile layer.
146 	@nogc public int getTY();
147 	/// Adds a tile.
148 	public void addTile(ABitmap tile, wchar id);
149 }
150 
151 public struct MappingElement{
152 	wchar tileID;				///Determines which tile is being used for the given instance
153 	BitmapAttrib attributes;	///General attributes
154 	ubyte paletteSel;			///Selects the palette for the bitmap if supported
155 	@nogc this(wchar tileID, BitmapAttrib attributes = BitmapAttrib(false, false)){
156 		this.tileID = tileID;
157 		this.attributes = attributes;
158 	}
159 }
160 
161 /**
162  * General purpose TileLayer with palette support, mainly for backgrounds.
163  * Use multiple of this class for paralax scrolling.
164  * Can use any kind of bitmaps thanks to code restructuring.
165  */
166 public class TileLayer : Layer, ITileLayer{
167 	protected struct DisplayListItem{
168 		ABitmap tile;			///reference counting only
169 		void* pixelDataPtr;		///points to the pixeldata
170 		//Color* palettePtr;		///points to the palette if present
171 		wchar ID;				///ID, mainly as a padding to 32 bit alignment
172 		ubyte wordLength;		///to avoid calling the more costly classinfo
173 		ubyte reserved;		///currently unused
174 		this(wchar ID, ABitmap tile){
175 			//palettePtr = tile.getPalettePtr();
176 			//this.paletteSel = paletteSel;
177 			this.ID = ID;
178 			this.tile=tile;
179 			if(tile.classinfo == typeid(Bitmap4Bit)){
180 				wordLength = 4;
181 				pixelDataPtr = (cast(Bitmap4Bit)(tile)).getPtr;
182 			}else if(tile.classinfo == typeid(Bitmap8Bit)){
183 				wordLength = 8;
184 				pixelDataPtr = (cast(Bitmap8Bit)(tile)).getPtr;
185 			}else if(tile.classinfo == typeid(Bitmap16Bit)){
186 				wordLength = 16;
187 				pixelDataPtr = (cast(Bitmap16Bit)(tile)).getPtr;
188 			}else if(tile.classinfo == typeid(Bitmap32Bit)){
189 				wordLength = 32;
190 				pixelDataPtr = (cast(Bitmap32Bit)(tile)).getPtr;
191 			}else{
192 				throw new TileFormatException("Bitmap format not supported!");
193 			}
194 		}
195 	}
196 	protected int tileX, tileY, mX, mY;
197 	protected int totalX, totalY;
198 	protected MappingElement[] mapping;
199 	//private wchar[] mapping;
200 	//private BitmapAttrib[] tileAttributes;
201 	protected Color[] src;
202 	protected BinarySearchTree!(wchar, DisplayListItem) displayList;
203 	protected bool warpMode;
204 	///Constructor. tX , tY : Set the size of the tiles on the layer.
205 	this(int tX, int tY, LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){
206 		tileX=tX;
207 		tileY=tY;
208 		setRenderingMode(renderMode);
209 		src.length = tileX;
210 	}
211 	/// Warpmode: if enabled, the layer will be turned into an "infinite" mode.
212 	public void setWarpMode(bool w){
213 		warpMode = w;
214 	}
215 	///Gets the the ID of the given element from the mapping. x , y : Position.
216 	@nogc public MappingElement readMapping(int x, int y){
217 		if(!warpMode){
218 			if(x < 0 || y < 0 || x >= mX || y >= mY){
219 				return MappingElement(0xFFFF);
220 			}
221 		}else{
222 			x = x % mX;
223 			y = y % mY;
224 		}
225 		return mapping[x+(mX*y)];
226 	}
227 	///Writes to the map. x , y : Position. w : ID of the tile.
228 	@nogc public void writeMapping(int x, int y, MappingElement w){
229 		mapping[x+(mX*y)]=w;
230 	}
231 	///Writes to the map. x , y : Position. w : ID of the tile.
232 	/*@nogc public void writeTileAttribute(int x, int y, BitmapAttrib ba){
233 		tileAttributes[x+(mX*y)]=ba;
234 	}*/
235 	///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map.
236 	///x*y=map.length
237 	public void loadMapping(int x, int y, MappingElement[] mapping){
238 		mX=x;
239 		mY=y;
240 		this.mapping = mapping;
241 		totalX=mX*tileX;
242 		totalY=mY*tileY;
243 	}
244 	///Adds a tile to the tileSet. t : The tile. id : The ID in wchar to differentiate between different tiles.
245 	public void addTile(ABitmap tile, wchar id){
246 		if(tile.width==tileX && tile.height==tileY){
247 			displayList[id]=DisplayListItem(id, tile);
248 		}else{
249 			throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null);
250 		}
251 	}
252 	///Removes the tile with the ID from the set.
253 	public void removeTile(wchar id){
254 		displayList.remove(id);
255 	}
256 	///Returns which tile is at the given pixel
257 	@nogc public MappingElement tileByPixel(int x, int y){
258 		x /= tileX;
259 		y /= tileY;
260 		if(warpMode){
261 			x %= mX;
262 			y %= mY;
263 		}
264 		if(x >= mX || y >= mY || x < 0 || y < 0) return MappingElement(0xFFFF);
265 		return mapping[x + y*mX];
266 	}
267 
268 	public @nogc override void updateRaster(void* workpad, int pitch, Color* palette){
269 		//import core.stdc.stdio;
270 		int y = sY < 0 && !warpMode ? sY * -1 : 0;
271 		int sY0 = cast(int)(cast(uint)(sY) & 0b0111_1111_1111_1111_1111_1111_1111_1111);
272 		int offsetP = y*pitch;	// The offset of the line that is being written
273 		int offsetY = sY0 % tileY;		//Scroll offset upwards
274 		int offsetY0 = (sY + rasterY) % tileY;
275 		int offsetXA = sX%tileX;	// tile offset of the first column
276 		//for( ; y < rasterY ; y+=tileY){
277 		while(y < rasterY){
278 			//int offsetY = tileX * ((y + sY)%tileY);
279 			int offsetYA = !y ? offsetY : 0;	//top offset for first tile, 0 otherwise
280 			int offsetYB = y + tileY > rasterY ? offsetY0 : tileY;	//bottom offset of last tile, equals tileY otherwise
281 			uint x = (cast(uint)(sY) & 0b0111_1111_1111_1111_1111_1111_1111_1111);
282 			int targetX = totalX - sX > rasterX && !warpMode ? rasterX : rasterX - (totalX - sX);
283 			void *p0 = (workpad + (x*Color.sizeof) + offsetP);
284 			while(x < targetX){
285 				MappingElement currentTile = tileByPixel(x+sX,y+sY);
286 				int tileXtarget = x + tileX < rasterX ? tileX : tileX - ((x + tileX) - rasterX) ;	// the length of the displayed tile
287 				int xp = (offsetXA != 0 && x == 0) ? offsetXA : 0;	// offset of the first column
288 				tileXtarget -= xp;	// length of the first tile
289 				if(currentTile.tileID != 0xFFFF){ // skip if tile is null
290 					//BitmapAttrib tileAttrib = tileAttributeByPixel(x+sX,y+sY);
291 					const DisplayListItem d = displayList[currentTile.tileID];	// pointer to the current tile's pixeldata
292 					int tileYOffset = tileY;
293 					tileYOffset *= currentTile.attributes.vertMirror ? -1 : 1;	//vertical mirroring
294 					//int pitchOffset = pitch * threads.length;
295 					void* p1 = p0;
296 					switch(d.wordLength){
297 						case 4:
298 							ubyte* c = cast(ubyte*)d.pixelDataPtr;
299 							c += currentTile.attributes.vertMirror ? ((tileY - offsetYA - 1) * tileX)>>1 : (offsetYA * tileX)>>1;
300 							for(int y0 = offsetYA ; y0 < offsetYB ; y0++){
301 								main4BitColorLookupFunction(c, cast(uint*)src.ptr, cast(uint*)(palette + (currentTile.paletteSel<<4)), tileX,
302 										x & 1);
303 								if(currentTile.attributes.horizMirror){//Horizontal mirroring
304 									flipHorizontal(cast(uint*)src.ptr, tileX);
305 								}
306 								mainRenderingFunction(cast(uint*)src.ptr + xp, cast(uint*)p1, tileXtarget);
307 								c += tileYOffset>>>1;
308 								p1 += pitch;
309 							}
310 							break;
311 						case 8:
312 							ubyte* c = cast(ubyte*)d.pixelDataPtr;
313 							c += currentTile.attributes.vertMirror ? (tileY - offsetYA - 1) * tileX : offsetYA * tileX;
314 							for(int y0 = offsetYA ; y0 < offsetYB ; y0++){
315 								main8BitColorLookupFunction(c, cast(uint*)src.ptr, cast(uint*)(palette + (currentTile.paletteSel<<4)), tileX);
316 								if(currentTile.attributes.horizMirror){//Horizontal mirroring
317 									flipHorizontal(cast(uint*)src.ptr, tileX);
318 								}
319 								mainRenderingFunction(cast(uint*)src.ptr + xp, cast(uint*)p1, tileXtarget);
320 								c += tileYOffset;
321 								p1 += pitch;
322 							}
323 							break;
324 						case 16:
325 							ushort* c = cast(ushort*)d.pixelDataPtr;
326 							c += currentTile.attributes.vertMirror ? (tileY - offsetYA - 1) * tileX : offsetYA * tileX;
327 							for(int y0 = offsetYA ; y0 < offsetYB ; y0++){
328 								mainColorLookupFunction(c, cast(uint*)src.ptr, cast(uint*)palette, tileX);
329 								if(currentTile.attributes.horizMirror){//Horizontal mirroring
330 									flipHorizontal(cast(uint*)src.ptr, tileX);
331 								}
332 								mainRenderingFunction(cast(uint*)src.ptr + xp, cast(uint*)p1, tileXtarget);
333 								c += tileYOffset;
334 								p1 += pitch;
335 							}
336 							break;
337 						case 32:
338 							Color* c = cast(Color*)d.pixelDataPtr;
339 							c += currentTile.attributes.vertMirror ? (tileY - offsetYA - 1) * tileX : offsetYA * tileX;
340 							for(int y0 = offsetYA ; y0 < offsetYB ; y0++){
341 								if(currentTile.attributes.horizMirror){//Horizontal mirroring
342 									copy32bit(cast(uint*)c, cast(uint*)src.ptr, tileX);
343 									flipHorizontal(cast(uint*)src.ptr, tileX);
344 									mainRenderingFunction(cast(uint*)src.ptr + xp, cast(uint*)p1, tileXtarget);
345 								}else{
346 									mainRenderingFunction(cast(uint*)(c + xp), cast(uint*)p1, tileXtarget);
347 								}
348 								c += tileYOffset;
349 								p1 += pitch;
350 							}
351 							break;
352 						default:
353 							break;
354 					}
355 
356 				}
357 				p0 += tileXtarget * Color.sizeof;
358 				x+=tileXtarget;
359 			}
360 			offsetP	+= !y ? pitch * (tileY - offsetY) : pitch * tileY;
361 			/*if(y + tileY > y) y += tileY - offsetY0;
362 			else if(y) y += tileY;
363 			else y += (tileY - offsetY);*/
364 			y += !y ? (tileY - offsetY) : tileY;
365 		}
366 
367 
368 	}
369 	public MappingElement[] getMapping(){
370 		return mapping;
371 	}
372 	@nogc public int getTileWidth(){
373 		return tileX;
374 	}
375 	@nogc public int getTileHeight(){
376 		return tileY;
377 	}
378 	@nogc public int getMX(){
379 		return mX;
380 	}
381 	@nogc public int getMY(){
382 		return mY;
383 	}
384 	@nogc public int getTX(){
385 		return totalX;
386 	}
387 	@nogc public int getTY(){
388 		return totalY;
389 	}
390 }
391 /**
392  * Implements a modified TileLayer with transformability with capabilities similar to MODE7.
393  * <br/>
394  * Transform function:
395  * [x',y'] = ([A,B,C,D] * ([x,y] + [sX,sY] - [x_0,y_0])>>>8 + [x_0,y_0]
396  * <br/>
397  * All basic transform values are integer based, 256 equals with 1.0
398  * <br/>
399  * Restrictions compared to standard TileLayer:
400  * <ul>
401  * <li>Tiles must have any of the following sizes: 8, 16, 32, 64; since this layer needs to do modulo computations for each pixel.</li>
402  * <li>Maximum layer size in pixels are restricted to 65536*65536 due to architectural limitations. Accelerated versions might raise
403  * this limitation.</li>
404  * </ul>
405  * HDMA emulation supported through delegate hBlankInterrupt.
406  */
407 public class TransformableTileLayer(BMPType = Bitmap16Bit, int TileX = 8, int TileY = 8) : Layer, ITileLayer{
408 		/*if(isPowerOf2(TileX) && isPowerOf2(TileY))*/
409 	protected struct DisplayListItem{
410 		void* pixelSrc;		///Used for quicker access to the Data
411 		wchar ID;			///ID, mainly used as a padding
412 		ushort reserved;	///Padding for 32 bit
413 		this(wchar ID, BMPType tile){
414 			this.ID = ID;
415 			pixelSrc = cast(void*)tile.getPtr();
416 		}
417 	}
418 	protected BinarySearchTree!(wchar, DisplayListItem) displayList;
419 	protected short[4] transformPoints;	/** Defines how the layer is being transformed */
420 	protected short[2] tpOrigin;
421 	protected Bitmap32Bit backbuffer;	///used to store current screen output
422 	/*static if(BMPType.mangleof == Bitmap8Bit.mangleof){
423 		protected ubyte[] src;
424 		protected Color* palettePtr;	///Shared palette
425 	}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
426 		protected ushort[] src;*/
427 	static if(BMPType.mangleof == Bitmap4Bit.mangleof || BMPType.mangleof == Bitmap8Bit.mangleof ||
428 			BMPType.mangleof == Bitmap16Bit.mangleof){
429 		protected ushort[] src;
430 	}else static if(BMPType.mangleof == Bitmap32Bit.mangleof){
431 
432 	}else static assert(false,"Template parameter " ~ BMPType.mangleof ~ " not supported by TransformableTileLayer!");
433 	protected bool needsUpdate;			///Set to true if backbuffer needs an update
434 	protected bool warpMode;			///Repeats the whole layer if set to true
435 	protected int mX, mY;				///"Inherited" from TileLayer
436 	static if(TileX == 8)
437 		protected immutable int shiftX = 3;
438 	else static if(TileX == 16)
439 		protected immutable int shiftX = 4;
440 	else static if(TileX == 32)
441 		protected immutable int shiftX = 5;
442 	else static if(TileX == 64)
443 		protected immutable int shiftX = 6;
444 	else static assert(false,"Unsupported horizontal tile size!");
445 	static if(TileY == 8)
446 		protected immutable int shiftY = 3;
447 	else static if(TileY == 16)
448 		protected immutable int shiftY = 4;
449 	else static if(TileY == 32)
450 		protected immutable int shiftY = 5;
451 	else static if(TileY == 64)
452 		protected immutable int shiftY = 6;
453 	else static assert(false,"Unsupported vertical tile size!");
454 	protected int totalX, totalY;
455 	protected MappingElement[] mapping;
456 	version(LDC){
457 		protected int4 _tileAmpersand;
458 		protected static short8 _increment;
459 	}
460 	alias HBIDelegate = @nogc void delegate(ref short[4] localABCD, ref short[2] localsXsY, ref short[2] localx0y0, short y);
461 	/**
462 	 * Called before each line being redrawn. Can modify global values for each lines.
463 	 */
464 	public HBIDelegate hBlankInterrupt;
465 
466 	this(LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){
467 		A = 256;
468 		B = 0;
469 		C = 0;
470 		D = 256;
471 		x_0 = 0;
472 		y_0 = 0;
473 		_tileAmpersand = [TileX - 1, TileY - 1, TileX - 1, TileY - 1];
474 		setRenderingMode(renderMode);
475 		needsUpdate = true;
476 	}
477 	static this(){
478 		for(int i ; i < 8 ; i+=2)
479 			_increment[i] = 2;
480 	}
481 	override public void setRasterizer(int rX,int rY) {
482 		super.setRasterizer(rX,rY);
483 		backbuffer = new Bitmap32Bit(rX, rY);
484 		static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof){
485 			src.length = rX;
486 		}
487 	}
488 
489 	override public @nogc void updateRaster(void* workpad,int pitch,Color* palette) {
490 		//import core.stdc.stdio;
491 		if(needsUpdate){
492 			needsUpdate = false;
493 			//clear buffer
494 			//backbuffer.clear();
495 			Color* dest = backbuffer.getPtr();
496 			short[2] sXsY = [cast(short)sX,cast(short)sY];
497 			short[4] localTP = transformPoints;
498 			short[2] localTPO = tpOrigin;
499 			//write new data into it
500 			for(short y; y < rasterY; y++){
501 				if(hBlankInterrupt !is null){
502 					hBlankInterrupt(localTP, sXsY, localTPO, y);
503 				}
504 				version(DMD){
505 					for(short x; x < rasterX; x++){
506 						int[2] xy = transformFunctionInt([x,y], localTP, localTPO, sXsY);
507 						//printf("[%i,%i]",xy[0],xy[1]);
508 						MappingElement currentTile = tileByPixelWithoutTransform(xy[0],xy[1]);
509 						if(currentTile.tileID != 0xFFFF){
510 							DisplayListItem d = displayList[currentTile.tileID];
511 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
512 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
513 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof){
514 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
515 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
516 								ushort* tsrc = cast(ushort*)d.pixelSrc;
517 							}else static if(BMPType.mangleof == Bitmap32Bit.mangleof){
518 								Color* tsrc = cast(Color*)d.pixelSrc;
519 							}
520 							xy[0] = xy[0] & (TileX - 1);
521 							xy[1] = xy[1] & (TileY - 1);
522 							const int totalOffset = xy[0] + xy[1] * TileX;
523 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
524 								src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile.paletteSel<<4;
525 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){
526 								src[x] = tsrc[totalOffset] | currentTile.paletteSel<<8;
527 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
528 								src[x] = tsrc[totalOffset];
529 							}else{
530 								*dest = *tsrc;
531 								dest++;
532 							}
533 						}else{
534 							static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
535 									BMPType.mangleof == Bitmap4Bit.mangleof){
536 								src[x] = 0;
537 							}else{
538 								(*dest).raw = 0;
539 							}
540 						}
541 					}
542 				}else version(LDC){
543 					/*short8 _sXsY = [sXsY[0],sXsY[1],sXsY[0],sXsY[1],sXsY[0],sXsY[1],sXsY[0],sXsY[1]],
544 							_localTP = [localTP[0], localTP[1],localTP[2], localTP[3], localTP[0], localTP[1],localTP[2], localTP[3]],
545 							_localTPO = [localTPO[0],localTPO[1],localTPO[0],localTPO[1],localTPO[0],localTPO[1],localTPO[0],localTPO[1]];
546 					int4 _localTPO_0 = [localTPO[0],localTPO[1],localTPO[0],localTPO[1]];*/
547 					short8 _sXsY, _localTP, _localTPO;
548 					for(int i; i < 8; i++){
549 						_sXsY[i] = sXsY[i & 1];
550 						_localTP[i] = localTP[i & 3];
551 						_localTPO[i] = localTPO[i & 1];
552 					}
553 					short8 xy_in;
554 					for(int i = 1; i < 8; i += 2){
555 						xy_in[i] = y;
556 					}
557 					xy_in[4] = 1;
558 					xy_in[6] = 1;
559 					int4 _localTPO_0;
560 					for(int i; i < 4; i++){
561 						_localTPO_0[i] = localTPO[i & 1];
562 					}
563 					for(short x; x < rasterX; x++){
564 						int4 xy = _mm_srai_epi32(_mm_madd_epi16(_localTP, xy_in + _sXsY - _localTPO),8) + _localTPO_0;
565 						MappingElement currentTile0 = tileByPixelWithoutTransform(xy[0],xy[1]),
566 								currentTile1 = tileByPixelWithoutTransform(xy[2],xy[3]);
567 						xy &= _tileAmpersand;
568 						if(currentTile0.tileID != 0xFFFF){
569 							const DisplayListItem d = displayList[currentTile0.tileID];
570 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
571 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
572 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof){
573 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
574 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
575 								ushort* tsrc = cast(ushort*)d.pixelSrc;
576 							}else static if(BMPType.mangleof == Bitmap32Bit.mangleof){
577 								Color* tsrc = cast(Color*)d.pixelSrc;
578 							}
579 							xy[0] = xy[0] & (TileX - 1);
580 							xy[1] = xy[1] & (TileY - 1);
581 							const int totalOffset = xy[0] + xy[1] * TileX;
582 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
583 								src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile0.paletteSel<<4;
584 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){
585 								src[x] = tsrc[totalOffset] | currentTile0.paletteSel<<8;
586 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
587 								src[x] = tsrc[totalOffset];
588 							}else{
589 								*dest = *tsrc;
590 								dest++;
591 							}
592 						}else{
593 							static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
594 									BMPType.mangleof == Bitmap4Bit.mangleof){
595 								src[x] = 0;
596 							}else{
597 								(*dest).raw = 0;
598 							}
599 						}
600 						x++;
601 						if(currentTile1.tileID != 0xFFFF){
602 							const DisplayListItem d = displayList[currentTile1.tileID];
603 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
604 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
605 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof){
606 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
607 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
608 								ushort* tsrc = cast(ushort*)d.pixelSrc;
609 							}else static if(BMPType.mangleof == Bitmap32Bit.mangleof){
610 								Color* tsrc = cast(Color*)d.pixelSrc;
611 							}
612 							xy[2] = xy[2] & (TileX - 1);
613 							xy[3] = xy[3] & (TileY - 1);
614 							const int totalOffset = xy[2] + xy[3] * TileX;
615 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
616 								src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile1.paletteSel<<4;
617 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){
618 								src[x] = tsrc[totalOffset] | currentTile1.paletteSel<<8;
619 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
620 								src[x] = tsrc[totalOffset];
621 							}else{
622 								*dest = *tsrc;
623 								dest++;
624 							}
625 						}else{
626 							static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
627 									BMPType.mangleof == Bitmap4Bit.mangleof){
628 								src[x] = 0;
629 							}else{
630 								(*dest).raw = 0;
631 							}
632 						}
633 						xy_in += _increment;
634 
635 					}
636 				}else static assert(false, "Compiler not supported");
637 				/*static if(BMPType.mangleof == Bitmap8Bit.mangleof){
638 					main8BitColorLookupFunction(src.ptr, cast(uint*)dest, cast(uint*)palettePtr, rasterX);
639 					dest += rasterX;
640 				}else*/ static if(BMPType.mangleof == Bitmap4Bit.mangleof || BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof){
641 					mainColorLookupFunction(src.ptr, cast(uint*)dest, cast(uint*)palette, rasterX);
642 					dest += rasterX;
643 				}
644 			}
645 		}
646 		//render surface onto the raster
647 		void* p0 = workpad;
648 		Color* c = backbuffer.getPtr();
649 		for(int y; y < rasterY; y++){
650 			mainRenderingFunction(cast(uint*)c,cast(uint*)p0,rasterX);
651 			c += rasterX;
652 			p0 += pitch;
653 		}
654 
655 	}
656 	///Returns which tile is at the given pixel
657 	@nogc public MappingElement tileByPixel(int x, int y){
658 		int[2] xy = transformFunctionInt([cast(short)x,cast(short)y],transformPoints,tpOrigin,[cast(short)sX,cast(short)sY]);
659 		return tileByPixelWithoutTransform(xy[0],xy[1]);
660 	}
661 	///Returns which tile is at the given pixel
662 	@nogc protected MappingElement tileByPixelWithoutTransform(int x, int y){
663 		x >>>= shiftX;
664 		y >>>= shiftY;
665 		if(warpMode){
666 			x %= mX;
667 			y %= mY;
668 		}
669 		if(x >= mX || y >= mY || x < 0 || y < 0) return MappingElement(0xFFFF);
670 		return mapping[x + y*mX];
671 	}
672 
673 	/**
674 	 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out.
675 	 */
676 	public @nogc @property short A(){
677 		return transformPoints[0];
678 	}
679 	/**
680 	 * Horizontal shearing.
681 	 */
682 	public @nogc @property short B(){
683 		return transformPoints[1];
684 	}
685 	/**
686 	 * Vertical shearing.
687 	 */
688 	public @nogc @property short C(){
689 		return transformPoints[2];
690 	}
691 	/**
692 	 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out.
693 	 */
694 	public @nogc @property short D(){
695 		return transformPoints[3];
696 	}
697 	/**
698 	 * Horizontal transformation offset.
699 	 */
700 	public @nogc @property short x_0(){
701 		return tpOrigin[0];
702 	}
703 	/**
704 	 * Vertical transformation offset.
705 	 */
706 	public @nogc @property short y_0(){
707 		return tpOrigin[1];
708 	}
709 	/**
710 	 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out.
711 	 */
712 	public @nogc @property short A(short newval){
713 		transformPoints[0] = newval;
714 		needsUpdate = true;
715 		return transformPoints[0];
716 	}
717 	/**
718 	 * Horizontal shearing.
719 	 */
720 	public @nogc @property short B(short newval){
721 		transformPoints[1] = newval;
722 		needsUpdate = true;
723 		return transformPoints[1];
724 	}
725 	/**
726 	 * Vertical shearing.
727 	 */
728 	public @nogc @property short C(short newval){
729 		transformPoints[2] = newval;
730 		needsUpdate = true;
731 		return transformPoints[2];
732 	}
733 	/**
734 	 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out.
735 	 */
736 	public @nogc @property short D(short newval){
737 		transformPoints[3] = newval;
738 		needsUpdate = true;
739 		return transformPoints[3];
740 	}
741 	/**
742 	 * Horizontal transformation offset.
743 	 */
744 	public @nogc @property short x_0(short newval){
745 		tpOrigin[0] = newval;
746 		//tpOrigin[2] = newval;
747 		needsUpdate = true;
748 		return tpOrigin[0];
749 	}
750 	/**
751 	 * Vertical transformation offset.
752 	 */
753 	public @nogc @property short y_0(short newval){
754 		tpOrigin[1] = newval;
755 		//tpOrigin[3] = newval;
756 		needsUpdate = true;
757 		return tpOrigin[1];
758 	}
759 	override public @safe @nogc void scroll(int x,int y) {
760 		super.scroll(x,y);
761 		needsUpdate = true;
762 	}
763 	override public @safe @nogc void relScroll(int x,int y) {
764 		super.relScroll(x,y);
765 		needsUpdate = true;
766 	}
767 	public MappingElement[] getMapping(){
768 		return mapping;
769 	}
770 	@nogc public int getTileWidth(){
771 		return TileX;
772 	}
773 	@nogc public int getTileHeight(){
774 		return TileY;
775 	}
776 	@nogc public int getMX(){
777 		return mX;
778 	}
779 	@nogc public int getMY(){
780 		return mY;
781 	}
782 	@nogc public int getTX(){
783 		return totalX;
784 	}
785 	@nogc public int getTY(){
786 		return totalY;
787 	}
788 	/// Warpmode: if enabled, the layer will be turned into an "infinite" mode.
789 	public void setWarpMode(bool w){
790 		warpMode = w;
791 	}
792 	///Gets the the ID of the given element from the mapping. x , y : Position.
793 	@nogc public MappingElement readMapping(int x, int y){
794 		if(!warpMode){
795 			if(x < 0 || y < 0 || x >= mX || y >= mY){
796 				return MappingElement(0xFFFF);
797 			}
798 		}else{
799 			x = x % mX;
800 			y = y % mY;
801 		}
802 		return mapping[x+(mX*y)];
803 	}
804 	///Writes to the map. x , y : Position. w : ID of the tile.
805 	@nogc public void writeMapping(int x, int y, MappingElement w){
806 		mapping[x+(mX*y)]=w;
807 	}
808 	public void addTile(ABitmap tile, wchar id){
809 		if(tile.classinfo != typeid(BMPType)){
810 			throw new TileFormatException("Incorrect type of tile!");
811 		}
812 		if(tile.width == TileX && tile.height == TileY){
813 			displayList[id]=DisplayListItem(id, cast(BMPType)tile);
814 		}else{
815 			throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null);
816 		}
817 	}
818 	///Removes the tile with the ID from the set.
819 	public void removeTile(wchar id){
820 		displayList.remove(id);
821 	}
822 	///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map.
823 	///x*y=map.length
824 	public void loadMapping(int x, int y, MappingElement[] mapping){
825 		mX=x;
826 		mY=y;
827 		this.mapping = mapping;
828 		totalX=mX*TileX;
829 		totalY=mY*TileY;
830 	}
831 }
832 /**
833  *Used by the collision detectors
834  *DEPRECATED! Use the new system instead!
835  */
836 public interface ISpriteCollision{
837 	///Returns all sprite coordinates.
838 	public ref Coordinate[int] getCoordinates();
839 	///Returns all sprite attributes.
840 	public ref BitmapAttrib[int] getSpriteAttributes();
841 	public ref int[] getSpriteSorter();
842 
843 }
844 /**
845  *General SpriteLayer interface.
846  */
847 public interface ISpriteLayer{
848 	///Removes the sprite with the given ID.
849 	public void removeSprite(int n);
850 	///Moves the sprite to the given location.
851 	public @nogc void moveSprite(int n, int x, int y);
852 	///Relatively moves the sprite by the given values.
853 	public @nogc void relMoveSprite(int n, int x, int y);
854 	///Gets the coordinate of the sprite.
855 	public Coordinate getSpriteCoordinate(int n);
856 	///Adds a sprite to the layer.
857 	public void addSprite(ABitmap s, int n, Coordinate c, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024);
858 	///Adds a sprite to the layer.
859 	public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024);
860 	///Replaces the sprite. If the new sprite has a different dimension, the old sprite's upper-left corner will be used.
861 	public void replaceSprite(ABitmap s, int n);
862 	///Replaces the sprite and moves to the given position.
863 	public void replaceSprite(ABitmap s, int n, int x, int y);
864 	///Replaces the sprite and moves to the given position.
865 	public void replaceSprite(ABitmap s, int n, Coordinate c);
866 	///Edits a sprite attribute.
867 	public void editSpriteAttribute(string S, T)(int n, T value);
868 	///Reads a sprite attribute
869 	public T readSpriteAttribute(string S, T)(int n);
870 	///Replaces a sprite attribute. DEPRECATED
871 	public void replaceSpriteAttribute(int n, BitmapAttrib attr);
872 	///Returns the displayed portion of the sprite.
873 	public @nogc Coordinate getSlice(int n);
874 	///Writes the displayed portion of the sprite.
875 	///Returns the new slice, if invalid (greater than the bitmap, etc.) returns the old one.
876 	public @nogc Coordinate setSlice(int n, Coordinate slice);
877 	///Returns the selected paletteID of the sprite.
878 	public @nogc ushort getPaletteID(int n);
879 	///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation
880 	///Palette must exist in the parent Raster, otherwise AccessError might happen
881 	public @nogc ushort setPaletteID(int n, ushort paletteID);
882 	///Scales bitmap horizontally
883 	public @nogc int scaleSpriteHoriz(int n, int hScl);
884 	///Scales bitmap vertically
885 	public @nogc int scaleSpriteVert(int n, int vScl);
886 }
887 /**
888  *Use it to call the collision detector
889  *DEPRECATED!
890  */
891 public interface SpriteMovementListener{
892 	///Called when a sprite is moved.
893 	void spriteMoved(int ID);
894 }
895 /**
896  * General-purpose sprite controller and renderer.
897  */
898 public class SpriteLayer : Layer, ISpriteLayer{
899 	/**
900 	 * Helps to determine the displaying properties and order of sprites.
901 	 */
902 	public struct DisplayListItem{
903 		Coordinate position;		/// Stores the position relative to the origin point. Actual display position is determined by the scroll positions.
904 		Coordinate slice;			/// To compensate for the lack of scanline interrupt capabilities, this enables chopping off parts of a sprite.
905 		//ABitmap sprite;				/// Defines the sprite being displayed on the screen.
906 		void* pixelData;			/// Points to the pixel data.
907 		//Color* palette;
908 		int width;					/// Width of the sprite
909 		int height;					/// Height of the sprite
910 		int scaleHoriz;				/// Horizontal scaling
911 		int scaleVert;				/// Vertical scaling
912 		int priority;				/// Used for automatic sorting and identification.
913 		BitmapAttrib attributes;	/// Horizontal and vertical mirroring. DEPRECATED
914 		ubyte wordLength;			/// Determines the word length of a sprite in a much quicker way than getting classinfo.
915 		/**
916 		 * Selects the palette of the sprite (Bitmap4Bit: 4096X16 color palettes; Bitmap8Bit: 256X256 color palettes)
917 		 */
918 		ushort paletteSel;
919 		this(Coordinate position, ABitmap sprite, int priority, ushort paletteSel = 0, int scaleHoriz = 1024,
920 				int scaleVert = 1024){
921 			this.position = position;
922 			this.width = sprite.width;
923 			this.height = sprite.height;
924 			this.priority = priority;
925 			//this.attributes = attributes;
926 			this.scaleVert = scaleVert;
927 			this.scaleHoriz = scaleHoriz;
928 			slice = Coordinate(0,0,sprite.width,sprite.height);
929 			if(sprite.classinfo == typeid(Bitmap4Bit)){
930 				wordLength = 4;
931 				pixelData = (cast(Bitmap4Bit)(sprite)).getPtr;
932 			}else if(sprite.classinfo == typeid(Bitmap8Bit)){
933 				wordLength = 8;
934 				pixelData = (cast(Bitmap8Bit)(sprite)).getPtr;
935 			}else if(sprite.classinfo == typeid(Bitmap16Bit)){
936 				wordLength = 16;
937 				pixelData = (cast(Bitmap16Bit)(sprite)).getPtr;
938 			}else if(sprite.classinfo == typeid(Bitmap32Bit)){
939 				wordLength = 32;
940 				pixelData = (cast(Bitmap32Bit)(sprite)).getPtr;
941 			}
942 		}
943 		this(Coordinate position, Coordinate slice, ABitmap sprite, int priority, int scaleHoriz = 1024,
944 				int scaleVert = 1024){
945 			this.position = position;
946 			//this.sprite = sprite;
947 			//palette = sprite.getPalettePtr();
948 			this.priority = priority;
949 			//this.attributes = attributes;
950 			this.scaleVert = scaleVert;
951 			this.scaleHoriz = scaleHoriz;
952 			if(slice.top < 0)
953 				slice.top = 0;
954 			if(slice.left < 0)
955 				slice.left = 0;
956 			if(slice.right >= sprite.width)
957 				slice.right = sprite.width - 1;
958 			if(slice.bottom >= sprite.height)
959 				slice.bottom = sprite.height - 1;
960 			this.slice = slice;
961 			if(sprite.classinfo == typeid(Bitmap4Bit)){
962 				wordLength = 4;
963 				pixelData = (cast(Bitmap4Bit)(sprite)).getPtr;
964 				//palette = sprite.getPalettePtr();
965 			}else if(sprite.classinfo == typeid(Bitmap8Bit)){
966 				wordLength = 8;
967 				pixelData = (cast(Bitmap8Bit)(sprite)).getPtr;
968 				//palette = sprite.getPalettePtr();
969 			}else if(sprite.classinfo == typeid(Bitmap16Bit)){
970 				wordLength = 16;
971 				pixelData = (cast(Bitmap16Bit)(sprite)).getPtr;
972 			}else if(sprite.classinfo == typeid(Bitmap32Bit)){
973 				wordLength = 32;
974 				pixelData = (cast(Bitmap32Bit)(sprite)).getPtr;
975 			}
976 		}
977 		/**
978 		 * Resets the slice to its original position.
979 		 */
980 		@nogc void resetSlice(){
981 			slice.left = 0;
982 			slice.top = 0;
983 			slice.right = position.width;
984 			slice.bottom = position.height;
985 		}
986 		/**
987 		 * Replaces the sprite with a new one.
988 		 * If the sizes are mismatching, the top-left coordinates are left as is, but the slicing is reset.
989 		 */
990 		void replaceSprite(ABitmap sprite){
991 			//this.sprite = sprite;
992 			//palette = sprite.getPalettePtr();
993 			if(this.width != sprite.width || this.height != sprite.height){
994 				this.width = sprite.width;
995 				this.height = sprite.height;
996 				position.right = position.left + cast(int)scaleNearestLength(width, scaleHoriz);
997 				position.bottom = position.top + cast(int)scaleNearestLength(height, scaleVert);
998 			}
999 			resetSlice();
1000 			if(sprite.classinfo == typeid(Bitmap4Bit)){
1001 				wordLength = 4;
1002 				pixelData = (cast(Bitmap4Bit)(sprite)).getPtr;
1003 				//palette = sprite.getPalettePtr();
1004 			}else if(sprite.classinfo == typeid(Bitmap8Bit)){
1005 				wordLength = 8;
1006 				pixelData = (cast(Bitmap8Bit)(sprite)).getPtr;
1007 				//palette = sprite.getPalettePtr();
1008 			}else if(sprite.classinfo == typeid(Bitmap16Bit)){
1009 				wordLength = 16;
1010 				pixelData = (cast(Bitmap16Bit)(sprite)).getPtr;
1011 			}else if(sprite.classinfo == typeid(Bitmap32Bit)){
1012 				wordLength = 32;
1013 				pixelData = (cast(Bitmap32Bit)(sprite)).getPtr;
1014 			}
1015 		}
1016 		@nogc int opCmp(in DisplayListItem d) const{
1017 			return priority - d.priority;
1018 		}
1019 		@nogc bool opEquals(in DisplayListItem d) const{
1020 			return priority == d.priority;
1021 		}
1022 		string toString(){
1023 			return "[Position: " ~ position.toString ~ ";\nDisplayed portion: " ~ slice.toString ~";\nPriority" ~
1024 				conv.to!string(priority) ~ "; PixelData: " ~ conv.to!string(pixelData) ~ "; Attributes: " ~ attributes.toString ~
1025 				"; PaletteSel: " ~ conv.to!string(paletteSel) ~ "; WordLenght: " ~ conv.to!string(wordLength) ~ "]";
1026 		}
1027 	}
1028 	protected DisplayListItem[] displayList;	///Stores the display data
1029 	Color[1024] src;
1030 	//size_t[8] prevSize;
1031 
1032 	public this(LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){
1033 		setRenderingMode(renderMode);
1034 		//src[0].length = 1024;
1035 	}
1036 	/+~this(){
1037 		foreach(p; src){
1038 			if(p)
1039 				free(p);
1040 		}
1041 	}+/
1042 	override public void setRasterizer(int rX,int rY) {
1043 		super.setRasterizer(rX,rY);
1044 		/+for(int i; i < src.length; i++){
1045 			src[i].length=rY;
1046 		}+/
1047 		//src.length = rY;
1048 	}
1049 	///Returns the displayed portion of the sprite.
1050 	public @nogc Coordinate getSlice(int n){
1051 		for(int i ; i < displayList.length ; i++){
1052 			if(displayList[i].priority == n){
1053 				return displayList[i].slice;
1054 			}
1055 		}
1056 		return Coordinate(0,0,0,0);
1057 	}
1058 	///Writes the displayed portion of the sprite.
1059 	///Returns the new slice, if invalid (greater than the bitmap, etc.) returns the old one.
1060 	public @nogc Coordinate setSlice(int n, Coordinate slice){
1061 		for(int i ; i < displayList.length ; i++){
1062 			if(displayList[i].priority == n){
1063 				//test if inputted slice is valid
1064 				if(slice.top >= 0 && slice.bottom >= 0 && slice.left >= 0 && slice.right >= 0 && slice.top <= slice.bottom &&
1065 						slice.left <= slice.right && slice.width <= displayList[i].slice.width && slice.height <=
1066 						displayList[i].slice.height){
1067 					displayList[i].slice = slice;
1068 				}
1069 				return displayList[i].slice;
1070 			}
1071 		}
1072 		return Coordinate(0,0,0,0);
1073 	}
1074 	///Returns the selected paletteID of the sprite.
1075 	public @nogc ushort getPaletteID(int n){
1076 		for(int i ; i < displayList.length ; i++){
1077 			if(displayList[i].priority == n){
1078 				return displayList[i].paletteSel;
1079 			}
1080 		}
1081 		return 0;
1082 	}
1083 	///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation
1084 	///Palette must exist in the parent Raster, otherwise AccessError might happen
1085 	public @nogc ushort setPaletteID(int n, ushort paletteID){
1086 		for(int i ; i < displayList.length ; i++){
1087 			if(displayList[i].priority == n){
1088 				if(displayList[i].wordLength == 4){
1089 					displayList[i].paletteSel = paletteID & 0x0F_FF;
1090 				}else if(displayList[i].wordLength == 8){
1091 					displayList[i].paletteSel = paletteID & 0x00_FF;
1092 				}
1093 				return displayList[i].paletteSel;
1094 			}
1095 		}
1096 		return 0;
1097 	}
1098 
1099 	public void addSprite(ABitmap s, int n, Coordinate c, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024){
1100 		import std.algorithm.sorting;
1101 		import std.algorithm.searching;
1102 		DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert);
1103 		if(canFind(displayList, d)){
1104 			throw new SpritePriorityException("Sprite number already exists!");
1105 		}else{
1106 			displayList ~= d;
1107 			displayList.sort!"a > b"();
1108 		}
1109 	}
1110 
1111 	public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024){
1112 		import std.algorithm.sorting;
1113 		import std.algorithm.searching;
1114 		Coordinate c = Coordinate(x,y,x+s.width,y+s.height);
1115 		DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert);
1116 		if(canFind(displayList, d)){
1117 			throw new SpritePriorityException("Sprite number already exists!");
1118 		}else{
1119 			displayList ~= d;
1120 			displayList.sort!"a > b"();
1121 		}
1122 	}
1123 	public void editSpriteAttribute(string S, T)(int n, T value){
1124 		for(int i; i < displayList.length ; i++){
1125 			if(displayList[i].priority == n){
1126 				mixin("displayList[i]."~S~" = value;");
1127 				return;
1128 			}
1129 		}
1130 	}
1131 	///Reads a sprite attribute
1132 	public T readSpriteAttribute(string S, T)(int n){
1133 		for(int i; i < displayList.length ; i++){
1134 			if(displayList[i].priority == n){
1135 				mixin("return displayList[i]."~S~";");
1136 			}
1137 		}
1138 		return T.init;
1139 	}
1140 	public void replaceSpriteAttribute(int n, BitmapAttrib attr){
1141 		for(int i; i < displayList.length ; i++){
1142 			if(displayList[i].priority == n){
1143 				displayList[i].attributes = attr;
1144 				return;
1145 			}
1146 		}
1147 	}
1148 	public void replaceSprite(ABitmap s, int n){
1149 		for(int i; i < displayList.length ; i++){
1150 			if(displayList[i].priority == n){
1151 				displayList[i].replaceSprite(s);
1152 				return;
1153 			}
1154 		}
1155 	}
1156 
1157 	public void replaceSprite(ABitmap s, int n, int x, int y){
1158 		for(int i; i < displayList.length ; i++){
1159 			if(displayList[i].priority == n){
1160 				displayList[i].replaceSprite(s);
1161 				return;
1162 			}
1163 		}
1164 	}
1165 
1166 	public void replaceSprite(ABitmap s, int n, Coordinate c){
1167 		for(int i; i < displayList.length ; i++){
1168 			if(displayList[i].priority == n){
1169 				displayList[i].replaceSprite(s);
1170 				return;
1171 			}
1172 		}
1173 	}
1174 
1175 	/*public ushort getTransparencyIndex(){
1176 		return transparencyIndex;
1177 	}*/
1178 
1179 	public void removeSprite(int n){
1180 		DisplayListItem[] ndl;
1181 		ndl.reserve(displayList.length);
1182 		for(int i; i < displayList.length ; i++){
1183 			if(displayList[i].priority != n){
1184 				ndl ~= displayList[i];
1185 			}
1186 		}
1187 		displayList = ndl;
1188 	}
1189 	public @nogc void moveSprite(int n, int x, int y){
1190 		for(int i; i < displayList.length ; i++){
1191 			if(displayList[i].priority == n){
1192 				displayList[i].position.move(x,y);
1193 				return;
1194 			}
1195 		}
1196 	}
1197 	public @nogc void relMoveSprite(int n, int x, int y){
1198 		for(int i; i < displayList.length ; i++){
1199 			if(displayList[i].priority == n){
1200 				displayList[i].position.relMove(x,y);
1201 				return;
1202 			}
1203 		}
1204 	}
1205 
1206 	public @nogc Coordinate getSpriteCoordinate(int n){
1207 		for(int i; i < displayList.length ; i++){
1208 			if(displayList[i].priority == n){
1209 				return displayList[i].position;
1210 			}
1211 		}
1212 		return Coordinate(0,0,0,0);
1213 	}
1214 	///Scales sprite horizontally. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found.
1215 	public @nogc int scaleSpriteHoriz(int n, int hScl){
1216 		if (!hScl) return -1;
1217 		for(int i; i < displayList.length ; i++){
1218 			if(displayList[i].priority == n){
1219 				displayList[i].scaleHoriz = hScl;
1220 				const int newWidth = cast(int)scaleNearestLength(displayList[i].width, hScl);
1221 				displayList[i].slice.right = newWidth;
1222 				return displayList[i].position.right = displayList[i].position.left + newWidth;
1223 			}
1224 		}
1225 		return -2;
1226 	}
1227 	///Scales sprite vertically. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found.
1228 	public @nogc int scaleSpriteVert(int n, int vScl){
1229 		if (!vScl) return -1;
1230 		for(int i; i < displayList.length ; i++){
1231 			if(displayList[i].priority == n){
1232 				displayList[i].scaleVert = vScl;
1233 				const int newHeight = cast(int)scaleNearestLength(displayList[i].height, vScl);
1234 				displayList[i].slice.bottom = newHeight;
1235 				return displayList[i].position.bottom = displayList[i].position.top + newHeight;
1236 			}
1237 		}
1238 		return -2;
1239 	}
1240 
1241 	public override @nogc void updateRaster(void* workpad, int pitch, Color* palette){
1242 		foreach(i ; displayList){
1243 			const int left = i.position.left + i.slice.left;
1244 			const int top = i.position.top + i.slice.top;
1245 			const int right = i.position.left + i.slice.right;
1246 			const int bottom = i.position.top + i.slice.bottom;
1247 			/+if((i.position.right > sX && i.position.bottom > sY) && (i.position.left < sX + rasterX && i.position.top < sY +
1248 					rasterY)){+/
1249 			if((right > sX && left < sX + rasterX) && (bottom > sY && top < sY + rasterY) && i.slice.width && i.slice.height){
1250 				int offsetXA = sX > left ? sX - left : 0;//Left hand side offset
1251 				int offsetXB = sX + rasterX < right ? right - rasterX : 0; //Right hand side offset
1252 				int offsetYA = sY > top ? sY - top : 0;
1253 				int offsetYB = sY + rasterY < bottom ? bottom - rasterY : 0;
1254 				//const int offsetYB0 = cast(int)scaleNearestLength(offsetYB, i.scaleVert);
1255 				const int sizeX = i.slice.width();
1256 				const int offsetX = left - sX;
1257 				int length = sizeX - offsetXA - offsetXB;
1258 				int lengthY = i.slice.height - offsetYA - offsetYB;
1259 				//const int lfour = length * 4;
1260 				const int offsetY = sY < top ? (top-sY)*pitch : 0;	//used if top portion of the sprite is off-screen
1261 				int offset, prevOffset;
1262 				const int scaleVertAbs = i.scaleVert * (i.scaleVert < 0 ? -1 : 1);
1263 				//int offset = offsetYA<<10 , prevOffset = (offsetYA*scaleVertAbs)>>10;
1264 				const int offsetYA0 = cast(int)(cast(double)offsetYA / (1024.0 / cast(double)scaleVertAbs));
1265 				const int sizeXOffset = i.width * (i.scaleVert < 0 ? -1 : 1);
1266 				switch(i.wordLength){
1267 					case 4:
1268 						ubyte* p0 = cast(ubyte*)i.pixelData + i.width * ((i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0)>>1);
1269 						size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB);
1270 						void* dest = workpad + (offsetX + offsetXA)*4 + offsetY;
1271 						for(int y = offsetYA ; y < i.slice.height - offsetYB ; y++){
1272 							horizontalScaleNearest4BitAndCLU(p0, src.ptr, palette + (i.paletteSel << 4), i.width, offsetXA & 1, i.scaleHoriz);
1273 							prevOffset += 1024;
1274 							for(; offset < prevOffset; offset += scaleVertAbs){
1275 								y++;
1276 								mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length);
1277 								dest += pitch;
1278 							}
1279 							p0 += sizeXOffset;
1280 						}
1281 						//}
1282 						break;
1283 					case 8:
1284 						ubyte* p0 = cast(ubyte*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0);
1285 						size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB);
1286 						void* dest = workpad + (offsetX + offsetXA)*4 + offsetY;
1287 						for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){
1288 							horizontalScaleNearestAndCLU(p0, src.ptr, palette + (i.paletteSel << 8), i.width, i.scaleHoriz);
1289 							prevOffset += 1024;
1290 							for(; offset < prevOffset; offset += scaleVertAbs){
1291 								y++;
1292 								mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length);
1293 								dest += pitch;
1294 							}
1295 							p0 += sizeXOffset;
1296 						}
1297 						break;
1298 					case 16:
1299 						ushort* p0 = cast(ushort*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0);
1300 						//ushort* p0 = cast(ushort*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - 1) : 0);
1301 						size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB);
1302 						void* dest = workpad + (offsetX + offsetXA)*4 + offsetY;
1303 						/*for( ; y < offsetYA ; ){
1304 							prevOffset += 1024;
1305 							for(; offset < prevOffset; offset += scaleVertAbs){
1306 								y++;
1307 							}
1308 							p0 += sizeXOffset;
1309 						}*/
1310 						for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){
1311 							horizontalScaleNearestAndCLU(p0, src.ptr, palette, i.width, i.scaleHoriz);
1312 							prevOffset += 1024;
1313 							for(; offset < prevOffset; offset += scaleVertAbs){
1314 								y++;
1315 								mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length);
1316 								dest += pitch;
1317 							}
1318 							p0 += sizeXOffset;
1319 						}
1320 						break;
1321 					case 32:
1322 						Color* p0 = cast(Color*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0);
1323 						size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB);
1324 						void* dest = workpad + (offsetX + offsetXA)*4 + offsetY;
1325 						for(int y = offsetYA ; y < i.slice.height - offsetYB ; y++){
1326 							horizontalScaleNearest(p0, src.ptr, i.width, i.scaleHoriz);
1327 							prevOffset += 1024;
1328 							for(; offset < prevOffset; offset += scaleVertAbs){
1329 								y++;
1330 								mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length);
1331 								dest += pitch;
1332 							}
1333 							p0 += sizeXOffset;
1334 						}
1335 						//}
1336 						break;
1337 					default:
1338 						break;
1339 				}
1340 
1341 			}
1342 		}
1343 		//foreach(int threadOffset; threads.parallel)
1344 			//free(src[threadOffset]);
1345 	}
1346 
1347 }
1348 /**
1349  * Puts various effects on the framebuffer (XOR blitter, etc).
1350  */
1351 public class EffectLayer : Layer{
1352 	/**
1353 	 * Stores various commands for effects
1354 	 */
1355 	public class EffectLayerCommand{
1356 		public CommandType command;
1357 		public Coordinate[] coordinates;
1358 		public Color[] colors;
1359 		public ushort[] indexedColors;
1360 		public int[] values;
1361 		public this(CommandType command, Coordinate[] coordinates, Color[] colors, int[] values = null){
1362 			this.command = command;
1363 			this.coordinates = coordinates;
1364 			this.indexedColors = null;
1365 			this.colors = colors;
1366 			this.values = values;
1367 		}
1368 		public this(CommandType command, Coordinate[] coordinates, ushort[] indexedColors, int[] values = null){
1369 			this.command = command;
1370 			this.coordinates = coordinates;
1371 			this.indexedColors = indexedColors;
1372 			this.colors = null;
1373 			this.values = values;
1374 		}
1375 	}
1376 	public enum CommandType : ubyte{
1377 		/// Does nothing, placeholder command.
1378 		NONE			=	0,
1379 		/**
1380 		 * Does a XOR blitter line. Parameters:
1381 		 * coordinate[0]: Begins the line from the top-left corner until the right corner. Bottom value is discarded.
1382 		 * color[0]: The 32 bit colorvector.
1383 		 */
1384 		XORBLITTERLINE	=	1,
1385 		/**
1386 		 * Does a XOR blitter box. Parameters:
1387 		 * coordinate[0]: The coordinates where the box should be drawn.
1388 		 * color[0]: The 32 bit colorvector.
1389 		 */
1390 		XORBLITTERBOX	=	2,
1391 		/**
1392 		 * Offsets a line by a given value. Parameters:
1393 		 * coordinate[0]: Begins the line from the top-left corner until the right corner. Bottom value is discarded.
1394 		 * value[0]: The amount which the line will be offsetted.
1395 		 *
1396 		 * NOTE: Be careful with this operation, if the algorithm has to write out from the screen, it'll cause a MemoryAccessViolationError.
1397 		 * Overscanning will enable to write outside of it as well as offsetting otherwise off-screen elements onto the screen.
1398 		 */
1399 		LINEOFFSET		=	3
1400 	}
1401 	private EffectLayerCommand[int] commandList;
1402 	private int[] commandListPriorities;
1403 	public this(){
1404 
1405 	}
1406 	/**
1407 	 * Adds a new command with the specified values.
1408 	 */
1409 	public void addCommand(int priority, EffectLayerCommand command){
1410 		import std.algorithm.sorting;
1411 		commandList[priority] = command;
1412 		commandListPriorities ~= priority;
1413 		commandListPriorities.sort();
1414 	}
1415 	/**
1416 	 * Removes a command at the specified priority.
1417 	 */
1418 	public void removeCommand(int priority){
1419 		commandList.remove(priority);
1420 		int[] newCommandListPriorities;
1421 		for(int i ; i < commandListPriorities.length ; i++){
1422 			if(commandListPriorities[i] != priority){
1423 				newCommandListPriorities ~= commandListPriorities[i];
1424 			}
1425 		}
1426 		commandListPriorities = newCommandListPriorities;
1427 	}
1428 
1429 	override public void updateRaster(void* workpad,int pitch,Color* palette) {
1430 		/*foreach(int i; commandListPriorities){
1431 			switch(commandList[i].command){
1432 				case CommandType.XORBLITTERLINE:
1433 					int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left;
1434 					if(commandList[i].indexedColors is null){
1435 						xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width());
1436 					}else{
1437 						xorBlitter(workpad + offset,palette[commandList[i].indexedColors[0]],commandList[i].coordinates[0].width());
1438 					}
1439 					break;
1440 				case CommandType.XORBLITTERBOX:
1441 					int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left;
1442 					if(commandList[i].indexedColors is null){
1443 						for(int y = commandList[i].coordinates[0].top; y < commandList[i].coordinates[0].bottom; y++){
1444 							xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width());
1445 							offset += pitch;
1446 						}
1447 					}else{
1448 						for(int y = commandList[i].coordinates[0].top; y < commandList[i].coordinates[0].bottom; y++){
1449 							xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width());
1450 							offset += pitch;
1451 						}
1452 					}
1453 					break;
1454 				case CommandType.LINEOFFSET:
1455 					int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left;
1456 					copyRegion(workpad + offset, workpad + offset + commandList[i].values[0], commandList[i].coordinates[0].width());
1457 					break;
1458 				default:
1459 					break;
1460 			}
1461 		}*/
1462 	}
1463 
1464 }
1465 unittest{
1466 	TransformableTileLayer test = new TransformableTileLayer();
1467 }