1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, graphics.bitmap module
5  */
6 
7 module PixelPerfectEngine.graphics.bitmap;
8 import std.bitmanip;
9 import PixelPerfectEngine.system.exc;
10 public import PixelPerfectEngine.system.advBitArray;
11 public import PixelPerfectEngine.graphics.common;
12 
13 /**
14  * Bitmap attributes, mainly for layers.
15  */
16 public struct BitmapAttrib{
17 	mixin(bitfields!(
18 		bool, "horizMirror", 1,
19 		bool, "vertMirror", 1,
20 		ubyte, "priority", 6));
21 	@nogc public this(bool horizMirror, bool vertMirror, ubyte priority = 0){
22 		this.horizMirror = horizMirror;
23 		this.vertMirror = vertMirror;
24 		this.priority = priority;
25 	}
26 	string toString() const{
27 		return "[horizMirror: " ~ horizMirror ~ " ; vertMirror: " ~ vertMirror ~ " ; priority: " ~ priority ~ "]";
28 	}
29 }
30 
31 /**
32  * Base bitmap functions, for enable the use of the same .
33  */
34 abstract class ABitmap{
35 	private Color* palettePtr;		///Set this to either a portion of the master palette or to a self-defined place. Not used in 32 bit bitmaps. DEPRECATED!
36     private int iX;
37     private int iY;
38 	/**
39 	 * Returns the width of the bitmap.
40 	 */
41 	public int width() pure @safe @property @nogc {
42 		return iX;
43 	}
44 	/**
45 	 * Returns the height of the bitmap.
46 	 */
47 	public int height() pure @safe @property @nogc {
48 		return iY;
49 	}
50 	abstract AdvancedBitArray generateStandardCollisionModel();
51 	/**
52 	 * Returns the palette pointer.
53 	 */
54 	@nogc public Color* getPalettePtr() {
55 		return palettePtr;
56 	}
57 	/**
58 	 * Sets the palette pointer. Make sure that you set it to a valid memory location.
59 	 * DEPRECATED!
60 	 */
61 	@nogc public void setPalettePtr(Color* p) {
62 		palettePtr = p;
63 	}
64 	/**
65 	 * Returns the wordlength of the type
66 	 */
67 	abstract @nogc @property string wordLengthByString();
68 	/**
69 	 * Clears the whole bitmap to a transparent color.
70 	 */
71 	abstract @nogc void clear();
72 }
73 /*
74  * S: Wordlength by usage. Possible values:
75  * - b: bit (for collision shapes) (currently unimplemented)
76  * - QB: QuarterByte or 2Bit (currently unimplemented)
77  * - HB: HalfByte or 4Bit
78  * - B: Byte or 8Bit
79  * - HW: HalfWord or 16Bit
80  * - W: Word or 32Bit
81  * T: Type. Possible values:
82  * - bool: 1Bit (for collision shapes) (currently unimplemented)
83  * - ubyte: 8Bit or under
84  * - ushort: 16Bit
85  * - Color: 32Bit
86  */
87 alias Bitmap4Bit = Bitmap!("HB",ubyte);
88 alias Bitmap8Bit = Bitmap!("B",ubyte);
89 alias Bitmap16Bit = Bitmap!("HW",ushort);
90 alias Bitmap32Bit = Bitmap!("W",Color);
91 /**
92  * Implements a bitmap with variable bit depth. Use the aliases to initialize them.
93  * Note for 16Bit bitmap: It's using the master palette, It's not implementing any 16 bit RGB or RGBA color space directly. Can implement such
94  * colorspaces via proper lookup tables.
95  * Note for 4Bit bitmap: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an exception.
96  */
97 public class Bitmap(string S,T) : ABitmap{
98 	T[] pixels;
99 	static if(S != "HB" && S != "QB"){
100 		/**
101 		 * Resizes the bitmap.
102 		 * NOTE: It's not for scaling.
103 		 */
104 		public void resize(int x, int y) @safe pure{
105 			pixels.length=x*y;
106 			iX = x;
107 			iY = y;
108 		}
109 		///Returns the pixel at the given position.
110 		@nogc public T readPixel(int x, int y) @safe pure{
111 			return pixels[x+(iX*y)];
112 		}
113 		///Writes the pixel at the given position.
114 		@nogc public void writePixel(int x, int y, T color) @safe pure{
115 			pixels[x+(iX*y)]=color;
116 		}
117 	}
118 	static if(S == "HB"){
119 		///Creates an empty bitmap. DEPRECATED!
120 		this(int x, int y, Color* palettePtr){
121 			if(x & 1)
122 				x++;
123 			iX=x;
124 			iY=y;
125 			pixels.length=(x*y)/2;
126 			this.palettePtr = palettePtr;
127 		}
128 		///Creates a bitmap from an array. DEPRECATED!
129 		this(ubyte[] p, int x, int y, Color* palettePtr){
130 			if (p.length/2 < x * y || x & 1)
131 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
132 			iX=x;
133 			iY=y;
134 			pixels=p;
135 			this.palettePtr = palettePtr;
136 		}
137 		///Creates an empty bitmap.
138 		this(int x, int y) @safe pure{
139 			if(x & 1)
140 				x++;
141 			iX=x;
142 			iY=y;
143 			pixels.length=(x*y)/2;
144 			//this.palettePtr = palettePtr;
145 		}
146 		///Creates a bitmap from an array.
147 		this(ubyte[] p, int x, int y) @safe pure{
148 			if (p.length/2 < x * y || x & 1)
149 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
150 			iX=x;
151 			iY=y;
152 			pixels=p;
153 			//this.palettePtr = palettePtr;
154 		}
155 		///Returns the pixel at the given position.
156 		@nogc public ubyte readPixel(int x, int y) @safe pure{
157 			if(x & 1)
158 				return pixels[x>>1+(iX*y)] & 0x0F;
159 			else
160 				return (pixels[x>>1+(iX*y)])>>4;
161 		}
162 		///Writes the pixel at the given position.
163 	    @nogc public void writePixel(int x, int y, ubyte color) @safe pure{
164 			if(x & 1){
165 				pixels[x+(iX*y)]&= 0xF0;
166 				pixels[x+(iX*y)]|= color;
167 			}else{
168 				pixels[x+(iX*y)]&= 0x0F;
169 				pixels[x+(iX*y)]|= color<<4;
170 			}
171 		}
172 		///Resizes the array behind the bitmap.
173 		public void resize(int x,int y) @safe pure{
174 			if(x & 1)
175 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
176 			pixels.length=x*y;
177 			iX = x;
178 			iY = y;
179 		}
180 
181 	}else static if(S == "B"){
182 		///Creates an empty bitmap. DEPRECATED!
183 		this(int x, int y, Color* palettePtr){
184 			if(x < 0 || y < 0)
185 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
186 			iX=x;
187 			iY=y;
188 			pixels.length=x*y;
189 			this.palettePtr = palettePtr;
190 		}
191 		///Creates a bitmap from an array. DEPRECATED!
192 		this(ubyte[] p, int x, int y, Color* palettePtr){
193 			if (p.length < x * y)
194 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
195 			iX=x;
196 			iY=y;
197 			pixels=p;
198 			this.palettePtr = palettePtr;
199 		}
200 		///Creates an empty bitmap.
201 		this(int x, int y) @safe pure{
202 			if(x < 0 || y < 0)
203 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
204 			iX=x;
205 			iY=y;
206 			pixels.length=x*y;
207 			//this.palettePtr = palettePtr;
208 		}
209 		///Creates a bitmap from an array.
210 		this(ubyte[] p, int x, int y) @safe pure{
211 			if (p.length < x * y)
212 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
213 			iX=x;
214 			iY=y;
215 			pixels=p;
216 			//this.palettePtr = palettePtr;
217 		}
218 	}else static if(S == "HW"){
219 		///Creates an empty bitmap.
220 		this(int x, int y) @safe pure{
221 			if(x < 0 || y < 0)
222 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
223 			iX=x;
224 			iY=y;
225 			pixels.length=x*y;
226 		}
227 		///Creates a bitmap from an array.
228 		this(ushort[] p, int x, int y) @safe pure{
229 			if (p.length < x * y)
230 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
231 			iX=x;
232 			iY=y;
233 			pixels=p;
234 		}
235 	}else static if(S == "W"){
236 		///Creates an empty bitmap.
237 		public this(int x, int y) @safe pure{
238 			if(x < 0 || y < 0)
239 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
240 			iX = x;
241 			iY = y;
242 			pixels.length = x * y;
243 		}
244 		///Creates a bitmap from an array.
245 		public this(Color[] p, int x, int y) @safe pure{
246 			if (p.length < x * y)
247 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
248 			iX = x;
249 			iY = y;
250 			this.pixels = p;
251 		}
252 	}else static assert("Template argument \"" ~ bitmapType ~ "\" not supported!");
253 	static if(S == "B" || S == "HW"){
254 		/**
255 		 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps.
256 		 */
257 		public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true) @safe pure{
258 			for(int i ; i < pixels.length ; i++){
259 				if(!(pixels[i] == 0 && keepZerothIndex)){
260 					pixels[i] += offset;
261 				}
262 			}
263 		}
264 	}
265 	static if(S == "W"){
266 		override public AdvancedBitArray generateStandardCollisionModel(){
267 			AdvancedBitArray result = new AdvancedBitArray(iX * iY);
268 			for(int i ; i < iX * iY ; i++){
269 				Color pixel = readPixel(i, 0);
270 				if(pixel.alpha != 0){
271 					result[i] = true;
272 				}
273 			}
274 			return result;
275 		}
276 		override @nogc void clear(){
277 			for(int i ; i < pixels.length ; i++){
278 				pixels[i] = Color(0x0);
279 			}
280 		}
281 	}else{
282 		override public AdvancedBitArray generateStandardCollisionModel(){
283 			AdvancedBitArray result = new AdvancedBitArray(iX * iY);
284 			for(int i ; i < iX * iY ; i++){
285 				T pixel = readPixel(i, 0);
286 				if(pixel != 0){
287 					result[i] = true;
288 				}
289 			}
290 			return result;
291 		}
292 		override @nogc void clear(){
293 			for(int i ; i < pixels.length ; i++){
294 				pixels[i] = 0;
295 			}
296 		}
297 	}
298 	@nogc public T* getPtr() pure {
299 		return pixels.ptr;
300 	}
301 	override @nogc @property string wordLengthByString() {
302 		return S;
303 	}
304 
305 }
306 
307 
308 /**
309  * Bypasses the transparency on color no.0, no.1 is set to red.
310  */
311 /*static const Color[16] errorPalette;
312 
313 public ABitmap generateDummyBitmap(int x, int y, wchar c){
314 	Color* p = errorPalette.ptr;
315 	Bitmap4Bit result = new Bitmap4Bit(x, y, p);
316 
317 	return result;
318 }*/