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  * 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
96  * colorspaces via proper lookup tables.
97  * Note for 4Bit bitmap: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an exception.
98  */
99 public class Bitmap(string S,T) : ABitmap {
100 	static if (S == "b") { 
101 		BitArray 			pixelAccess;
102 		protected size_t 	pitch;	///Total length of a line in bits
103 		bool				invertHoriz;	///Horizontal invertion for reading and writing
104 		bool				invertVert;		///Vertical invertion for reading anr writing
105 	}
106 	T[] 					pixels;
107 	static if(S != "HB" && S != "QB" && S != "b"){
108 		/**
109 		 * Unified CTOR to create empty bitmap.
110 		 */
111 		public this(int w, int h) @safe pure {
112 			_width = w;
113 			_height = h;
114 			pixels.length = w * h;
115 		}
116 		/**
117 		 * Unified CTOR tor create bitmap from preexisting data.
118 		 */
119 		public this(T[] src, int w, int h) @safe pure {
120 			_width = w;
121 			_height = h;
122 			pixels = src;
123 			if(pixels.length != w * h)
124 				throw new BitmapFormatException("Bitmap size mismatch!");
125 		}
126 		/**
127 		 * Resizes the bitmap.
128 		 * NOTE: It's not for scaling.
129 		 */
130 		public void resize(int x, int y) @safe pure {
131 			pixels.length=x*y;
132 			_width = x;
133 			_height = y;
134 		}
135 		///Returns the pixel at the given position.
136 		@nogc public T readPixel(int x, int y) @safe pure {
137 			return pixels[x+(_width*y)];
138 		}
139 		///Writes the pixel at the given position.
140 		@nogc public void writePixel(int x, int y, T color) @safe pure {
141 			pixels[x+(_width*y)]=color;
142 		}
143 		/**
144 		 * Returns a 2D slice (window) of the bitmap.
145 		 */
146 		public Bitmap!(S,T) window(int iX0, int iY0, int iX1, int iY1) @safe pure {
147 			T[] workpad;
148 			const int localWidth = (iX1 - iX0), localHeight = (iY1 - iY0);
149 			workpad.length  = localWidth * localHeight;
150 			for (int y ; y < localHeight ; y++) {
151 				for (int x ; x < localWidth ; x++) {
152 					workpad[x = (y * localWidth)] = pixels[iX0 + x + ((y + iY0) * _width)];
153 				}
154 			}
155 			return new Bitmap!(S,T)(workpad, localWidth, localHeight);
156 		}
157 	} else static if(S == "HB"){
158 		///Creates an empty bitmap. DEPRECATED!
159 		this(int x, int y, Color* palettePtr) @safe pure {
160 			if(x & 1)
161 				x++;
162 			_width=x;
163 			_height=y;
164 			pixels.length=(x*y)/2;
165 			this.palettePtr = palettePtr;
166 		}
167 		///Creates a bitmap from an array. DEPRECATED!
168 		this(ubyte[] p, int x, int y, Color* palettePtr){
169 			if (p.length/2 < x * y || x & 1)
170 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
171 			_width=x;
172 			_height=y;
173 			pixels=p;
174 			this.palettePtr = palettePtr;
175 		}
176 		///Creates an empty bitmap.
177 		this(int x, int y) @safe pure{
178 			if(x & 1)
179 				x++;
180 			_width=x;
181 			_height=y;
182 			pixels.length=(x*y)/2;
183 			//this.palettePtr = palettePtr;
184 		}
185 		///Creates a bitmap from an array.
186 		this(ubyte[] p, int x, int y) @safe pure{
187 			if (p.length/2 < x * y || x & 1)
188 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
189 			_width=x;
190 			_height=y;
191 			pixels=p;
192 			//this.palettePtr = palettePtr;
193 		}
194 		///Returns the pixel at the given position.
195 		@nogc public ubyte readPixel(int x, int y) @safe pure{
196 			if(x & 1)
197 				return pixels[x>>1+(_width*y)] & 0x0F;
198 			else
199 				return (pixels[x>>1+(_width*y)])>>4;
200 		}
201 		///Writes the pixel at the given position.
202 	    @nogc public void writePixel(int x, int y, ubyte color) @safe pure {
203 			if(x & 1){
204 				pixels[x+(_width*y)]&= 0xF0;
205 				pixels[x+(_width*y)]|= color;
206 			}else{
207 				pixels[x+(_width*y)]&= 0x0F;
208 				pixels[x+(_width*y)]|= color<<4;
209 			}
210 		}
211 		
212 		///Resizes the array behind the bitmap.
213 		public void resize(int x,int y) @safe pure {
214 			if(x & 1)
215 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
216 			pixels.length=x*y;
217 			_width = x;
218 			_height = y;
219 		}
220 
221 	} else static if(S == "b") {
222 		/**
223 		 * CTOR for 1 bit bitmaps with no preexisting source.
224 		 */
225 		public this(int w, int h) @trusted pure {
226 			_width = w;
227 			_height = h;
228 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
229 			pixels.length = pitch / (size_t.sizeof * 8);
230 			pixelAccess = BitArray(pixels, pitch * height);
231 		}
232 		/**
233 		 * CTOR to convert 8bit aligned bitmaps to 32/64bit ones.
234 		 */
235 		public this(ubyte[] src, int w, int h) @trusted pure {
236 			_width = w;
237 			_height = h;
238 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
239 			const size_t pitch0 = w + (8 - (w % 8));
240 			const size_t len = pitch / (size_t.sizeof * 8), len0 = pitch0 / 8;
241 			for (size_t i ; i < len0 * h; i+= len0) {
242 				ubyte[] workpad = src[i..i+len0];
243 				workpad.length = len;
244 				pixels ~= reinterpretCast!size_t(workpad);
245 			}
246 			pixelAccess = BitArray(pixels, pitch * height);
247 		}
248 		/**
249 		 * CTOR for 1 bit bitmaps with a preexisting source.
250 		 * Alignment and padding is for size_t (32 and 64 bit, on their respected systems)
251 		 */
252 		public this(size_t[] src, int w, int h) @trusted pure {
253 			_width = w;
254 			_height = h;
255 			pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8)));
256 			pixels = src;
257 			pixelAccess = BitArray(pixels, pitch * height);
258 		}
259 		///Returns the pixel at the given position.
260 		@nogc public bool readPixel(int x, int y) @trusted pure {
261 			return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)];
262 		}
263 		///Writes the pixel at the given position.
264 	    @nogc public bool writePixel(int x, int y, bool val) @trusted pure {
265 			return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)] = val;
266 		}
267 	}
268 	static if(S == "B" || S == "HW") {
269 		/**
270 		 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps.
271 		 */
272 		public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true) @safe pure{
273 			for(int i ; i < pixels.length ; i++){
274 				if(!(pixels[i] == 0 && keepZerothIndex)){
275 					pixels[i] += offset;
276 				}
277 			}
278 		}
279 	}
280 	static if(S == "W"){
281 		/**
282 		 * Generates a basic collision shape using
283 		 */
284 		public Bitmap1bit getBasicCollisionShape() @safe pure {
285 			return null;
286 		}
287 		override void clear() @nogc @safe pure nothrow {
288 			for(int i ; i < pixels.length ; i++){
289 				pixels[i] = Color(0x0);
290 			}
291 		}
292 	}else{
293 		/+override public AdvancedBitArray generateStandardCollisionModel(){	//REMOVE BY 0.10.0!
294 			AdvancedBitArray result = new AdvancedBitArray(_width * _height);
295 			for(int i ; i < _width * _height ; i++){
296 				T pixel = readpixel(i, 0);
297 				if(pixel != 0){
298 					result[i] = true;
299 				}
300 			}
301 			return result;
302 		}+/
303 		override void clear() @nogc @safe pure nothrow {
304 			for(int i ; i < pixels.length ; i++){
305 				pixels[i] = 0;
306 			}
307 		}
308 	}
309 	@nogc public T* getPtr() pure @trusted nothrow {
310 		return pixels.ptr;
311 	}
312 	override @nogc @property string wordLengthByString() @safe {
313 		return S;
314 	}
315 
316 }
317 
318 /**
319  * Defines Bitmap types
320  */
321 public enum BitmapTypes : ubyte {
322 	Undefined,			///Can be used for error checking, e.g. if a tile was initialized or not
323 	Bmp1Bit,
324 	Bmp2Bit,
325 	Bmp4Bit,
326 	Bmp8Bit,
327 	Bmp16Bit,
328 	Bmp32Bit,
329 	Planar,
330 }