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