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 import bitleveld.reinterpret;
11 
12 //public import pixelperfectengine.system.advBitArray;
13 public import pixelperfectengine.graphics.common;
14 
15 /**
16  * Bitmap attributes, mainly for layers.
17  */
18 public struct BitmapAttrib{
19 	mixin(bitfields!(
20 		bool, "horizMirror", 1,
21 		bool, "vertMirror", 1,
22 		ubyte, "priority", 6));
23 	public this(bool horizMirror, bool vertMirror, ubyte priority = 0) @nogc nothrow @safe pure{
24 		this.horizMirror = horizMirror;
25 		this.vertMirror = vertMirror;
26 		this.priority = priority;
27 	}
28 	string toString() const @safe pure nothrow{
29 		return "[horizMirror: " ~ horizMirror ~ " ; vertMirror: " ~ vertMirror ~ " ; priority: " ~ priority ~ "]";
30 	}
31 }
32 
33 /**
34  * Base bitmap functions, for enable the use of the same .
35  */
36 abstract class ABitmap{
37 	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!
38     private int _width;
39     private int _height;
40 	/**
41 	 * Returns the width of the bitmap.
42 	 */
43 	public int width() pure @safe @property @nogc nothrow {
44 		return _width;
45 	}
46 	/**
47 	 * Returns the height of the bitmap.
48 	 */
49 	public int height() pure @safe @property @nogc nothrow {
50 		return _height;
51 	}
52 	/**
53 	 * Returns the palette pointer.
54 	 */
55 	public Color* getPalettePtr() pure @trusted @property @nogc nothrow {
56 		return palettePtr;
57 	}
58 	/**
59 	 * Sets the palette pointer. Make sure that you set it to a valid memory location.
60 	 * DEPRECATED!
61 	 */
62 	public void setPalettePtr(Color* p) pure @safe @property @nogc nothrow {
63 		palettePtr = p;
64 	}
65 	/**
66 	 * Returns the wordlength of the type
67 	 */
68 	abstract string wordLengthByString() pure @safe @property @nogc nothrow ;
69 	/**
70 	 * Clears the whole bitmap to a transparent color.
71 	 */
72 	abstract void clear() pure @safe @nogc nothrow;
73 }
74 /*
75  * S: Wordlength by usage. Possible values:
76  * - b: bit (for collision shapes)
77  * - QB: QuarterByte or 2Bit (currently unimplemented)
78  * - HB: HalfByte or 4Bit
79  * - B: Byte or 8Bit
80  * - HW: HalfWord or 16Bit
81  * - W: Word or 32Bit
82  * T: Type. Possible values:
83  * - size_t: used for bitarrays
84  * - ubyte: 8Bit or under
85  * - ushort: 16Bit
86  * - Color: 32Bit
87  */
88 alias Bitmap1bit = Bitmap!("b",size_t);
89 alias Bitmap4Bit = Bitmap!("HB",ubyte);
90 alias Bitmap8Bit = Bitmap!("B",ubyte);
91 alias Bitmap16Bit = Bitmap!("HW",ushort);
92 alias Bitmap32Bit = Bitmap!("W",Color);
93 /**
94  * Implements a bitmap with variable bit depth. Use the aliases to initialize them.
95  *
96  * Note on 16 bit bitmaps: It's using the master palette, It's not implementing any 16 bit RGB or RGBA color space 
97  * directly. Can implement such
98  * colorspaces via proper lookup tables.
99  *
100  * Note on 4 bit bitmaps: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an 
101  * exception.
102  *
103  * Note on 1 bit bitmaps: Uses size_t based paddings for more than one bit testing at the time in the future, through
104  * the use of logic functions.
105  */
106 public class Bitmap(string S,T) : ABitmap {
107 	static if (S == "b") { 
108 		BitArray 			pixelAccess;
109 		protected size_t 	pitch;	///Total length of a line in bits
110 		bool				invertHoriz;	///Horizontal invertion for reading and writing
111 		bool				invertVert;		///Vertical invertion for reading anr writing
112 	}
113 	/**
114 	 * Image data.
115 	 */
116 	T[] 					pixels;
117 	static if(S != "HB" && S != "QB" && S != "b"){
118 		/**
119 		 * Unified CTOR to create empty bitmap.
120 		 */
121 		public this(int w, int h) @safe pure {
122 			_width = w;
123 			_height = h;
124 			pixels.length = w * h;
125 		}
126 		/**
127 		 * Unified CTOR tor create bitmap from preexisting data.
128 		 */
129 		public this(T[] src, int w, int h) @safe pure {
130 			_width = w;
131 			_height = h;
132 			pixels = src;
133 			if(pixels.length != w * h)
134 				throw new BitmapFormatException("Bitmap size mismatch!");
135 		}
136 		/**
137 		 * Resizes the bitmap.
138 		 * NOTE: It's not for scaling.
139 		 */
140 		public void resize(int x, int y) @safe pure {
141 			pixels.length=x*y;
142 			_width = x;
143 			_height = y;
144 		}
145 		///Returns the pixel at the given position.
146 		@nogc public T readPixel(int x, int y) @safe pure {
147 			return pixels[x+(_width*y)];
148 		}
149 		///Writes the pixel at the given position.
150 		@nogc public void writePixel(int x, int y, T color) @safe pure {
151 			pixels[x+(_width*y)]=color;
152 		}
153 		/**
154 		 * Returns a 2D slice (window) of the bitmap.
155 		 */
156 		public Bitmap!(S,T) window(int iX0, int iY0, int iX1, int iY1) @safe pure {
157 			T[] workpad;
158 			const int localWidth = (iX1 - iX0), localHeight = (iY1 - iY0);
159 			workpad.length  = localWidth * localHeight;
160 			for (int y ; y < localHeight ; y++) {
161 				for (int x ; x < localWidth ; x++) {
162 					workpad[x = (y * localWidth)] = pixels[iX0 + x + ((y + iY0) * _width)];
163 				}
164 			}
165 			return new Bitmap!(S,T)(workpad, localWidth, localHeight);
166 		}
167 	} else static if(S == "HB"){
168 		
169 		///Creates an empty bitmap.
170 		this(int x, int y) @safe pure{
171 			if(x & 1)
172 				x++;
173 			_width=x;
174 			_height=y;
175 			pixels.length=(x*y)/2;
176 			//this.palettePtr = palettePtr;
177 		}
178 		///Creates a bitmap from an array.
179 		this(ubyte[] p, int x, int y) @safe pure{
180 			if (p.length/2 < x * y || x & 1)
181 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
182 			_width=x;
183 			_height=y;
184 			pixels=p;
185 			//this.palettePtr = palettePtr;
186 		}
187 		///Returns the pixel at the given position.
188 		@nogc public ubyte readPixel(int x, int y) @safe pure{
189 			if(x & 1)
190 				return pixels[x>>1+(_width*y)] & 0x0F;
191 			else
192 				return (pixels[x>>1+(_width*y)])>>4;
193 		}
194 		///Writes the pixel at the given position.
195 	    @nogc public void writePixel(int x, int y, ubyte color) @safe pure {
196 			if(x & 1){
197 				pixels[x+(_width*y)]&= 0xF0;
198 				pixels[x+(_width*y)]|= color;
199 			}else{
200 				pixels[x+(_width*y)]&= 0x0F;
201 				pixels[x+(_width*y)]|= color<<4;
202 			}
203 		}
204 		
205 		///Resizes the array behind the bitmap.
206 		public void resize(int x,int y) @safe pure {
207 			if(x & 1)
208 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
209 			pixels.length=x*y;
210 			_width = x;
211 			_height = y;
212 		}
213 
214 	} else static if(S == "b") {
215 		/**
216 		 * CTOR for 1 bit bitmaps with no preexisting source.
217 		 */
218 		public this(int w, int h) @trusted pure {
219 			_width = w;
220 			_height = h;
221 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
222 			pixels.length = pitch / (size_t.sizeof * 8) * h;
223 			pixelAccess = BitArray(pixels, pitch * height);
224 		}
225 		/**
226 		 * CTOR to convert 8bit aligned bitmaps to 32/64bit ones.
227 		 */
228 		public this(ubyte[] src, int w, int h) @trusted pure {
229 			_width = w;
230 			_height = h;
231 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
232 			const size_t pitch0 = w + (8 - (w % 8));
233 			const size_t len = pitch / (size_t.sizeof * 8), len0 = pitch0 / 8;
234 			for (size_t i ; i < len0 * h; i+= len0) {
235 				ubyte[] workpad = src[i..i+len0];
236 				workpad.length = len;
237 				pixels ~= reinterpretCast!size_t(workpad);
238 			}
239 			pixelAccess = BitArray(pixels, pitch * height);
240 		}
241 		/**
242 		 * CTOR for 1 bit bitmaps with a preexisting source.
243 		 * Alignment and padding is for size_t (32 and 64 bit, on their respected systems)
244 		 */
245 		public this(size_t[] src, int w, int h) @trusted pure {
246 			_width = w;
247 			_height = h;
248 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
249 			pixels = src;
250 			pixelAccess = BitArray(pixels, pitch * height);
251 		}
252 		///Returns the pixel at the given position.
253 		@nogc public bool readPixel(int x, int y) @trusted pure {
254 			assert (x >= 0 && x < _width && y >= 0 && y < _height);
255 			return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)];
256 		}
257 		///Writes the pixel at the given position.
258 	    @nogc public bool writePixel(int x, int y, bool val) @trusted pure {
259 			assert (x >= 0 && x < _width && y >= 0 && y < _height);
260 			return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)] = val;
261 		}
262 		/**
263 		 * Tests a single line of pixels between two 1 bit bitmaps for collision, using a single chunk of pixels. (Unimplemented, is a placeholder as of now)
264 		 * * line: The (first) line, which is being tested in the current object.
265 		 * * other: The other object that this is being tested against.
266 		 * * otherLine: The (first) line, which is being tested in the other object.
267 		 * * offset: The horizontal offset of the other object to the right. If negative, then it's being offsetted to the left.
268 		 * * nOfLines: The number of lines to be tested. Must be non-zero, otherwise the test won't run.
269 		 */
270 		final public bool testLineCollision(int line, Bitmap1bit other, int otherLine, const int offset, uint nOfLines = 1) 
271 				@safe @nogc nothrow pure const {
272 			for ( ; nOfLines > 0 ; nOfLines--) {
273 
274 			}
275 			return false;
276 		}
277 	}
278 	static if(S == "B" || S == "HW") {
279 		/**
280 		 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps.
281 		 */
282 		public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true) @safe pure{
283 			for(int i ; i < pixels.length ; i++){
284 				if(!(pixels[i] == 0 && keepZerothIndex)){
285 					pixels[i] += offset;
286 				}
287 			}
288 		}
289 	}
290 	static if(S == "W"){
291 		/**
292 		 * Clears the Bitmap
293 		 */
294 		override void clear() @nogc @safe pure nothrow {
295 			for(int i ; i < pixels.length ; i++){
296 				pixels[i] = Color(0x0);
297 			}
298 		}
299 	}else{
300 		/+override public AdvancedBitArray generateStandardCollisionModel(){	//REMOVE BY 0.10.0!
301 			AdvancedBitArray result = new AdvancedBitArray(_width * _height);
302 			for(int i ; i < _width * _height ; i++){
303 				T pixel = readpixel(i, 0);
304 				if(pixel != 0){
305 					result[i] = true;
306 				}
307 			}
308 			return result;
309 		}+/
310 		override void clear() @nogc @safe pure nothrow {
311 			for(int i ; i < pixels.length ; i++){
312 				pixels[i] = 0;
313 			}
314 		}
315 	}
316 	@nogc public T* getPtr() pure @trusted nothrow {
317 		return pixels.ptr;
318 	}
319 	override @nogc @property string wordLengthByString() @safe {
320 		return S;
321 	}
322 	static if (S != "b" || S != "W") {
323 		/**
324 		 * Generates a standard collision model by checking against a transparency value (default vaule is T.init).
325 		 */
326 		Bitmap1bit generateStandardCollisionModel(const T transparency = T.init) {
327 			Bitmap1bit output = new Bitmap1bit(width, height);
328 			for (int y ; y < height ; y++) {
329 				for (int x ; x < width ; x++) {
330 					output.writePixel(x, y, readPixel(x, y) != transparency);
331 				}
332 			}
333 			return output;
334 		}
335 	}
336 }
337 
338 /**
339  * Defines Bitmap types
340  */
341 public enum BitmapTypes : ubyte {
342 	Undefined,			///Can be used for error checking, e.g. if a tile was initialized or not
343 	Bmp1Bit,
344 	Bmp2Bit,
345 	Bmp4Bit,
346 	Bmp8Bit,
347 	Bmp16Bit,
348 	Bmp32Bit,
349 	Planar,				///Mainly used as a placeholder
350 }