1 /*
2  * Copyright (C) 2015-2020, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, graphics.layers.trnstilelayer module
5  */
6 
7 module pixelperfectengine.graphics.layers.trnstilelayer;
8 
9 public import pixelperfectengine.graphics.layers.base;
10 import pixelperfectengine.system.etc;
11 import pixelperfectengine.system.exc;
12 import collections.treemap;
13 import inteli.emmintrin;
14 
15 /**
16  * Implements a modified TileLayer with transformability with capabilities similar to MODE7.
17  * <br/>
18  * Transform function:
19  * [x',y'] = ([A,B,C,D] * ([x,y] + [sX,sY] - [x_0,y_0])>>>8 + [x_0,y_0]
20  * <br/>
21  * All basic transform values are integer based, 256 equals with 1.0
22  * <br/>
23  * Restrictions compared to standard TileLayer:
24  * <ul>
25  * <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>
26  * <li>In future versions, map sizes for this layer will be restricted to power of two sizes to make things faster</li>
27  * <li>Maximum layer size in pixels are restricted to 65536*65536 due to architectural limitations. Accelerated versions might raise
28  * this limitation.</li>
29  * </ul>
30  * HDMA emulation supported through delegate hBlankInterrupt.
31  */
32 public class TransformableTileLayer(BMPType = Bitmap16Bit, int TileX = 8, int TileY = 8) : Layer, ITileLayer, ITTL{
33 		/*if(isPowerOf2(TileX) && isPowerOf2(TileY))*/
34 	protected struct DisplayListItem {
35 		BMPType	tile;		///For reference counting
36 		void* 	pixelSrc;	///Used for quicker access to the Data
37 		wchar 	ID;			///ID, mainly used as a padding and secondary identification
38 		/**
39 		 * Sets the maximum accessable color amount by the bitmap.
40 		 * By default, for 4 bit bitmaps, it's 4, and it enables 256 * 16 color palettes.
41 		 * This limitation is due to the way how the MappingElement struct works.
42 		 * 8 bit bitmaps can assess the full 256 * 256 palette space.
43 		 * Lower values can be described to avoid wasting palettes space in cases when the
44 		 * bitmaps wouldn't use their full capability.
45 		 * Not used with 16 bit indexed and 32 bit direct color bitmaps.
46 		 */
47 		ubyte	palShift;
48 		ubyte 	reserved;	///Padding for 32 bit
49 		this (wchar ID, BMPType tile, ubyte paletteSh = 0) pure @trusted @nogc nothrow {
50 			void _systemWrapper() pure @system @nogc nothrow {
51 				pixelSrc = cast(void*)tile.getPtr();
52 			}
53 			this.ID = ID;
54 			this.tile = tile;
55 			static if (is(BMPType == Bitmap4Bit)) this.palShift = paletteSh ? paletteSh : 4;
56 			else static if (is(BMPType == Bitmap8Bit)) this.palShift = paletteSh ? paletteSh : 8;
57 			_systemWrapper;
58 		}
59 		string toString() const {
60 			import std.conv : to;
61 			string result = to!string(cast(ushort)ID) ~ " ; " ~ to!string(pixelSrc);
62 			return result;
63 		}
64 	}
65 	alias DisplayList = TreeMap!(wchar, DisplayListItem, true);
66 	protected DisplayList displayList;
67 	protected short[4] transformPoints;	/** Defines how the layer is being transformed */
68 	protected short[2] tpOrigin;		/** Transform point */
69 	protected Bitmap32Bit backbuffer;	///used to store current screen output
70 	/*static if(BMPType.mangleof == Bitmap8Bit.mangleof){
71 		protected ubyte[] src;
72 		protected Color* palettePtr;	///Shared palette
73 	}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
74 		protected ushort[] src;*/
75 	static if(is(BMPType == Bitmap4Bit) || is(BMPType == Bitmap8Bit) ||	is(BMPType == Bitmap16Bit)){
76 		protected ushort[] src;
77 	}else static if(is(BMPType == Bitmap32Bit)){
78 
79 	}else static assert(false,"Template parameter " ~ BMPType.mangleof ~ " not supported by TransformableTileLayer!");
80 	//TO DO: Replace these with a single 32 bit value
81 	protected bool		needsUpdate;	///Set to true if backbuffer needs an update (might be replaced with flags)
82 	public WarpMode		warpMode;		///Repeats the whole layer if set to true
83 	public ubyte		masterVal;		///Sets the master alpha value for the layer
84 	public ushort		paletteOffset;	///Offsets the palette by the given amount
85 	protected int mX, mY;				///"Inherited" from TileLayer
86 	static if(TileX == 8)
87 		protected immutable int shiftX = 3;
88 	else static if(TileX == 16)
89 		protected immutable int shiftX = 4;
90 	else static if(TileX == 32)
91 		protected immutable int shiftX = 5;
92 	else static if(TileX == 64)
93 		protected immutable int shiftX = 6;
94 	else static assert(false,"Unsupported horizontal tile size!");
95 	static if(TileY == 8)
96 		protected immutable int shiftY = 3;
97 	else static if(TileY == 16)
98 		protected immutable int shiftY = 4;
99 	else static if(TileY == 32)
100 		protected immutable int shiftY = 5;
101 	else static if(TileY == 64)
102 		protected immutable int shiftY = 6;
103 	else static assert(false,"Unsupported vertical tile size!");
104 	protected int totalX, totalY;
105 	protected MappingElement[] mapping;
106 	
107 	protected int4 _tileAmpersand;      ///Used for quick modulo by power of two to calculate tile positions.
108 	protected short8 _increment;
109 	protected __m128i _mapAmpersand;    ///Used for quick modulo by power of two to read maps
110 	//protected __m128i _mapShift;		///Used for quick divide by power of two to read maps
111 	
112 	alias HBIDelegate = 
113 			@nogc nothrow void delegate(ref short[4] localABCD, ref short[2] localsXsY, ref short[2] localx0y0, short y);
114 	/**
115 	 * Called before each line being redrawn. Can modify global values for each lines.
116 	 */
117 	public HBIDelegate hBlankInterrupt;
118 
119 	this (RenderingMode renderMode = RenderingMode.AlphaBlend) {
120 		A = 256;
121 		B = 0;
122 		C = 0;
123 		D = 256;
124 		x_0 = 0;
125 		y_0 = 0;
126 		_tileAmpersand = [TileX - 1, TileY - 1, TileX - 1, TileY - 1];
127 		//_mapShift = [shiftX, shiftY, shiftX, shiftY];
128 		masterVal = ubyte.max;
129 		setRenderingMode(renderMode);
130 		needsUpdate = true;
131 		//static if (BMPType.mangleof == Bitmap4Bit.mangleof) _paletteOffset = 4;
132 		//else static if (BMPType.mangleof == Bitmap8Bit.mangleof) _paletteOffset = 8;
133 		for(int i ; i < 8 ; i+=2)
134 			_increment[i] = 2;
135 	}
136 
137 
138 	
139 	override public void setRasterizer(int rX,int rY) {
140 		super.setRasterizer(rX,rY);
141 		backbuffer = new Bitmap32Bit(rX, rY);
142 		static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof){
143 			src.length = rX;
144 		}
145 	}
146 	public override LayerType getLayerType() @nogc @safe pure nothrow const {
147 		return LayerType.TransformableTile;
148 	}
149 	override public @nogc void updateRaster(void* workpad,int pitch,Color* palette) {
150 		//import core.stdc.stdio;
151 		if(needsUpdate){
152 			needsUpdate = false;
153 			//clear buffer
154 			//backbuffer.clear();
155 			Color* dest = backbuffer.getPtr();
156 			short[2] sXsY = [cast(short)sX,cast(short)sY];
157 			short[4] localTP = transformPoints;
158 			short[2] localTPO = tpOrigin;
159 			//write new data into it
160 			for(short y; y < rasterY; y++){
161 				if(hBlankInterrupt !is null){
162 					hBlankInterrupt(localTP, sXsY, localTPO, y);
163 				}
164 				short8 _sXsY, _localTP, _localTPO;
165 				for(int i; i < 8; i++){
166 					_sXsY[i] = sXsY[i & 1];
167 					_localTP[i] = localTP[i & 3];
168 					_localTPO[i] = localTPO[i & 1];
169 				}
170 				short8 xy_in;
171 				for(int i = 1; i < 8; i += 2){
172 					xy_in[i] = y;
173 				}
174 				xy_in[4] = 1;
175 				xy_in[6] = 1;
176 				int4 _localTPO_0;
177 				for(int i; i < 4; i++){
178 					_localTPO_0[i] = localTPO[i & 1];
179 				}
180 				for(short x; x < rasterX; x++){
181 					int4 xy = _mm_srai_epi32(_mm_madd_epi16(cast(int4)_localTP, cast(int4)(xy_in + _sXsY - _localTPO)),8)
182 							+ _localTPO_0;
183 					/+MappingElement currentTile0 = tileByPixelWithoutTransform(xy[0],xy[1]),
184 							currentTile[1] = tileByPixelWithoutTransform(xy[2],xy[3]);+/
185 					MappingElement[2] currentTile = tileByPixelWithoutTransform(xy);
186 					xy &= _tileAmpersand;
187 					if(currentTile[0].tileID != 0xFFFF){
188 						const DisplayListItem d = displayList[currentTile[0].tileID];
189 						static if(is(BMPType == Bitmap4Bit)){
190 							ubyte* tsrc = cast(ubyte*)d.pixelSrc;
191 						}else static if(is(BMPType == Bitmap8Bit)){
192 							ubyte* tsrc = cast(ubyte*)d.pixelSrc;
193 						}else static if(is(BMPType == Bitmap16Bit)){
194 							ushort* tsrc = cast(ushort*)d.pixelSrc;
195 						}else static if(is(BMPType == Bitmap32Bit)){
196 							Color* tsrc = cast(Color*)d.pixelSrc;
197 						}
198 						xy[0] = xy[0] & (TileX - 1);
199 						xy[1] = xy[1] & (TileY - 1);
200 						const int totalOffset = xy[0] + xy[1] * TileX;
201 						static if(BMPType.mangleof == Bitmap4Bit.mangleof){
202 							src[x] = cast(ushort)((totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | 
203 									currentTile[0].paletteSel<<d.palShift);
204 						}else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){
205 							src[x] = cast(ushort)(tsrc[totalOffset] | currentTile[0].paletteSel<<d.palShift);
206 						}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
207 							src[x] = tsrc[totalOffset];
208 						}else{
209 							*dest = *tsrc;
210 							dest++;
211 						}
212 					}else{
213 						static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
214 								BMPType.mangleof == Bitmap4Bit.mangleof){
215 							src[x] = 0;
216 						}else{
217 							(*dest).raw = 0;
218 						}
219 					}
220 					x++;
221 					if(currentTile[1].tileID != 0xFFFF){
222 						const DisplayListItem d = displayList[currentTile[1].tileID];
223 						static if(is(BMPType == Bitmap4Bit)){
224 							ubyte* tsrc = cast(ubyte*)d.pixelSrc;
225 						}else static if(is(BMPType == Bitmap8Bit)){
226 							ubyte* tsrc = cast(ubyte*)d.pixelSrc;
227 						}else static if(is(BMPType == Bitmap16Bit)){
228 							ushort* tsrc = cast(ushort*)d.pixelSrc;
229 						}else static if(is(BMPType == Bitmap32Bit)){
230 							Color* tsrc = cast(Color*)d.pixelSrc;
231 						}
232 						xy[2] = xy[2] & (TileX - 1);
233 						xy[3] = xy[3] & (TileY - 1);
234 						const int totalOffset = xy[2] + xy[3] * TileX;
235 						static if(BMPType.mangleof == Bitmap4Bit.mangleof){
236 							src[x] = cast(ushort)((totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | 
237 									currentTile[1].paletteSel<<d.palShift);
238 						} else static if(BMPType.mangleof == Bitmap8Bit.mangleof ) {
239 							src[x] = cast(ushort)(tsrc[totalOffset] | currentTile[1].paletteSel<<d.palShift);
240 						} else static if(BMPType.mangleof == Bitmap16Bit.mangleof) {
241 							src[x] = tsrc[totalOffset];
242 						} else {
243 							*dest = *tsrc;
244 							dest++;
245 						}
246 					} else {
247 						static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
248 								BMPType.mangleof == Bitmap4Bit.mangleof){
249 							src[x] = 0;
250 						} else {
251 							(*dest).raw = 0;
252 						}
253 					}
254 					xy_in += _increment;
255 
256 				}
257 				/+else {
258 					for(short x; x < rasterX; x++){
259 						int[2] xy = transformFunctionInt([x,y], localTP, localTPO, sXsY);
260 						//printf("[%i,%i]",xy[0],xy[1]);
261 						MappingElement currentTile = tileByPixelWithoutTransform(xy[0],xy[1]);
262 						if(currentTile.tileID != 0xFFFF){
263 							const DisplayListItem d = displayList[currentTile.tileID];
264 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
265 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
266 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof){
267 								ubyte* tsrc = cast(ubyte*)d.pixelSrc;
268 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
269 								ushort* tsrc = cast(ushort*)d.pixelSrc;
270 							}else static if(BMPType.mangleof == Bitmap32Bit.mangleof){
271 								Color* tsrc = cast(Color*)d.pixelSrc;
272 							}
273 							xy[0] = xy[0] & (TileX - 1);
274 							xy[1] = xy[1] & (TileY - 1);
275 							const int totalOffset = xy[0] + xy[1] * TileX;
276 							static if(BMPType.mangleof == Bitmap4Bit.mangleof){
277 								src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile.paletteSel<<_paletteOffset;
278 							}else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){
279 								src[x] = tsrc[totalOffset] | currentTile.paletteSel<<_paletteOffset;
280 							}else static if(BMPType.mangleof == Bitmap16Bit.mangleof){
281 								src[x] = tsrc[totalOffset];
282 							}else{
283 								*dest = *tsrc;
284 								dest++;
285 							}
286 						}else{
287 							static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof ||
288 									BMPType.mangleof == Bitmap4Bit.mangleof){
289 								src[x] = 0;
290 							}else{
291 								(*dest).raw = 0;
292 							}
293 						}
294 					}
295 				}+/
296 				static if(BMPType.mangleof == Bitmap4Bit.mangleof || BMPType.mangleof == Bitmap8Bit.mangleof || 
297 						BMPType.mangleof == Bitmap16Bit.mangleof){
298 					mainColorLookupFunction(src.ptr, cast(uint*)dest, cast(uint*)palette + paletteOffset, rasterX);
299 					dest += rasterX;
300 				}
301 			}
302 		}
303 		//render surface onto the raster
304 		void* p0 = workpad;
305 		Color* c = backbuffer.getPtr();
306 		for(int y; y < rasterY; y++){
307 			mainRenderingFunction(cast(uint*)c, cast(uint*)p0, rasterX, masterVal);
308 			c += rasterX;
309 			p0 += pitch;
310 		}
311 
312 	}
313 	///Returns which tile is at the given pixel
314 	public MappingElement tileByPixel(int x, int y) @nogc @safe pure nothrow const {
315 		//static if (USE_INTEL_INTRINSICS) {
316 		//	return MappingElement.init;
317 		//} else {
318 		int[2] xy = transformFunctionInt([cast(short)x,cast(short)y],transformPoints,tpOrigin,[cast(short)sX,cast(short)sY]);
319 		return tileByPixelWithoutTransform(xy[0],xy[1]);
320 		//}
321 		
322 	}
323 	///Returns which tile is at the given pixel.
324 	final protected MappingElement tileByPixelWithoutTransform(int x, int y) @nogc @safe pure nothrow const {
325 		x >>>= shiftX;
326 		y >>>= shiftY;
327 		final switch (warpMode) with (WarpMode) {
328 			case Off:
329 				if (x >= mX || y >= mY || x < 0 || y < 0) return MappingElement(0xFFFF);
330 				break;
331 			case MapRepeat:
332 				x &= _mapAmpersand[0];
333 				y &= _mapAmpersand[1];
334 				break;
335 			case TileRepeat:
336 				if (x >= mX || y >= mY || x < 0 || y < 0) return MappingElement(0x0000);
337 				break;
338 		}
339 		return mapping[x + y * mX];
340 	}
341 	///Returns two tiles to speed up rendering.
342 	final protected MappingElement[2] tileByPixelWithoutTransform(__m128i params) @nogc @safe pure nothrow const {
343 		//params >>>= mapShift;
344 		params[0] >>= shiftX;
345 		params[2] >>= shiftX;
346 		params[1] >>= shiftY;
347 		params[3] >>= shiftY;
348 		MappingElement[2] result;
349 		final switch (warpMode) with (WarpMode) {
350 			case Off:
351 				if (params[0] >= mX || params[1] >= mY || params[0] < 0 || params[1] < 0) result[0] = MappingElement(0xFFFF);
352 				else result[0] = mapping[params[0] + params[1] * mX];
353 				if (params[2] >= mX || params[3] >= mY || params[2] < 0 || params[3] < 0) result[1] = MappingElement(0xFFFF);
354 				else result[1] = mapping[params[2] + params[3] * mX];
355 				return result;
356 			case MapRepeat:
357 				params &= _mapAmpersand;
358 				result[0] = mapping[params[0] + params[1] * mX];
359 				result[1] = mapping[params[2] + params[3] * mX];
360 				return result;
361 			case TileRepeat:
362 				if (params[0] >= mX || params[1] >= mY || params[0] < 0 || params[1] < 0) result[0] = MappingElement(0x0000);
363 				else result[0] = mapping[params[0] + params[1] * mX];
364 				if (params[2] >= mX || params[3] >= mY || params[2] < 0 || params[3] < 0) result[1] = MappingElement(0x0000);
365 				else result[1] = mapping[params[2] + params[3] * mX];
366 				return result;
367 		}
368 	}
369 	/**
370 	 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out.
371 	 */
372 	public @property short A() @nogc @safe nothrow pure const {
373 		return transformPoints[0];
374 	}
375 	/**
376 	 * Horizontal shearing.
377 	 */
378 	public @property short B() @nogc @safe nothrow pure const {
379 		return transformPoints[1];
380 	}
381 	/**
382 	 * Vertical shearing.
383 	 */
384 	public @property short C() @nogc @safe nothrow pure const {
385 		return transformPoints[2];
386 	}
387 	/**
388 	 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out.
389 	 */
390 	public @property short D() @nogc @safe nothrow pure const {
391 		return transformPoints[3];
392 	}
393 	/**
394 	 * Horizontal transformation offset.
395 	 */
396 	public @property short x_0() @nogc @safe nothrow pure const {
397 		return tpOrigin[0];
398 	}
399 	/**
400 	 * Vertical transformation offset.
401 	 */
402 	public @property short y_0() @nogc @safe nothrow pure const {
403 		return tpOrigin[1];
404 	}
405 	/**
406 	 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out.
407 	 */
408 	public @property short A(short newval) @nogc @safe nothrow pure {
409 		transformPoints[0] = newval;
410 		needsUpdate = true;
411 		return transformPoints[0];
412 	}
413 	/**
414 	 * Horizontal shearing.
415 	 */
416 	public @property short B(short newval) @nogc @safe nothrow pure {
417 		transformPoints[1] = newval;
418 		needsUpdate = true;
419 		return transformPoints[1];
420 	}
421 	/**
422 	 * Vertical shearing.
423 	 */
424 	public @property short C(short newval) @nogc @safe nothrow pure {
425 		transformPoints[2] = newval;
426 		needsUpdate = true;
427 		return transformPoints[2];
428 	}
429 	/**
430 	 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out.
431 	 */
432 	public @property short D(short newval) @nogc @safe nothrow pure {
433 		transformPoints[3] = newval;
434 		needsUpdate = true;
435 		return transformPoints[3];
436 	}
437 	/**
438 	 * Horizontal transformation offset.
439 	 */
440 	public @property short x_0(short newval) @nogc @safe nothrow pure {
441 		tpOrigin[0] = newval;
442 		//tpOrigin[2] = newval;
443 		needsUpdate = true;
444 		return tpOrigin[0];
445 	}
446 	/**
447 	 * Vertical transformation offset.
448 	 */
449 	public @property short y_0(short newval) @nogc @safe nothrow pure {
450 		tpOrigin[1] = newval;
451 		//tpOrigin[3] = newval;
452 		needsUpdate = true;
453 		return tpOrigin[1];
454 	}
455 	override public void scroll(int x,int y) {
456 		super.scroll(x,y);
457 		needsUpdate = true;
458 	}
459 	override public void relScroll(int x,int y) {
460 		super.relScroll(x,y);
461 		needsUpdate = true;
462 	}
463 	public MappingElement[] getMapping() @nogc @safe pure nothrow {
464 		return mapping;
465 	}
466 	/+public MappingElement readMapping(int x, int y) @nogc @safe pure nothrow const {
467 		return mapping[(y * mX) + x];
468 	}+/
469 	public int getTileWidth() @nogc @safe pure nothrow const {
470 		return TileX;
471 	}
472 	public int getTileHeight() @nogc @safe pure nothrow const {
473 		return TileY;
474 	}
475 	public int getMX() @nogc @safe pure nothrow const {
476 		return mX;
477 	}
478 	public int getMY() @nogc @safe pure nothrow const {
479 		return mY;
480 	}
481 	public size_t getTX() @nogc @safe pure nothrow const {
482 		return totalX;
483 	}
484 	public size_t getTY() @nogc @safe pure nothrow const {
485 		return totalY;
486 	}
487 	/// Sets the warp mode.
488 	/// Returns the new warp mode that is being used.
489 	public WarpMode setWarpMode(WarpMode mode) @nogc @safe pure nothrow {
490 		return warpMode = mode;
491 	}
492 	/// Returns the currently used warp mode.
493 	public WarpMode getWarpMode() @nogc @safe pure nothrow const {
494 		return warpMode;
495 	}
496 	///Gets the the ID of the given element from the mapping. x , y : Position.
497 	public MappingElement readMapping(int x, int y) @nogc @safe pure nothrow const {
498 		if(!warpMode){
499 			if(x < 0 || y < 0 || x >= mX || y >= mY){
500 				return MappingElement(0xFFFF);
501 			}
502 		}else{
503 			x = x % mX;
504 			y = y % mY;
505 		}
506 		return mapping[x+(mX*y)];
507 	}
508 	///Writes to the map. x , y : Position. w : ID of the tile.
509 	@nogc public void writeMapping(int x, int y, MappingElement w) {
510 		mapping[x+(mX*y)]=w;
511 	}
512 	public void addTile(ABitmap tile, wchar id, ubyte paletteSh = 0) {
513 		if(typeid(tile) !is typeid(BMPType)){
514 			throw new TileFormatException("Incorrect type of tile!");
515 		}
516 		if(tile.width == TileX && tile.height == TileY){
517 			displayList[id] = DisplayListItem(id, cast(BMPType)tile, paletteSh);
518 		}else{
519 			throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null);
520 		}
521 	}
522 	///Returns a tile from the displaylist
523 	public ABitmap getTile(wchar id) {
524 		return displayList[id].tile;
525 	}
526 	///Removes the tile with the ID from the set.
527 	public void removeTile(wchar id){
528 		displayList.remove(id);
529 	}
530 	///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map.
531 	///x*y=map.length
532 	public void loadMapping(int x, int y, MappingElement[] mapping) {
533 		if (!isPowerOf2(x) || !isPowerOf2(y)) 
534 			throw new MapFormatException("Map sizes are not power of two!");
535 		if (x * y != mapping.length) 
536 			throw new MapFormatException("Incorrect map sizes!");
537 		mX=x;
538 		mY=y;
539 		_mapAmpersand[0] = x - 1;
540 		_mapAmpersand[1] = y - 1;
541 		_mapAmpersand[2] = x - 1;
542 		_mapAmpersand[3] = y - 1;
543 		this.mapping = mapping;
544 		totalX=mX*TileX;
545 		totalY=mY*TileY;
546 	}
547 	public void clearTilemap() @nogc @safe pure nothrow {
548 		for (size_t i ; i < mapping.length ; i++) {
549 			mapping[i] = MappingElement.init;
550 		}
551 	}
552 }