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 import bitleveld.datatypes; 12 13 //public import pixelperfectengine.system.advBitArray; 14 public import pixelperfectengine.graphics.common; 15 16 /** 17 * Bitmap attributes, mainly for layers. 18 */ 19 public struct BitmapAttrib{ 20 mixin(bitfields!( 21 bool, "horizMirror", 1, 22 bool, "vertMirror", 1, 23 ubyte, "priority", 6)); 24 public this(bool horizMirror, bool vertMirror, ubyte priority = 0) @nogc nothrow @safe pure{ 25 this.horizMirror = horizMirror; 26 this.vertMirror = vertMirror; 27 this.priority = priority; 28 } 29 string toString() const @safe pure nothrow{ 30 return "[horizMirror: " ~ horizMirror ~ " ; vertMirror: " ~ vertMirror ~ " ; priority: " ~ priority ~ "]"; 31 } 32 } 33 34 /** 35 * Base bitmap functions, for enable the use of the same . 36 */ 37 abstract class ABitmap{ 38 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! 39 private int _width; 40 private int _height; 41 /** 42 * Returns the width of the bitmap. 43 */ 44 public int width() pure @safe @property @nogc nothrow const { 45 return _width; 46 } 47 /** 48 * Returns the height of the bitmap. 49 */ 50 public int height() pure @safe @property @nogc nothrow const { 51 return _height; 52 } 53 /** 54 * Returns the palette pointer. 55 * DEPRECATED! 56 */ 57 public deprecated Color* getPalettePtr() pure @trusted @property @nogc nothrow { 58 return palettePtr; 59 } 60 /** 61 * Sets the palette pointer. Make sure that you set it to a valid memory location. 62 * DEPRECATED! 63 */ 64 public deprecated void setPalettePtr(Color* p) pure @safe @property @nogc nothrow { 65 palettePtr = p; 66 } 67 /** 68 * Returns the wordlength of the type 69 */ 70 abstract string wordLengthByString() pure @safe @property @nogc nothrow const; 71 /** 72 * Clears the whole bitmap to a transparent color. 73 */ 74 abstract void clear() pure @safe @nogc nothrow; 75 } 76 /* 77 * S: Wordlength by usage. Possible values: 78 * - b: bit (for collision shapes) 79 * - QB: QuarterByte or 2Bit (currently unimplemented) 80 * - HB: HalfByte or 4Bit 81 * - B: Byte or 8Bit 82 * - HW: HalfWord or 16Bit 83 * - W: Word or 32Bit 84 * T: Type. Possible values: 85 * - size_t: used for bitarrays 86 * - ubyte: 8Bit or under 87 * - ushort: 16Bit 88 * - Color: 32Bit 89 */ 90 alias Bitmap1Bit = Bitmap!("b",size_t); 91 alias Bitmap2Bit = Bitmap!("QB",ubyte); 92 alias Bitmap4Bit = Bitmap!("HB",ubyte); 93 alias Bitmap8Bit = Bitmap!("B",ubyte); 94 alias Bitmap16Bit = Bitmap!("HW",ushort); 95 alias Bitmap32Bit = Bitmap!("W",Color); 96 /** 97 * Implements a bitmap with variable bit depth. Use the aliases to initialize them. 98 * 99 * Note on 16 bit bitmaps: It's using the master palette, It's not implementing any 16 bit RGB or RGBA color space 100 * directly. Can implement such 101 * colorspaces via proper lookup tables. 102 * 103 * Note on 4 bit bitmaps: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an 104 * exception. 105 * 106 * Note on 1 bit bitmaps: Uses size_t based paddings for more than one bit testing at the time in the future, through 107 * the use of logic functions. 108 */ 109 public class Bitmap(string S,T) : ABitmap { 110 protected size_t pitch; ///Total length of a line in bits 111 static if (S == "b") { 112 BitArray pixelAccess; 113 //bool invertHoriz; ///Horizontal invertion for reading and writing 114 //bool invertVert; ///Vertical invertion for reading anr writing 115 } else static if (S == "QB") { 116 QuadArray pixelAccess; 117 } else static if (S == "HB") { 118 NibbleArray pixelAccess; 119 } 120 /** 121 * Image data. 122 */ 123 T[] pixels; 124 static if(S != "HB" && S != "QB" && S != "b"){ 125 /** 126 * Unified CTOR to create empty bitmap. 127 */ 128 public this(int w, int h) @safe pure { 129 _width = w; 130 _height = h; 131 pixels.length = w * h; 132 } 133 /** 134 * Unified CTOR tor create bitmap from preexisting data. 135 */ 136 public this(T[] src, int w, int h) @safe pure { 137 _width = w; 138 _height = h; 139 pixels = src; 140 if(pixels.length != w * h) 141 throw new BitmapFormatException("Bitmap size mismatch!"); 142 } 143 /** 144 * Resizes the bitmap. 145 * NOTE: It's not for scaling. 146 */ 147 public void resize(int x, int y) @safe pure { 148 pixels.length=x*y; 149 _width = x; 150 _height = y; 151 } 152 ///Returns the pixel at the given position. 153 @nogc public T readPixel(int x, int y) @safe pure { 154 return pixels[x+(_width*y)]; 155 } 156 ///Writes the pixel at the given position. 157 @nogc public T writePixel(int x, int y, T color) @safe pure { 158 return pixels[x+(_width*y)]=color; 159 } 160 /** 161 * Returns a 2D slice (window) of the bitmap. 162 */ 163 public Bitmap!(S,T) window(int iX0, int iY0, int iX1, int iY1) @safe pure { 164 T[] workpad; 165 const int localWidth = (iX1 - iX0), localHeight = (iY1 - iY0); 166 workpad.length = localWidth * localHeight; 167 for (int y ; y < localHeight ; y++) { 168 for (int x ; x < localWidth ; x++) { 169 workpad[x = (y * localWidth)] = pixels[iX0 + x + ((y + iY0) * _width)]; 170 } 171 } 172 return new Bitmap!(S,T)(workpad, localWidth, localHeight); 173 } 174 } else static if(S == "HB"){ 175 176 ///Creates an empty bitmap. 177 this(int w, int h) @safe pure{ 178 if (w & 1) 179 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 180 _width=w; 181 _height=h; 182 pitch = _width / 2; 183 pixels.length = pitch * _height; 184 pixelAccess = NibbleArray(pixels, _width * _height); 185 } 186 ///Creates a bitmap from an array. 187 this(ubyte[] p, int w, int h) @safe pure{ 188 if (p.length * 2 != w * h || w & 1) 189 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 190 _width=w; 191 _height=h; 192 pitch = _width / 2; 193 pixels=p; 194 pixelAccess = NibbleArray(pixels, _width * _height); 195 } 196 ///Resizes the array behind the bitmap. 197 public void resize(int x,int y) @safe pure { 198 if(x & 1) 199 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 200 pixels.length = (x / 2) * y; 201 _width = x; 202 _height = y; 203 } 204 } else static if(S == "QB") { 205 ///Creates an empty bitmap. 206 this(int w, int h) @safe pure{ 207 if (w & 1) 208 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 209 _width=w; 210 _height=h; 211 pitch = _width / 2; 212 pixels.length = pitch * _height; 213 pixelAccess = QuadArray(pixels, _width * _height); 214 } 215 ///Creates a bitmap from an array. 216 this(ubyte[] p, int w, int h) @safe pure{ 217 if (p.length * 4 != w * h || w & 3) 218 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 219 _width=w; 220 _height=h; 221 pitch = _width / 2; 222 pixels=p; 223 pixelAccess = QuadArray(pixels, _width * _height); 224 } 225 } else static if(S == "b") { 226 /** 227 * CTOR for 1 bit bitmaps with no preexisting source. 228 */ 229 public this(int w, int h) @trusted pure { 230 _width = w; 231 _height = h; 232 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 233 pixels.length = pitch / (size_t.sizeof * 8) * h; 234 pixelAccess = BitArray(pixels, pitch * height); 235 } 236 /** 237 * CTOR to convert 8bit aligned bitmaps to 32/64bit ones. 238 */ 239 public this(ubyte[] src, int w, int h) @trusted pure { 240 _width = w; 241 _height = h; 242 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 243 const size_t pitch0 = w + (8 - (w % 8)); 244 const size_t len = pitch / (size_t.sizeof * 8), len0 = pitch0 / 8; 245 for (size_t i ; i < len0 * h; i+= len0) { 246 ubyte[] workpad = src[i..i+len0]; 247 workpad.length = len; 248 pixels ~= reinterpretCast!size_t(workpad); 249 } 250 pixelAccess = BitArray(pixels, pitch * height); 251 } 252 /** 253 * CTOR for 1 bit bitmaps with a preexisting source. 254 * Alignment and padding is for size_t (32 and 64 bit, on their respected systems) 255 */ 256 public this(size_t[] src, int w, int h) @trusted pure { 257 _width = w; 258 _height = h; 259 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 260 pixels = src; 261 pixelAccess = BitArray(pixels, pitch * height); 262 } 263 static if (size_t.sizeof == 8) { 264 static enum SHAM = 6; 265 static enum SHFLAG = 0x3F; 266 static enum BITAM = 64; 267 } else { 268 static enum SHAM = 5; 269 static enum SHFLAG = 0x1F; 270 static enum BITAM = 32; 271 } 272 /** 273 * Tests two collision models against each other. 274 * Params: 275 * line = The first line to test against on the left hand side collision model. 276 * lineAm = The amount of lines to be tested. 277 * other = The right hand side collision model. 278 * otherLine = The first line to test against on the right hand side collision model. 279 * offset = The amount, which the right hand side collision model's left edge is away from the left hand 280 * side collision model's left edge. 281 * overlapAm = The amount of overlap between the two objects. 282 * Returns: 283 */ 284 public bool testCollision(int line, int lineAm, Bitmap1Bit other, int otherLine, const int offset, 285 const int overlapAm) @safe @nogc nothrow pure const { 286 const int chOffset = offset>>SHAM; 287 assert(overlapAm, "This function should not be called if `overlapAm == 0`"); 288 assert(lineAm, "This function should not be called if `lineAm == 0`"); 289 for ( ; lineAm > 0 ; lineAm--, line++, otherLine++) { 290 int overlapCntr = overlapAm; 291 int loffset = offset; 292 int lchOffset = chOffset; 293 if (testChunkCollision(line, chOffset, other, otherLine, loffset)) 294 return true; 295 overlapCntr -= BITAM; 296 loffset += BITAM; 297 lchOffset++; 298 for ( ; overlapCntr > 0 ; overlapCntr -= BITAM, loffset += BITAM, lchOffset++) { 299 if (testChunkCollisionB(line, chOffset, other, otherLine, loffset)) 300 return true; 301 } 302 } 303 return false; 304 } 305 /** 306 * Tests a single chunk of pixels between two 1 bit bitmaps for collision, using a single chunk (size_t) of 307 * pixels. 308 * Params: 309 * line: The (first) line, which is being tested in the current object. 310 * other: The other object that this is being tested against. 311 * otherLine: The (first) line, which is being tested in the other object. 312 * offset: The horizontal offset of the other object to the right. 313 * Returns: True is collision has been detected. 314 */ 315 final protected bool testChunkCollision(int line, int chOffset, Bitmap1Bit other, int otherLine, int offset) 316 @safe @nogc nothrow pure const { 317 if (pixels[chOffset + (line * (pitch>>SHAM))] & 318 (other.pixels[(offset>>SHAM) + ((other.pitch>>SHAM) * otherLine)]<<(offset & SHFLAG))) 319 return true; 320 return false; 321 } 322 /** 323 * Tests a single chunk of pixels between two 1 bit bitmaps for collision, using a single chunk (size_t) of 324 * pixels, if other is wider than what size_t allows. 325 * Params: 326 * line: The (first) line, which is being tested in the current object. 327 * other: The other object that this is being tested against. 328 * otherLine: The (first) line, which is being tested in the other object. 329 * offset: The horizontal offset of the other object to the right. 330 * Returns: True is collision has been detected. 331 */ 332 final protected bool testChunkCollisionB(int line, int chOffset, Bitmap1Bit other, int otherLine, int offset) 333 @safe @nogc nothrow pure const { 334 if (pixels[chOffset + (line * (pitch>>SHAM))] & 335 ((other.pixels[(offset>>SHAM) + ((other.pitch>>SHAM) * otherLine)]<<(offset & SHFLAG)) | 336 (other.pixels[((offset>>SHAM) - 1) + ((other.pitch>>SHAM) * otherLine)]>>(SHFLAG - (offset & SHFLAG))))) 337 return true; 338 return false; 339 } 340 } 341 static if(S == "B" || S == "HW") { 342 /** 343 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps. 344 */ 345 public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true) @safe pure{ 346 for(int i ; i < pixels.length ; i++){ 347 if(!(pixels[i] == 0 && keepZerothIndex)){ 348 pixels[i] += offset; 349 } 350 } 351 } 352 } 353 static if (S == "b" || S == "QB" || S == "HB") { 354 /** 355 * Returns a 2D slice (window) of the bitmap. 356 */ 357 public Bitmap!(S,T) window(int iX0, int iY0, int iX1, int iY1) @safe pure { 358 const int localWidth = (iX1 - iX0), localHeight = (iY1 - iY0); 359 Bitmap!(S,T) result = new Bitmap!(S,T)(localWidth, localHeight); 360 for (int y ; y < localHeight ; y++) { 361 for (int x ; x < localWidth ; x++) { 362 result.writePixel(x, y, readPixel(iX0 + x, iY0 + y)); 363 } 364 } 365 return result; 366 } 367 } 368 static if (S == "QB" || S == "HB") { 369 ///Returns the pixel at the given position. 370 @nogc public T readPixel(int x, int y) @trusted pure { 371 assert (x >= 0 && x < _width && y >= 0 && y < _height); 372 return pixelAccess[x + (y * pitch)]; 373 } 374 ///Writes the pixel at the given position. 375 @nogc public T writePixel(int x, int y, T val) @trusted pure { 376 assert (x >= 0 && x < _width && y >= 0 && y < _height); 377 return pixelAccess[x + (y * pitch)] = val; 378 }} 379 static if(S == "W"){ 380 /** 381 * Clears the Bitmap 382 */ 383 override void clear() @nogc @safe pure nothrow { 384 for(int i ; i < pixels.length ; i++){ 385 pixels[i] = Color(0x0); 386 } 387 } 388 }else{ 389 override void clear() @nogc @safe pure nothrow { 390 for(int i ; i < pixels.length ; i++){ 391 pixels[i] = 0; 392 } 393 } 394 } 395 @nogc public T* getPtr() pure @trusted nothrow { 396 return pixels.ptr; 397 } 398 override string wordLengthByString() @safe @nogc pure nothrow @property const { 399 return S; 400 } 401 static if (S != "b") { 402 /** 403 * Generates a standard collision model by checking against a transparency value (default vaule is T.init). 404 */ 405 Bitmap1Bit generateStandardCollisionModel(const T transparency = T.init) { 406 Bitmap1Bit output = new Bitmap1Bit(width, height); 407 for (int y ; y < height ; y++) { 408 for (int x ; x < width ; x++) { 409 output.writePixel(x, y, readPixel(x, y) != transparency); 410 } 411 } 412 return output; 413 } 414 } else { 415 ///Returns the pixel at the given position. 416 @nogc public bool readPixel(int x, int y) @trusted pure { 417 assert (x >= 0 && x < _width && y >= 0 && y < _height); 418 return pixelAccess[x + (y * pitch)]; 419 } 420 ///Writes the pixel at the given position. 421 @nogc public bool writePixel(int x, int y, bool val) @trusted pure { 422 assert (x >= 0 && x < _width && y >= 0 && y < _height); 423 return pixelAccess[x + (y * pitch)] = val; 424 } 425 } 426 } 427 428 /** 429 * Defines Bitmap types 430 */ 431 public enum BitmapTypes : ubyte { 432 Undefined, ///Can be used for error checking, e.g. if a tile was initialized or not 433 Bmp1Bit, 434 Bmp2Bit, 435 Bmp4Bit, 436 Bmp8Bit, 437 Bmp16Bit, 438 Bmp32Bit, 439 Planar, ///Mainly used as a placeholder 440 }