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