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