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 }