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.
36     private int iX;
37     private int iY;
38 	/**
39 	 * Returns the width of the bitmap.
40 	 */
41 	@property @nogc public int width(){
42 		return iX;
43 	}
44 	/**
45 	 * Returns the height of the bitmap.
46 	 */
47 	@property @nogc public int height(){
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 	 */
60 	@nogc public void setPalettePtr(Color* p){
61 		palettePtr = p;
62 	}
63 	/**
64 	 * Returns the wordlength of the type
65 	 */
66 	abstract @nogc @property string wordLengthByString();
67 	/**
68 	 * Clears the whole bitmap to a transparent color.
69 	 */
70 	abstract @nogc void clear();
71 }
72 /*
73  * S: Wordlength by usage. Possible values:
74  * - b: bit (for collision shapes)
75  * - QB: QuadByte or 2Bit (currently unimplemented)
76  * - HB: HalfByte or 4Bit
77  * - B: Byte or 8Bit
78  * - HW: HalfWord or 16Bit
79  * - W: Word or 32Bit
80  * T: Type. Possible values:
81  * - bool: 1Bit (for collision shapes)
82  * - ubyte: 8Bit or under
83  * - ushort: 16Bit
84  * - Color: 32Bit
85  */
86 alias Bitmap4Bit = Bitmap!("HB",ubyte);
87 alias Bitmap8Bit = Bitmap!("B",ubyte);
88 alias Bitmap16Bit = Bitmap!("HW",ushort);
89 alias Bitmap32Bit = Bitmap!("W",Color);
90 /**
91  * Implements a bitmap with variable bit depth. Use the aliases to initialize them.
92  * 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
93  * colorspaces via proper lookup tables.
94  * Note for 4Bit bitmap: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an exception.
95  */
96 public class Bitmap(string S,T) : ABitmap{
97 	T[] pixels;
98 	static if(S != "HB" && S != "QB"){
99 		/**
100 		 * Resizes the bitmap.
101 		 * NOTE: It's not for scaling.
102 		 */
103 		public void resize(int x, int y){
104 			pixels.length=x*y;
105 			iX = x;
106 			iY = y;
107 		}
108 		///Returns the pixel at the given position.
109 		@nogc public T readPixel(int x, int y){
110 			return pixels[x+(iX*y)];
111 		}
112 		///Writes the pixel at the given position.
113 		@nogc public void writePixel(int x, int y, T color){
114 			pixels[x+(iX*y)]=color;
115 		}
116 	}
117 	static if(S == "HB"){
118 		///Creates an empty bitmap.
119 		this(int x, int y, Color* palettePtr = null){
120 			if(x & 1)
121 				x++;
122 			iX=x;
123 			iY=y;
124 			pixels.length=(x*y)/2;
125 			this.palettePtr = palettePtr;
126 		}
127 		///Creates a bitmap from an array.
128 		this(ubyte[] p, int x, int y, Color* palettePtr = null){
129 			if (p.length/2 < x * y || x & 1)
130 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
131 			iX=x;
132 			iY=y;
133 			pixels=p;
134 			this.palettePtr = palettePtr;
135 		}
136 		///Returns the pixel at the given position.
137 		@nogc public ubyte readPixel(int x, int y){
138 			if(x & 1)
139 				return pixels[x>>1+(iX*y)] & 0x0F;
140 			else
141 				return (pixels[x>>1+(iX*y)])>>4;
142 		}
143 		///Writes the pixel at the given position.
144 	    @nogc public void writePixel(int x, int y, ubyte color){
145 			if(x & 1){
146 				pixels[x+(iX*y)]&= 0xF0;
147 				pixels[x+(iX*y)]|= color;
148 			}else{
149 				pixels[x+(iX*y)]&= 0x0F;
150 				pixels[x+(iX*y)]|= color<<4;
151 			}
152 		}
153 		///Resizes the array behind the bitmap.
154 		public void resize(int x,int y){
155 			if(x & 1)
156 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
157 			pixels.length=x*y;
158 			iX = x;
159 			iY = y;
160 		}
161 
162 	}else static if(S == "B"){
163 		///Creates an empty bitmap.
164 		this(int x, int y, Color* palettePtr = null){
165 			if(x < 0 || y < 0)
166 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
167 			iX=x;
168 			iY=y;
169 			pixels.length=x*y;
170 			this.palettePtr = palettePtr;
171 		}
172 		///Creates a bitmap from an array.
173 		this(ubyte[] p, int x, int y, Color* palettePtr = null){
174 			if (p.length < x * y)
175 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
176 			iX=x;
177 			iY=y;
178 			pixels=p;
179 			this.palettePtr = palettePtr;
180 		}
181 	}else static if(S == "HW"){
182 		///Creates an empty bitmap.
183 		this(int x, int y){
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 		}
190 		///Creates a bitmap from an array.
191 		this(ushort[] p, int x, int y){
192 			if (p.length < x * y)
193 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
194 			iX=x;
195 			iY=y;
196 			pixels=p;
197 		}
198 	}else static if(S == "W"){
199 		///Creates an empty bitmap.
200 		public this(int x, int y){
201 			if(x < 0 || y < 0)
202 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
203 			iX = x;
204 			iY = y;
205 			pixels.length = x * y;
206 		}
207 		///Creates a bitmap from an array.
208 		public this(Color[] p, int x, int y){
209 			if (p.length < x * y)
210 				throw new BitmapFormatException("Incorrect Bitmap size exception!");
211 			iX = x;
212 			iY = y;
213 			this.pixels = p;
214 		}
215 	}else static assert("Template argument \"" ~ bitmapType ~ "\" not supported!");
216 	static if(S == "B" || S == "HW"){
217 		/**
218 		 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps.
219 		 */
220 		public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true){
221 			for(int i ; i < pixels.length ; i++){
222 				if(!(pixels[i] == 0 && keepZerothIndex)){
223 					pixels[i] += offset;
224 				}
225 			}
226 		}
227 	}
228 	static if(S == "W"){
229 		override public AdvancedBitArray generateStandardCollisionModel(){
230 			AdvancedBitArray result = new AdvancedBitArray(iX * iY);
231 			for(int i ; i < iX * iY ; i++){
232 				Color pixel = readPixel(i, 0);
233 				if(pixel.alpha != 0){
234 					result[i] = true;
235 				}
236 			}
237 			return result;
238 		}
239 		override @nogc void clear(){
240 			for(int i ; i < pixels.length ; i++){
241 				pixels[i] = Color(0x0);
242 			}
243 		}
244 	}else{
245 		override public AdvancedBitArray generateStandardCollisionModel(){
246 			AdvancedBitArray result = new AdvancedBitArray(iX * iY);
247 			for(int i ; i < iX * iY ; i++){
248 				T pixel = readPixel(i, 0);
249 				if(pixel != 0){
250 					result[i] = true;
251 				}
252 			}
253 			return result;
254 		}
255 		override @nogc void clear(){
256 			for(int i ; i < pixels.length ; i++){
257 				pixels[i] = 0;
258 			}
259 		}
260 	}
261 	@nogc public T* getPtr() pure{
262 		return pixels.ptr;
263 	}
264 	override @nogc @property string wordLengthByString() {
265 		return S;
266 	}
267 
268 }
269 
270 
271 /**
272  * Bypasses the transparency on color no.0, no.1 is set to red.
273  */
274 /*static const Color[16] errorPalette;
275 
276 public ABitmap generateDummyBitmap(int x, int y, wchar c){
277 	Color* p = errorPalette.ptr;
278 	Bitmap4Bit result = new Bitmap4Bit(x, y, p);
279 
280 	return result;
281 }*/