1 /* 2 * Copyright (C) 2015-2020, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, graphics.layers.tilelayer module 5 */ 6 7 module PixelPerfectEngine.graphics.layers.tilelayer; 8 9 public import PixelPerfectEngine.graphics.layers.base; 10 import collections.treemap; 11 12 public class TileLayer : Layer, ITileLayer { 13 /** 14 * Implements a single tile to be displayed. 15 * Is ordered in a BinarySearchTree for fast lookup. 16 */ 17 protected struct DisplayListItem { 18 ABitmap tile; ///reference counting only 19 void* pixelDataPtr; ///points to the pixeldata 20 //Color* palettePtr; ///points to the palette if present 21 wchar ID; ///ID, mainly as a padding to 32 bit alignment 22 ubyte wordLength; ///to avoid calling the more costly classinfo 23 /** 24 * Sets the maximum accessable color amount by the bitmap. 25 * By default, for 4 bit bitmaps, it's 4, and it enables 256 * 16 color palettes. 26 * This limitation is due to the way how the MappingElement struct works. 27 * 8 bit bitmaps can assess the full 256 * 256 palette space. 28 * Lower values can be described to avoid wasting palettes space in cases when the 29 * bitmaps wouldn't use their full capability. 30 */ 31 ubyte paletteSh; 32 ///Default ctor 33 this(wchar ID, ABitmap tile, ubyte paletteSh = 0) pure @safe { 34 //palettePtr = tile.getPalettePtr(); 35 //this.paletteSel = paletteSel; 36 this.ID = ID; 37 this.tile=tile; 38 if(typeid(tile) is typeid(Bitmap4Bit)){ 39 wordLength = 4; 40 this.paletteSh = paletteSh ? paletteSh : 4; 41 pixelDataPtr = (cast(Bitmap4Bit)(tile)).getPtr; 42 }else if(typeid(tile) is typeid(Bitmap8Bit)){ 43 wordLength = 8; 44 this.paletteSh = paletteSh ? paletteSh : 8; 45 pixelDataPtr = (cast(Bitmap8Bit)(tile)).getPtr; 46 }else if(typeid(tile) is typeid(Bitmap16Bit)){ 47 wordLength = 16; 48 pixelDataPtr = (cast(Bitmap16Bit)(tile)).getPtr; 49 }else if(typeid(tile) is typeid(Bitmap32Bit)){ 50 wordLength = 32; 51 pixelDataPtr = (cast(Bitmap32Bit)(tile)).getPtr; 52 }else{ 53 throw new TileFormatException("Bitmap format not supported!"); 54 } 55 } 56 string toString() const { 57 import std.conv : to; 58 string result = to!string(cast(ushort)ID) ~ " ; " ~ to!string(pixelDataPtr) ~ " ; " ~ to!string(wordLength); 59 return result; 60 } 61 } 62 protected int tileX; ///Tile width 63 protected int tileY; ///Tile height 64 protected int mX; ///Map width 65 protected int mY; ///Map height 66 protected size_t totalX; ///Total width of the tilelayer in pixels 67 protected size_t totalY; ///Total height of the tilelayer in pixels 68 protected MappingElement[] mapping;///Contains the mapping data 69 //private wchar[] mapping; 70 //private BitmapAttrib[] tileAttributes; 71 protected Color[] src; ///Local buffer 72 alias DisplayList = TreeMap!(wchar, DisplayListItem, true); 73 protected DisplayList displayList; ///displaylist using a BST to allow skipping elements 74 /** 75 * Enables the TileLayer to access other parts of the palette if needed. 76 * Does not effect 16 bit bitmaps, but effects all 4 and 8 bit bitmap 77 * within the layer, so use with caution to avoid memory leakages. 78 */ 79 public ushort paletteOffset; 80 /** 81 * Sets the warp mode of the layer. 82 * Can repeat the whole layer, a single tile, or be turned off completely. 83 */ 84 public WarpMode warpMode; 85 public ubyte masterVal; ///Sets the master alpha value for the layer 86 ///Emulates horizontal blanking interrupt effects, like per-line scrolling. 87 ///line no -1 indicates that no lines have been drawn yet. 88 public @nogc void delegate(int line, ref int sX0, ref int sY0) hBlankInterrupt; 89 ///Constructor. tX , tY : Set the size of the tiles on the layer. 90 this(int tX, int tY, RenderingMode renderMode = RenderingMode.AlphaBlend){ 91 tileX=tX; 92 tileY=tY; 93 setRenderingMode(renderMode); 94 src.length = tileX; 95 } 96 ///Gets the the ID of the given element from the mapping. x , y : Position. 97 public MappingElement readMapping(int x, int y) @nogc @safe pure nothrow const { 98 final switch (warpMode) with (WarpMode) { 99 case Off: 100 if(x < 0 || y < 0 || x >= mX || y >= mY){ 101 return MappingElement(0xFFFF); 102 } 103 break; 104 case MapRepeat: 105 //x *= x > 0 ? 1 : -1; 106 x = cast(uint)x % mX; 107 //y *= y > 0 ? 1 : -1; 108 y = cast(uint)y % mY; 109 break; 110 case TileRepeat: 111 if(x < 0 || y < 0 || x >= mX || y >= mY){ 112 return MappingElement(0x0000); 113 } 114 break; 115 } 116 return mapping[x+(mX*y)]; 117 } 118 ///Writes to the map. x , y : Position. w : ID of the tile. 119 public void writeMapping(int x, int y, MappingElement w) @nogc @safe pure nothrow { 120 if(x >= 0 && y >= 0 && x < mX && y < mY) 121 mapping[x+(mX*y)]=w; 122 } 123 /** 124 * Writes a text to the map. 125 * This function is a bit rudamentary, as it doesn't handle word breaks, and needs per-line writing. 126 * Requires the text to be in 16 bit format 127 */ 128 public void writeTextToMap(const int x, const int y, const ubyte color, wstring text, 129 BitmapAttrib atrb = BitmapAttrib.init) @nogc @safe pure nothrow { 130 for (int i ; i < text.length ; i++) { 131 writeMapping(x + i, y, MappingElement(text[i], atrb, color)); 132 } 133 } 134 ///Writes to the map. x , y : Position. w : ID of the tile. 135 /*@nogc public void writeTileAttribute(int x, int y, BitmapAttrib ba){ 136 tileAttributes[x+(mX*y)]=ba; 137 }*/ 138 ///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map. 139 ///x*y=map.length 140 public void loadMapping(int x, int y, MappingElement[] mapping) @safe pure { 141 assert (x * y == mapping.length); 142 mX=x; 143 mY=y; 144 this.mapping = mapping; 145 totalX=mX*tileX; 146 totalY=mY*tileY; 147 } 148 ///Adds a tile to the tileSet. t : The tile. id : The ID in wchar to differentiate between different tiles. 149 public void addTile(ABitmap tile, wchar id, ubyte paletteSh = 0) { 150 if(tile.width==tileX && tile.height==tileY) { 151 displayList[id] = DisplayListItem(id, tile, paletteSh); 152 }else{ 153 throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null); 154 } 155 } 156 ///Returns a tile from the displaylist 157 public ABitmap getTile(wchar id) { 158 return displayList[id].tile; 159 } 160 ///Removes the tile with the ID from the set. 161 public void removeTile(wchar id) { 162 displayList.remove(id); 163 } 164 ///Returns which tile is at the given pixel 165 public MappingElement tileByPixel(int x, int y) @nogc @safe pure nothrow const { 166 x = cast(uint)x / tileX; 167 y = cast(uint)y / tileY; 168 return readMapping(x, y); 169 } 170 171 public @nogc override void updateRaster(void* workpad, int pitch, Color* palette) { 172 import std.stdio : printf; 173 int sX0 = sX, sY0 = sY; 174 if (hBlankInterrupt !is null) 175 hBlankInterrupt(-1, sX0, sY0); 176 177 for (int line ; line < rasterY ; line++) { 178 if (hBlankInterrupt !is null) 179 hBlankInterrupt(line, sX0, sY0); 180 if ((sY0 >= 0 && sY0 < totalY) || warpMode != WarpMode.Off) { 181 int sXlocal = sX0; 182 int sYAbs = sY0 & int.max; 183 const sizediff_t offsetP = line * pitch; // The offset of the line that is being written 184 void* w0 = workpad + offsetP; 185 const int offsetY = sYAbs % tileY; //Offset of the current line of the tiles in this line 186 const int offsetX0 = tileX - ((cast(uint)sXlocal + rasterX) % tileX); //Scroll offset of the rightmost column 187 const int offsetX = (cast(uint)sXlocal % tileX); //Scroll offset of the leftmost column 188 int tileXLength = offsetX ? tileX - offsetX : tileX; 189 for (int col ; col < rasterX ; ) { 190 //const int sXCurr = col && sX < 0 ? sXlocal - tileXLength : sXlocal; 191 const MappingElement currentTile = tileByPixel(sXlocal, sYAbs); 192 if (currentTile.tileID != 0xFFFF) { 193 const DisplayListItem tileInfo = displayList[currentTile.tileID]; 194 const int offsetX1 = col ? 0 : offsetX; 195 const int offsetY0 = currentTile.attributes.vertMirror ? tileY - offsetY - 1 : offsetY; 196 if (col + tileXLength > rasterX) { 197 tileXLength -= offsetX0; 198 } 199 final switch (tileInfo.wordLength) { 200 case 4: 201 ubyte* tileSrc = cast(ubyte*)tileInfo.pixelDataPtr + (offsetX1 + (offsetY0 * tileX)>>>1); 202 main4BitColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette) + 203 (currentTile.paletteSel<<tileInfo.paletteSh) + paletteOffset, tileXLength, offsetX1 & 1); 204 if(currentTile.attributes.horizMirror){//Horizontal mirroring 205 flipHorizontal(src); 206 } 207 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength,masterVal); 208 break; 209 case 8: 210 ubyte* tileSrc = cast(ubyte*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 211 main8BitColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette) + 212 (currentTile.paletteSel<<tileInfo.paletteSh) + paletteOffset, tileXLength); 213 if(currentTile.attributes.horizMirror){//Horizontal mirroring 214 flipHorizontal(src); 215 } 216 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength,masterVal); 217 break; 218 case 16: 219 ushort* tileSrc = cast(ushort*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 220 mainColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette), tileXLength); 221 if(currentTile.attributes.horizMirror){//Horizontal mirroring 222 flipHorizontal(src); 223 } 224 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength,masterVal); 225 break; 226 case 32: 227 Color* tileSrc = cast(Color*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 228 if(!currentTile.attributes.horizMirror) { 229 mainRenderingFunction(cast(uint*)tileSrc,cast(uint*)w0,tileXLength,masterVal); 230 } else { 231 copy(cast(uint*)tileSrc, cast(uint*)src, tileXLength); 232 flipHorizontal(src); 233 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength,masterVal); 234 } 235 break; 236 237 } 238 239 } 240 sXlocal += tileXLength; 241 col += tileXLength; 242 w0 += tileXLength<<2; 243 244 tileXLength = tileX; 245 } 246 } 247 sY0++; 248 } 249 } 250 public MappingElement[] getMapping() @nogc @safe pure nothrow { 251 return mapping; 252 } 253 public int getTileWidth() @nogc @safe pure nothrow const { 254 return tileX; 255 } 256 public int getTileHeight() @nogc @safe pure nothrow const { 257 return tileY; 258 } 259 public int getMX() @nogc @safe pure nothrow const { 260 return mX; 261 } 262 public int getMY() @nogc @safe pure nothrow const { 263 return mY; 264 } 265 public size_t getTX() @nogc @safe pure nothrow const { 266 return totalX; 267 } 268 public size_t getTY() @nogc @safe pure nothrow const { 269 return totalY; 270 } 271 /// Sets the warp mode. 272 /// Returns the new warp mode that is being used. 273 public WarpMode setWarpMode(WarpMode mode) @nogc @safe pure nothrow { 274 return warpMode = mode; 275 } 276 /// Returns the currently used warp mode. 277 public WarpMode getWarpMode() @nogc @safe pure nothrow const { 278 return warpMode; 279 } 280 }