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 }*/