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 * 96 * Note on 16 bit bitmaps: It's using the master palette, It's not implementing any 16 bit RGB or RGBA color space 97 * directly. Can implement such 98 * colorspaces via proper lookup tables. 99 * 100 * Note on 4 bit bitmaps: It's width needs to be an even number (for rendering simplicity), otherwise it'll cause an 101 * exception. 102 * 103 * Note on 1 bit bitmaps: Uses size_t based paddings for more than one bit testing at the time in the future, through 104 * the use of logic functions. 105 */ 106 public class Bitmap(string S,T) : ABitmap { 107 static if (S == "b") { 108 BitArray pixelAccess; 109 protected size_t pitch; ///Total length of a line in bits 110 bool invertHoriz; ///Horizontal invertion for reading and writing 111 bool invertVert; ///Vertical invertion for reading anr writing 112 } 113 /** 114 * Image data. 115 */ 116 T[] pixels; 117 static if(S != "HB" && S != "QB" && S != "b"){ 118 /** 119 * Unified CTOR to create empty bitmap. 120 */ 121 public this(int w, int h) @safe pure { 122 _width = w; 123 _height = h; 124 pixels.length = w * h; 125 } 126 /** 127 * Unified CTOR tor create bitmap from preexisting data. 128 */ 129 public this(T[] src, int w, int h) @safe pure { 130 _width = w; 131 _height = h; 132 pixels = src; 133 if(pixels.length != w * h) 134 throw new BitmapFormatException("Bitmap size mismatch!"); 135 } 136 /** 137 * Resizes the bitmap. 138 * NOTE: It's not for scaling. 139 */ 140 public void resize(int x, int y) @safe pure { 141 pixels.length=x*y; 142 _width = x; 143 _height = y; 144 } 145 ///Returns the pixel at the given position. 146 @nogc public T readPixel(int x, int y) @safe pure { 147 return pixels[x+(_width*y)]; 148 } 149 ///Writes the pixel at the given position. 150 @nogc public void writePixel(int x, int y, T color) @safe pure { 151 pixels[x+(_width*y)]=color; 152 } 153 /** 154 * Returns a 2D slice (window) of the bitmap. 155 */ 156 public Bitmap!(S,T) window(int iX0, int iY0, int iX1, int iY1) @safe pure { 157 T[] workpad; 158 const int localWidth = (iX1 - iX0), localHeight = (iY1 - iY0); 159 workpad.length = localWidth * localHeight; 160 for (int y ; y < localHeight ; y++) { 161 for (int x ; x < localWidth ; x++) { 162 workpad[x = (y * localWidth)] = pixels[iX0 + x + ((y + iY0) * _width)]; 163 } 164 } 165 return new Bitmap!(S,T)(workpad, localWidth, localHeight); 166 } 167 } else static if(S == "HB"){ 168 169 ///Creates an empty bitmap. 170 this(int x, int y) @safe pure{ 171 if(x & 1) 172 x++; 173 _width=x; 174 _height=y; 175 pixels.length=(x*y)/2; 176 //this.palettePtr = palettePtr; 177 } 178 ///Creates a bitmap from an array. 179 this(ubyte[] p, int x, int y) @safe pure{ 180 if (p.length/2 < x * y || x & 1) 181 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 182 _width=x; 183 _height=y; 184 pixels=p; 185 //this.palettePtr = palettePtr; 186 } 187 ///Returns the pixel at the given position. 188 @nogc public ubyte readPixel(int x, int y) @safe pure{ 189 if(x & 1) 190 return pixels[x>>1+(_width*y)] & 0x0F; 191 else 192 return (pixels[x>>1+(_width*y)])>>4; 193 } 194 ///Writes the pixel at the given position. 195 @nogc public void writePixel(int x, int y, ubyte color) @safe pure { 196 if(x & 1){ 197 pixels[x+(_width*y)]&= 0xF0; 198 pixels[x+(_width*y)]|= color; 199 }else{ 200 pixels[x+(_width*y)]&= 0x0F; 201 pixels[x+(_width*y)]|= color<<4; 202 } 203 } 204 205 ///Resizes the array behind the bitmap. 206 public void resize(int x,int y) @safe pure { 207 if(x & 1) 208 throw new BitmapFormatException("Incorrect Bitmap size exception!"); 209 pixels.length=x*y; 210 _width = x; 211 _height = y; 212 } 213 214 } else static if(S == "b") { 215 /** 216 * CTOR for 1 bit bitmaps with no preexisting source. 217 */ 218 public this(int w, int h) @trusted pure { 219 _width = w; 220 _height = h; 221 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 222 pixels.length = pitch / (size_t.sizeof * 8) * h; 223 pixelAccess = BitArray(pixels, pitch * height); 224 } 225 /** 226 * CTOR to convert 8bit aligned bitmaps to 32/64bit ones. 227 */ 228 public this(ubyte[] src, int w, int h) @trusted pure { 229 _width = w; 230 _height = h; 231 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 232 const size_t pitch0 = w + (8 - (w % 8)); 233 const size_t len = pitch / (size_t.sizeof * 8), len0 = pitch0 / 8; 234 for (size_t i ; i < len0 * h; i+= len0) { 235 ubyte[] workpad = src[i..i+len0]; 236 workpad.length = len; 237 pixels ~= reinterpretCast!size_t(workpad); 238 } 239 pixelAccess = BitArray(pixels, pitch * height); 240 } 241 /** 242 * CTOR for 1 bit bitmaps with a preexisting source. 243 * Alignment and padding is for size_t (32 and 64 bit, on their respected systems) 244 */ 245 public this(size_t[] src, int w, int h) @trusted pure { 246 _width = w; 247 _height = h; 248 pitch = w + (size_t.sizeof * 8 - (w % (size_t.sizeof * 8))); 249 pixels = src; 250 pixelAccess = BitArray(pixels, pitch * height); 251 } 252 ///Returns the pixel at the given position. 253 @nogc public bool readPixel(int x, int y) @trusted pure { 254 assert (x >= 0 && x < _width && y >= 0 && y < _height); 255 return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)]; 256 } 257 ///Writes the pixel at the given position. 258 @nogc public bool writePixel(int x, int y, bool val) @trusted pure { 259 assert (x >= 0 && x < _width && y >= 0 && y < _height); 260 return pixelAccess[(invertHoriz ? _width - x : x) + ((invertVert ? _height - y : y) * pitch)] = val; 261 } 262 /** 263 * Tests a single line of pixels between two 1 bit bitmaps for collision, using a single chunk of pixels. (Unimplemented, is a placeholder as of now) 264 * * line: The (first) line, which is being tested in the current object. 265 * * other: The other object that this is being tested against. 266 * * otherLine: The (first) line, which is being tested in the other object. 267 * * offset: The horizontal offset of the other object to the right. If negative, then it's being offsetted to the left. 268 * * nOfLines: The number of lines to be tested. Must be non-zero, otherwise the test won't run. 269 */ 270 final public bool testLineCollision(int line, Bitmap1bit other, int otherLine, const int offset, uint nOfLines = 1) 271 @safe @nogc nothrow pure const { 272 for ( ; nOfLines > 0 ; nOfLines--) { 273 274 } 275 return false; 276 } 277 } 278 static if(S == "B" || S == "HW") { 279 /** 280 * Offsets all indexes in the bitmap by a certain value. Keeps zeroth index (usually for transparency) if needed. Useful when converting bitmaps. 281 */ 282 public @nogc void offsetIndexes(ushort offset, bool keepZerothIndex = true) @safe pure{ 283 for(int i ; i < pixels.length ; i++){ 284 if(!(pixels[i] == 0 && keepZerothIndex)){ 285 pixels[i] += offset; 286 } 287 } 288 } 289 } 290 static if(S == "W"){ 291 /** 292 * Clears the Bitmap 293 */ 294 override void clear() @nogc @safe pure nothrow { 295 for(int i ; i < pixels.length ; i++){ 296 pixels[i] = Color(0x0); 297 } 298 } 299 }else{ 300 /+override public AdvancedBitArray generateStandardCollisionModel(){ //REMOVE BY 0.10.0! 301 AdvancedBitArray result = new AdvancedBitArray(_width * _height); 302 for(int i ; i < _width * _height ; i++){ 303 T pixel = readpixel(i, 0); 304 if(pixel != 0){ 305 result[i] = true; 306 } 307 } 308 return result; 309 }+/ 310 override void clear() @nogc @safe pure nothrow { 311 for(int i ; i < pixels.length ; i++){ 312 pixels[i] = 0; 313 } 314 } 315 } 316 @nogc public T* getPtr() pure @trusted nothrow { 317 return pixels.ptr; 318 } 319 override @nogc @property string wordLengthByString() @safe { 320 return S; 321 } 322 static if (S != "b" || S != "W") { 323 /** 324 * Generates a standard collision model by checking against a transparency value (default vaule is T.init). 325 */ 326 Bitmap1bit generateStandardCollisionModel(const T transparency = T.init) { 327 Bitmap1bit output = new Bitmap1bit(width, height); 328 for (int y ; y < height ; y++) { 329 for (int x ; x < width ; x++) { 330 output.writePixel(x, y, readPixel(x, y) != transparency); 331 } 332 } 333 return output; 334 } 335 } 336 } 337 338 /** 339 * Defines Bitmap types 340 */ 341 public enum BitmapTypes : ubyte { 342 Undefined, ///Can be used for error checking, e.g. if a tile was initialized or not 343 Bmp1Bit, 344 Bmp2Bit, 345 Bmp4Bit, 346 Bmp8Bit, 347 Bmp16Bit, 348 Bmp32Bit, 349 Planar, ///Mainly used as a placeholder 350 }