1 /* 2 * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, graphics.layers module 5 */ 6 module PixelPerfectEngine.graphics.layers; 7 8 public import PixelPerfectEngine.graphics.bitmap; 9 public import PixelPerfectEngine.graphics.common; 10 import PixelPerfectEngine.graphics.transformFunctions; 11 import PixelPerfectEngine.system.binarySearchTree; 12 import PixelPerfectEngine.system.etc; 13 import std.parallelism; 14 //import std.container.rbtree; 15 //import system.etc; 16 import PixelPerfectEngine.system.exc; 17 //import std.algorithm; 18 import bindbc.sdl; 19 import core.stdc.stdlib; 20 //import std.range; 21 import CPUblit.composing; 22 import CPUblit.colorlookup; 23 import CPUblit.transform; 24 import conv = std.conv; 25 26 version(LDC){ 27 import inteli.emmintrin; 28 } 29 /// For generating a function out of a template 30 @nogc void localBlt(uint* src, uint* dest, size_t length){ 31 blitter(src, dest, length); 32 } 33 /** 34 * The basis of all layer classes, containing functions for rendering. 35 */ 36 abstract class Layer { 37 protected @nogc void function(uint* src, uint* dest, size_t length) mainRenderingFunction; ///Used to get around some readability issues. (void* src, void* dest, int length) 38 protected @nogc void function(ushort* src, uint* dest, uint* palette, size_t length) mainColorLookupFunction; 39 //protected @nogc void function(uint* src, int length) mainHorizontalMirroringFunction; 40 protected @nogc void function(ubyte* src, uint* dest, uint* palette, size_t length) main8BitColorLookupFunction; 41 protected @nogc void function(ubyte* src, uint* dest, uint* palette, size_t length, int offset) main4BitColorLookupFunction; 42 protected LayerRenderingMode renderMode; 43 44 // scrolling position 45 protected int sX, sY, rasterX, rasterY; 46 47 /// Sets the main rasterizer 48 public void setRasterizer(int rX, int rY){ 49 //frameBuffer = frameBufferP; 50 rasterX=rX; 51 rasterY=rY; 52 53 } 54 ///Sets the rendering mode 55 @nogc public void setRenderingMode(LayerRenderingMode mode){ 56 renderMode = mode; 57 switch(mode){ 58 case LayerRenderingMode.ALPHA_BLENDING: 59 //mainRenderingFunction = &alphaBlend; 60 mainRenderingFunction = &alphaBlend32bit; 61 break; 62 case LayerRenderingMode.BLITTER: 63 mainRenderingFunction = &localBlt; 64 break; 65 default: 66 mainRenderingFunction = ©32bit; 67 } 68 mainColorLookupFunction = &colorLookup!(ushort,uint); 69 //mainHorizontalMirroringFunction = &flipHorizontal; 70 main8BitColorLookupFunction = &colorLookup!(ubyte,uint); 71 main4BitColorLookupFunction = &colorLookup4Bit!uint; 72 } 73 ///Absolute scrolling. 74 @nogc @safe public void scroll(int x, int y){ 75 sX=x; 76 sY=y; 77 } 78 ///Relative scrolling. Positive values scrolls the layer left and up, negative values scrolls the layer down and right. 79 @nogc @safe public void relScroll(int x, int y){ 80 sX=sX+x; 81 sY=sY+y; 82 } 83 ///Getter for the X scroll position. 84 @nogc @safe public int getSX(){ 85 return sX; 86 } 87 ///Getter for the Y scroll position. 88 @nogc @safe public int getSY(){ 89 return sY; 90 } 91 /// Override this to enable output to the raster 92 public abstract void updateRaster(void* workpad, int pitch, Color* palette); 93 ///Standard algorithm for horizontal mirroring 94 ///Will be deprecated in later versions and instead external functions will be used. 95 @nogc protected void flipHorizontal(uint* src, int length){ 96 uint s; 97 uint* dest = src + length; 98 for(int i ; i < length ; i++){ 99 s = *src; 100 *src = *dest; 101 *dest = s; 102 src++; 103 dest--; 104 } 105 } 106 } 107 /** 108 * Mostly used for internal communication. 109 */ 110 public enum LayerType { 111 NULL, 112 tile, 113 transformableTile, 114 sprite, 115 } 116 /** 117 * Sets the rendering mode of the TileLayer. 118 * 119 * COPY is the fastest, but overrides any kind of transparency keying. It directly writes into the framebuffer. Should only be used for certain applications, like bottom layers. 120 * BLITTER uses a custom BitBlT algorithm for the SSE2 instruction set. Automatically generates the copying mask depending on the alpha-value. Any alpha-value that's non-zero will cause a non-transparent pixel, and all zeros are completely transparent. Gradual transparency in not avaliable. 121 * ALPHA_BLENDING uses SSE2 for alpha blending. The slowest (although speed loss might be minimal due to memory access limitation), but allows gradual transparencies. 122 */ 123 public enum LayerRenderingMode{ 124 COPY, ///the fastest, but overrides any kind of transparency keying. It directly writes into the framebuffer. Should only be used for certain applications, like bottom layers. 125 BLITTER, ///uses a custom BitBlT algorithm for the SSE2 instruction set. Automatically generates the copying mask depending on the alpha-value. Any alpha-value that's non-zero will cause a non-transparent pixel, and all zeros are completely transparent. Gradual transparency in not avaliable. 126 ALPHA_BLENDING ///uses SSE2 for alpha blending. The slowest (although speed loss might be minimal due to memory access limitation), but allows gradual transparencies. 127 } 128 /** 129 * Tile interface, defines common functions. 130 */ 131 public interface ITileLayer{ 132 public MappingElement[] getMapping(); 133 /// Reads the mapping element from the given area. 134 @nogc public MappingElement readMapping(int x, int y); 135 /// Writes the given element into the mapping at the given location. 136 @nogc public void writeMapping(int x, int y, MappingElement w); 137 /// Loads the mapping, primarily used for deserialization. 138 public void loadMapping(int x, int y, MappingElement[] mapping); 139 /// Removes the tile from the display list with the given ID. 140 public void removeTile(wchar id); 141 /// Returns the tile ID from the location by pixel. 142 @nogc public MappingElement tileByPixel(int x, int y); 143 /// Returns the width of the tiles. 144 @nogc public int getTileWidth() pure; 145 /// Returns the height of the tiles. 146 @nogc public int getTileHeight() pure; 147 /// Returns the width of the mapping. 148 @nogc public int getMX() pure; 149 /// Returns the height of the mapping. 150 @nogc public int getMY() pure; 151 /// Returns the total width of the tile layer. 152 @nogc public int getTX() pure; 153 /// Returns the total height of the tile layer. 154 @nogc public int getTY() pure; 155 /// Adds a tile. 156 public void addTile(ABitmap tile, wchar id); 157 /// Returns the tile. 158 public ABitmap getTile(wchar id); 159 } 160 /** 161 * Universal Mapping element, that is stored on 32 bit. 162 */ 163 public struct MappingElement{ 164 wchar tileID; ///Determines which tile is being used for the given instance 165 BitmapAttrib attributes; ///General attributes, such as vertical and horizontal mirroring. The extra 6 bits can be used for various purposes 166 ubyte paletteSel; ///Selects the palette for the bitmap if supported 167 ///Default constructor 168 @nogc @safe this(wchar tileID, BitmapAttrib attributes = BitmapAttrib(false, false)){ 169 this.tileID = tileID; 170 this.attributes = attributes; 171 } 172 public string toString() const { 173 import std.conv : to; 174 return "[tileID:" ~ to!string(cast(int)tileID) ~ "; attributes:" ~ attributes.toString ~ "; paletteSel:" ~ 175 to!string(paletteSel) ~ "]"; 176 } 177 } 178 179 /** 180 * General purpose TileLayer with palette support, mainly for backgrounds. 181 * Use multiple of this class for paralax scrolling. 182 * Can use any kind of bitmaps thanks to code restructuring. 183 */ 184 public class TileLayer : Layer, ITileLayer{ 185 /** 186 * Implements a single tile to be displayed. 187 * Is ordered in a BinarySearchTree for fast lookup. 188 */ 189 protected struct DisplayListItem{ 190 ABitmap tile; ///reference counting only 191 void* pixelDataPtr; ///points to the pixeldata 192 //Color* palettePtr; ///points to the palette if present 193 wchar ID; ///ID, mainly as a padding to 32 bit alignment 194 ubyte wordLength; ///to avoid calling the more costly classinfo 195 ubyte reserved; ///currently unused 196 ///Default ctor 197 this(wchar ID, ABitmap tile){ 198 //palettePtr = tile.getPalettePtr(); 199 //this.paletteSel = paletteSel; 200 this.ID = ID; 201 this.tile=tile; 202 if(tile.classinfo == typeid(Bitmap4Bit)){ 203 wordLength = 4; 204 pixelDataPtr = (cast(Bitmap4Bit)(tile)).getPtr; 205 }else if(tile.classinfo == typeid(Bitmap8Bit)){ 206 wordLength = 8; 207 pixelDataPtr = (cast(Bitmap8Bit)(tile)).getPtr; 208 }else if(tile.classinfo == typeid(Bitmap16Bit)){ 209 wordLength = 16; 210 pixelDataPtr = (cast(Bitmap16Bit)(tile)).getPtr; 211 }else if(tile.classinfo == typeid(Bitmap32Bit)){ 212 wordLength = 32; 213 pixelDataPtr = (cast(Bitmap32Bit)(tile)).getPtr; 214 }else{ 215 throw new TileFormatException("Bitmap format not supported!"); 216 } 217 } 218 } 219 protected int tileX; ///Tile width 220 protected int tileY; ///Tile height 221 protected int mX; ///Map width 222 protected int mY; ///Map height 223 protected int totalX; ///Total width of the tilelayer in pixels 224 protected int totalY; ///Total height of the tilelayer in pixels 225 protected MappingElement[] mapping;///Contains the mapping data 226 //private wchar[] mapping; 227 //private BitmapAttrib[] tileAttributes; 228 protected Color[] src; ///Local buffer 229 protected BinarySearchTree!(wchar, DisplayListItem) displayList; ///displaylist using a BST to allow skipping elements 230 protected bool warpMode; 231 ///Emulates horizontal blanking interrupt effects, like per-line scrolling. 232 ///line no -1 indicates that no lines have been drawn yet. 233 public @nogc void delegate(int line, ref int sX0, ref int sY0) hBlankInterrupt; 234 ///Constructor. tX , tY : Set the size of the tiles on the layer. 235 this(int tX, int tY, LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){ 236 tileX=tX; 237 tileY=tY; 238 setRenderingMode(renderMode); 239 src.length = tileX; 240 } 241 /// Warpmode: if enabled, the layer will be turned into an "infinite" mode. 242 public void setWarpMode(bool w){ 243 warpMode = w; 244 } 245 ///Gets the the ID of the given element from the mapping. x , y : Position. 246 @nogc public MappingElement readMapping(int x, int y){ 247 if(!warpMode){ 248 if(x < 0 || y < 0 || x >= mX || y >= mY){ 249 return MappingElement(0xFFFF); 250 } 251 }else{ 252 x = x % mX; 253 y = y % mY; 254 } 255 return mapping[x+(mX*y)]; 256 } 257 ///Writes to the map. x , y : Position. w : ID of the tile. 258 @nogc public void writeMapping(int x, int y, MappingElement w){ 259 mapping[x+(mX*y)]=w; 260 } 261 ///Writes to the map. x , y : Position. w : ID of the tile. 262 /*@nogc public void writeTileAttribute(int x, int y, BitmapAttrib ba){ 263 tileAttributes[x+(mX*y)]=ba; 264 }*/ 265 ///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map. 266 ///x*y=map.length 267 public void loadMapping(int x, int y, MappingElement[] mapping){ 268 mX=x; 269 mY=y; 270 this.mapping = mapping; 271 totalX=mX*tileX; 272 totalY=mY*tileY; 273 } 274 ///Adds a tile to the tileSet. t : The tile. id : The ID in wchar to differentiate between different tiles. 275 public void addTile(ABitmap tile, wchar id) { 276 if(tile.width==tileX && tile.height==tileY) { 277 displayList[id] = DisplayListItem(id, tile); 278 }else{ 279 throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null); 280 } 281 } 282 ///Returns a tile from the displaylist 283 public ABitmap getTile(wchar id) { 284 return displayList[id].tile; 285 } 286 ///Removes the tile with the ID from the set. 287 public void removeTile(wchar id) { 288 displayList.remove(id); 289 } 290 ///Returns which tile is at the given pixel 291 @nogc public MappingElement tileByPixel(int x, int y){ 292 if(!warpMode && (x < 0 || y < 0)) return MappingElement(0xFFFF); 293 x /= tileX; 294 y /= tileY; 295 if(warpMode){ 296 x %= mX; 297 y %= mY; 298 } 299 if(x >= mX || y >= mY) return MappingElement(0xFFFF); 300 return mapping[x + y*mX]; 301 } 302 303 public @nogc override void updateRaster(void* workpad, int pitch, Color* palette){ 304 int sX0 = sX, sY0 = sY; 305 if (hBlankInterrupt !is null) 306 hBlankInterrupt(-1, sX0, sY0); 307 308 for (int line ; line < rasterY ; line++) { 309 if (hBlankInterrupt !is null) 310 hBlankInterrupt(line, sX0, sY0); 311 if ((sY0 >= 0 && sY0 < totalY) || warpMode) { 312 int sXAbs = warpMode ? sX0 & int.max : sX0, sYAbs = sY0 & int.max; 313 const sizediff_t offsetP = line * pitch; // The offset of the line that is being written 314 void* w0 = workpad + offsetP; 315 const int offsetY = sYAbs % tileY; //Offset of the current line of the tiles in this line 316 const int offsetX0 = tileX - ((sXAbs + rasterX) % tileX); //Scroll offset of the rightmost column 317 const int offsetX = (sXAbs & int.max) % tileX; //Scroll offset of the leftmost column 318 int tileXLength = offsetX ? tileX - offsetX : tileX; 319 for (int col ; col < rasterX ; ) { 320 const MappingElement currentTile = tileByPixel(sXAbs, sYAbs); 321 if (currentTile.tileID != 0xFFFF) { 322 const DisplayListItem tileInfo = displayList[currentTile.tileID]; 323 const int offsetX1 = col ? 0 : offsetX; 324 const int offsetY0 = currentTile.attributes.vertMirror ? tileY - offsetY : offsetY; 325 if (col + tileXLength > rasterX) { 326 tileXLength -= offsetX0; 327 } 328 final switch (tileInfo.wordLength) { 329 case 4: 330 ubyte* tileSrc = cast(ubyte*)tileInfo.pixelDataPtr + (offsetX1 + (offsetY0 * tileX)>>>1); 331 main4BitColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette) + (currentTile.paletteSel<<4), 332 tileXLength, offsetX1 & 1); 333 if(currentTile.attributes.horizMirror){//Horizontal mirroring 334 flipHorizontal(cast(uint*)src.ptr, tileX); 335 } 336 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength); 337 break; 338 case 8: 339 ubyte* tileSrc = cast(ubyte*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 340 main8BitColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette) + (currentTile.paletteSel<<8), tileXLength); 341 if(currentTile.attributes.horizMirror){//Horizontal mirroring 342 flipHorizontal(cast(uint*)src.ptr, tileX); 343 } 344 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength); 345 break; 346 case 16: 347 ushort* tileSrc = cast(ushort*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 348 mainColorLookupFunction(tileSrc, cast(uint*)src, (cast(uint*)palette), tileXLength); 349 if(currentTile.attributes.horizMirror){//Horizontal mirroring 350 flipHorizontal(cast(uint*)src.ptr, tileX); 351 } 352 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength); 353 break; 354 case 32: 355 Color* tileSrc = cast(Color*)tileInfo.pixelDataPtr + offsetX1 + (offsetY0 * tileX); 356 if(!currentTile.attributes.horizMirror) { 357 mainRenderingFunction(cast(uint*)tileSrc,cast(uint*)w0,tileXLength); 358 } else { 359 CPUblit.composing.copy32bit(cast(uint*)tileSrc, cast(uint*)src, tileXLength); 360 flipHorizontal(cast(uint*)src.ptr, tileX); 361 mainRenderingFunction(cast(uint*)src,cast(uint*)w0,tileXLength); 362 } 363 break; 364 365 } 366 367 } 368 sXAbs += tileXLength; 369 col += tileXLength; 370 w0 += tileXLength<<2; 371 372 tileXLength = tileX; 373 } 374 } 375 sY0++; 376 } 377 } 378 public MappingElement[] getMapping(){ 379 return mapping; 380 } 381 @nogc public int getTileWidth() pure{ 382 return tileX; 383 } 384 @nogc public int getTileHeight() pure{ 385 return tileY; 386 } 387 @nogc public int getMX() pure{ 388 return mX; 389 } 390 @nogc public int getMY() pure{ 391 return mY; 392 } 393 @nogc public int getTX() pure{ 394 return totalX; 395 } 396 @nogc public int getTY() pure{ 397 return totalY; 398 } 399 } 400 /** 401 * Implements a modified TileLayer with transformability with capabilities similar to MODE7. 402 * <br/> 403 * Transform function: 404 * [x',y'] = ([A,B,C,D] * ([x,y] + [sX,sY] - [x_0,y_0])>>>8 + [x_0,y_0] 405 * <br/> 406 * All basic transform values are integer based, 256 equals with 1.0 407 * <br/> 408 * Restrictions compared to standard TileLayer: 409 * <ul> 410 * <li>Tiles must have any of the following sizes: 8, 16, 32, 64; since this layer needs to do modulo computations for each pixel.</li> 411 * <li>Maximum layer size in pixels are restricted to 65536*65536 due to architectural limitations. Accelerated versions might raise 412 * this limitation.</li> 413 * </ul> 414 * HDMA emulation supported through delegate hBlankInterrupt. 415 */ 416 public class TransformableTileLayer(BMPType = Bitmap16Bit, int TileX = 8, int TileY = 8) : Layer, ITileLayer{ 417 /*if(isPowerOf2(TileX) && isPowerOf2(TileY))*/ 418 protected struct DisplayListItem { 419 BMPType tile; ///For reference counting 420 void* pixelSrc; ///Used for quicker access to the Data 421 wchar ID; ///ID, mainly used as a padding 422 ushort reserved; ///Padding for 32 bit 423 this (wchar ID, BMPType tile) { 424 this.ID = ID; 425 this.tile = tile; 426 pixelSrc = cast(void*)tile.getPtr(); 427 } 428 } 429 protected BinarySearchTree!(wchar, DisplayListItem) displayList; 430 protected short[4] transformPoints; /** Defines how the layer is being transformed */ 431 protected short[2] tpOrigin; 432 protected Bitmap32Bit backbuffer; ///used to store current screen output 433 /*static if(BMPType.mangleof == Bitmap8Bit.mangleof){ 434 protected ubyte[] src; 435 protected Color* palettePtr; ///Shared palette 436 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 437 protected ushort[] src;*/ 438 static if(BMPType.mangleof == Bitmap4Bit.mangleof || BMPType.mangleof == Bitmap8Bit.mangleof || 439 BMPType.mangleof == Bitmap16Bit.mangleof){ 440 protected ushort[] src; 441 }else static if(BMPType.mangleof == Bitmap32Bit.mangleof){ 442 443 }else static assert(false,"Template parameter " ~ BMPType.mangleof ~ " not supported by TransformableTileLayer!"); 444 protected bool needsUpdate; ///Set to true if backbuffer needs an update 445 protected bool warpMode; ///Repeats the whole layer if set to true 446 protected int mX, mY; ///"Inherited" from TileLayer 447 static if(TileX == 8) 448 protected immutable int shiftX = 3; 449 else static if(TileX == 16) 450 protected immutable int shiftX = 4; 451 else static if(TileX == 32) 452 protected immutable int shiftX = 5; 453 else static if(TileX == 64) 454 protected immutable int shiftX = 6; 455 else static assert(false,"Unsupported horizontal tile size!"); 456 static if(TileY == 8) 457 protected immutable int shiftY = 3; 458 else static if(TileY == 16) 459 protected immutable int shiftY = 4; 460 else static if(TileY == 32) 461 protected immutable int shiftY = 5; 462 else static if(TileY == 64) 463 protected immutable int shiftY = 6; 464 else static assert(false,"Unsupported vertical tile size!"); 465 protected int totalX, totalY; 466 protected MappingElement[] mapping; 467 version(LDC){ 468 protected int4 _tileAmpersand; 469 protected static short8 _increment; 470 } 471 alias HBIDelegate = @nogc void delegate(ref short[4] localABCD, ref short[2] localsXsY, ref short[2] localx0y0, short y); 472 /** 473 * Called before each line being redrawn. Can modify global values for each lines. 474 */ 475 public HBIDelegate hBlankInterrupt; 476 477 this(LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){ 478 A = 256; 479 B = 0; 480 C = 0; 481 D = 256; 482 x_0 = 0; 483 y_0 = 0; 484 _tileAmpersand = [TileX - 1, TileY - 1, TileX - 1, TileY - 1]; 485 setRenderingMode(renderMode); 486 needsUpdate = true; 487 } 488 static this(){ 489 for(int i ; i < 8 ; i+=2) 490 _increment[i] = 2; 491 } 492 override public void setRasterizer(int rX,int rY) { 493 super.setRasterizer(rX,rY); 494 backbuffer = new Bitmap32Bit(rX, rY); 495 static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof){ 496 src.length = rX; 497 } 498 } 499 500 override public @nogc void updateRaster(void* workpad,int pitch,Color* palette) { 501 //import core.stdc.stdio; 502 if(needsUpdate){ 503 needsUpdate = false; 504 //clear buffer 505 //backbuffer.clear(); 506 Color* dest = backbuffer.getPtr(); 507 short[2] sXsY = [cast(short)sX,cast(short)sY]; 508 short[4] localTP = transformPoints; 509 short[2] localTPO = tpOrigin; 510 //write new data into it 511 for(short y; y < rasterY; y++){ 512 if(hBlankInterrupt !is null){ 513 hBlankInterrupt(localTP, sXsY, localTPO, y); 514 } 515 version(DMD){ 516 for(short x; x < rasterX; x++){ 517 int[2] xy = transformFunctionInt([x,y], localTP, localTPO, sXsY); 518 //printf("[%i,%i]",xy[0],xy[1]); 519 MappingElement currentTile = tileByPixelWithoutTransform(xy[0],xy[1]); 520 if(currentTile.tileID != 0xFFFF){ 521 const DisplayListItem d = displayList[currentTile.tileID]; 522 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 523 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 524 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof){ 525 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 526 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 527 ushort* tsrc = cast(ushort*)d.pixelSrc; 528 }else static if(BMPType.mangleof == Bitmap32Bit.mangleof){ 529 Color* tsrc = cast(Color*)d.pixelSrc; 530 } 531 xy[0] = xy[0] & (TileX - 1); 532 xy[1] = xy[1] & (TileY - 1); 533 const int totalOffset = xy[0] + xy[1] * TileX; 534 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 535 src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile.paletteSel<<4; 536 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){ 537 src[x] = tsrc[totalOffset] | currentTile.paletteSel<<8; 538 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 539 src[x] = tsrc[totalOffset]; 540 }else{ 541 *dest = *tsrc; 542 dest++; 543 } 544 }else{ 545 static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof || 546 BMPType.mangleof == Bitmap4Bit.mangleof){ 547 src[x] = 0; 548 }else{ 549 (*dest).raw = 0; 550 } 551 } 552 } 553 }else version(LDC){ 554 /*short8 _sXsY = [sXsY[0],sXsY[1],sXsY[0],sXsY[1],sXsY[0],sXsY[1],sXsY[0],sXsY[1]], 555 _localTP = [localTP[0], localTP[1],localTP[2], localTP[3], localTP[0], localTP[1],localTP[2], localTP[3]], 556 _localTPO = [localTPO[0],localTPO[1],localTPO[0],localTPO[1],localTPO[0],localTPO[1],localTPO[0],localTPO[1]]; 557 int4 _localTPO_0 = [localTPO[0],localTPO[1],localTPO[0],localTPO[1]];*/ 558 short8 _sXsY, _localTP, _localTPO; 559 for(int i; i < 8; i++){ 560 _sXsY[i] = sXsY[i & 1]; 561 _localTP[i] = localTP[i & 3]; 562 _localTPO[i] = localTPO[i & 1]; 563 } 564 short8 xy_in; 565 for(int i = 1; i < 8; i += 2){ 566 xy_in[i] = y; 567 } 568 xy_in[4] = 1; 569 xy_in[6] = 1; 570 int4 _localTPO_0; 571 for(int i; i < 4; i++){ 572 _localTPO_0[i] = localTPO[i & 1]; 573 } 574 for(short x; x < rasterX; x++){ 575 int4 xy = _mm_srai_epi32(_mm_madd_epi16(_localTP, xy_in + _sXsY - _localTPO),8) + _localTPO_0; 576 MappingElement currentTile0 = tileByPixelWithoutTransform(xy[0],xy[1]), 577 currentTile1 = tileByPixelWithoutTransform(xy[2],xy[3]); 578 xy &= _tileAmpersand; 579 if(currentTile0.tileID != 0xFFFF){ 580 const DisplayListItem d = displayList[currentTile0.tileID]; 581 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 582 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 583 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof){ 584 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 585 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 586 ushort* tsrc = cast(ushort*)d.pixelSrc; 587 }else static if(BMPType.mangleof == Bitmap32Bit.mangleof){ 588 Color* tsrc = cast(Color*)d.pixelSrc; 589 } 590 xy[0] = xy[0] & (TileX - 1); 591 xy[1] = xy[1] & (TileY - 1); 592 const int totalOffset = xy[0] + xy[1] * TileX; 593 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 594 src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile0.paletteSel<<4; 595 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){ 596 src[x] = tsrc[totalOffset] | currentTile0.paletteSel<<8; 597 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 598 src[x] = tsrc[totalOffset]; 599 }else{ 600 *dest = *tsrc; 601 dest++; 602 } 603 }else{ 604 static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof || 605 BMPType.mangleof == Bitmap4Bit.mangleof){ 606 src[x] = 0; 607 }else{ 608 (*dest).raw = 0; 609 } 610 } 611 x++; 612 if(currentTile1.tileID != 0xFFFF){ 613 const DisplayListItem d = displayList[currentTile1.tileID]; 614 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 615 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 616 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof){ 617 ubyte* tsrc = cast(ubyte*)d.pixelSrc; 618 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 619 ushort* tsrc = cast(ushort*)d.pixelSrc; 620 }else static if(BMPType.mangleof == Bitmap32Bit.mangleof){ 621 Color* tsrc = cast(Color*)d.pixelSrc; 622 } 623 xy[2] = xy[2] & (TileX - 1); 624 xy[3] = xy[3] & (TileY - 1); 625 const int totalOffset = xy[2] + xy[3] * TileX; 626 static if(BMPType.mangleof == Bitmap4Bit.mangleof){ 627 src[x] = (totalOffset & 1 ? tsrc[totalOffset>>1]>>4 : tsrc[totalOffset>>1] & 0x0F) | currentTile1.paletteSel<<4; 628 }else static if(BMPType.mangleof == Bitmap8Bit.mangleof ){ 629 src[x] = tsrc[totalOffset] | currentTile1.paletteSel<<8; 630 }else static if(BMPType.mangleof == Bitmap16Bit.mangleof){ 631 src[x] = tsrc[totalOffset]; 632 }else{ 633 *dest = *tsrc; 634 dest++; 635 } 636 }else{ 637 static if(BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof || 638 BMPType.mangleof == Bitmap4Bit.mangleof){ 639 src[x] = 0; 640 }else{ 641 (*dest).raw = 0; 642 } 643 } 644 xy_in += _increment; 645 646 } 647 }else static assert(false, "Compiler not supported"); 648 /*static if(BMPType.mangleof == Bitmap8Bit.mangleof){ 649 main8BitColorLookupFunction(src.ptr, cast(uint*)dest, cast(uint*)palettePtr, rasterX); 650 dest += rasterX; 651 }else*/ static if(BMPType.mangleof == Bitmap4Bit.mangleof || BMPType.mangleof == Bitmap8Bit.mangleof || BMPType.mangleof == Bitmap16Bit.mangleof){ 652 mainColorLookupFunction(src.ptr, cast(uint*)dest, cast(uint*)palette, rasterX); 653 dest += rasterX; 654 } 655 } 656 } 657 //render surface onto the raster 658 void* p0 = workpad; 659 Color* c = backbuffer.getPtr(); 660 for(int y; y < rasterY; y++){ 661 mainRenderingFunction(cast(uint*)c,cast(uint*)p0,rasterX); 662 c += rasterX; 663 p0 += pitch; 664 } 665 666 } 667 ///Returns which tile is at the given pixel 668 @nogc public MappingElement tileByPixel(int x, int y){ 669 int[2] xy = transformFunctionInt([cast(short)x,cast(short)y],transformPoints,tpOrigin,[cast(short)sX,cast(short)sY]); 670 return tileByPixelWithoutTransform(xy[0],xy[1]); 671 } 672 ///Returns which tile is at the given pixel 673 @nogc protected MappingElement tileByPixelWithoutTransform(int x, int y){ 674 x >>>= shiftX; 675 y >>>= shiftY; 676 if(warpMode){ 677 x %= mX; 678 y %= mY; 679 } 680 if(x >= mX || y >= mY || x < 0 || y < 0) return MappingElement(0xFFFF); 681 return mapping[x + y*mX]; 682 } 683 684 /** 685 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out. 686 */ 687 public @nogc @property short A(){ 688 return transformPoints[0]; 689 } 690 /** 691 * Horizontal shearing. 692 */ 693 public @nogc @property short B(){ 694 return transformPoints[1]; 695 } 696 /** 697 * Vertical shearing. 698 */ 699 public @nogc @property short C(){ 700 return transformPoints[2]; 701 } 702 /** 703 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out. 704 */ 705 public @nogc @property short D(){ 706 return transformPoints[3]; 707 } 708 /** 709 * Horizontal transformation offset. 710 */ 711 public @nogc @property short x_0(){ 712 return tpOrigin[0]; 713 } 714 /** 715 * Vertical transformation offset. 716 */ 717 public @nogc @property short y_0(){ 718 return tpOrigin[1]; 719 } 720 /** 721 * Horizontal scaling. Greater than 256 means zooming in, less than 256 means zooming out. 722 */ 723 public @nogc @property short A(short newval){ 724 transformPoints[0] = newval; 725 needsUpdate = true; 726 return transformPoints[0]; 727 } 728 /** 729 * Horizontal shearing. 730 */ 731 public @nogc @property short B(short newval){ 732 transformPoints[1] = newval; 733 needsUpdate = true; 734 return transformPoints[1]; 735 } 736 /** 737 * Vertical shearing. 738 */ 739 public @nogc @property short C(short newval){ 740 transformPoints[2] = newval; 741 needsUpdate = true; 742 return transformPoints[2]; 743 } 744 /** 745 * Vertical scaling. Greater than 256 means zooming in, less than 256 means zooming out. 746 */ 747 public @nogc @property short D(short newval){ 748 transformPoints[3] = newval; 749 needsUpdate = true; 750 return transformPoints[3]; 751 } 752 /** 753 * Horizontal transformation offset. 754 */ 755 public @nogc @property short x_0(short newval){ 756 tpOrigin[0] = newval; 757 //tpOrigin[2] = newval; 758 needsUpdate = true; 759 return tpOrigin[0]; 760 } 761 /** 762 * Vertical transformation offset. 763 */ 764 public @nogc @property short y_0(short newval){ 765 tpOrigin[1] = newval; 766 //tpOrigin[3] = newval; 767 needsUpdate = true; 768 return tpOrigin[1]; 769 } 770 override public @safe @nogc void scroll(int x,int y) { 771 super.scroll(x,y); 772 needsUpdate = true; 773 } 774 override public @safe @nogc void relScroll(int x,int y) { 775 super.relScroll(x,y); 776 needsUpdate = true; 777 } 778 public MappingElement[] getMapping(){ 779 return mapping; 780 } 781 @nogc public int getTileWidth() pure{ 782 return TileX; 783 } 784 @nogc public int getTileHeight() pure{ 785 return TileY; 786 } 787 @nogc public int getMX() pure{ 788 return mX; 789 } 790 @nogc public int getMY() pure{ 791 return mY; 792 } 793 @nogc public int getTX() pure{ 794 return totalX; 795 } 796 @nogc public int getTY() pure{ 797 return totalY; 798 } 799 /// Warpmode: if enabled, the layer will be turned into an "infinite" mode. 800 public void setWarpMode(bool w){ 801 warpMode = w; 802 } 803 ///Gets the the ID of the given element from the mapping. x , y : Position. 804 @nogc public MappingElement readMapping(int x, int y){ 805 if(!warpMode){ 806 if(x < 0 || y < 0 || x >= mX || y >= mY){ 807 return MappingElement(0xFFFF); 808 } 809 }else{ 810 x = x % mX; 811 y = y % mY; 812 } 813 return mapping[x+(mX*y)]; 814 } 815 ///Writes to the map. x , y : Position. w : ID of the tile. 816 @nogc public void writeMapping(int x, int y, MappingElement w){ 817 mapping[x+(mX*y)]=w; 818 } 819 public void addTile(ABitmap tile, wchar id){ 820 if(tile.classinfo != typeid(BMPType)){ 821 throw new TileFormatException("Incorrect type of tile!"); 822 } 823 if(tile.width == TileX && tile.height == TileY){ 824 displayList[id]=DisplayListItem(id, cast(BMPType)tile); 825 }else{ 826 throw new TileFormatException("Incorrect tile size!", __FILE__, __LINE__, null); 827 } 828 } 829 ///Returns a tile from the displaylist 830 public ABitmap getTile(wchar id) { 831 return displayList[id].tile; 832 } 833 ///Removes the tile with the ID from the set. 834 public void removeTile(wchar id){ 835 displayList.remove(id); 836 } 837 ///Loads a mapping from an array. x , y : Sizes of the mapping. map : an array representing the elements of the map. 838 ///x*y=map.length 839 public void loadMapping(int x, int y, MappingElement[] mapping){ 840 mX=x; 841 mY=y; 842 this.mapping = mapping; 843 totalX=mX*TileX; 844 totalY=mY*TileY; 845 } 846 } 847 /** 848 *Used by the collision detectors 849 *DEPRECATED! Use the new system instead! 850 */ 851 public interface ISpriteCollision{ 852 ///Returns all sprite coordinates. 853 public ref Coordinate[int] getCoordinates(); 854 ///Returns all sprite attributes. 855 public ref BitmapAttrib[int] getSpriteAttributes(); 856 public ref int[] getSpriteSorter(); 857 858 } 859 /** 860 *General SpriteLayer interface. 861 */ 862 public interface ISpriteLayer{ 863 ///Removes the sprite with the given ID. 864 public void removeSprite(int n); 865 ///Moves the sprite to the given location. 866 public @nogc void moveSprite(int n, int x, int y); 867 ///Relatively moves the sprite by the given values. 868 public @nogc void relMoveSprite(int n, int x, int y); 869 ///Gets the coordinate of the sprite. 870 public Coordinate getSpriteCoordinate(int n); 871 ///Adds a sprite to the layer. 872 public void addSprite(ABitmap s, int n, Coordinate c, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024); 873 ///Adds a sprite to the layer. 874 public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024); 875 ///Replaces the sprite. If the new sprite has a different dimension, the old sprite's upper-left corner will be used. 876 public void replaceSprite(ABitmap s, int n); 877 ///Replaces the sprite and moves to the given position. 878 public void replaceSprite(ABitmap s, int n, int x, int y); 879 ///Replaces the sprite and moves to the given position. 880 public void replaceSprite(ABitmap s, int n, Coordinate c); 881 ///Edits a sprite attribute. 882 public void editSpriteAttribute(string S, T)(int n, T value); 883 ///Reads a sprite attribute 884 public T readSpriteAttribute(string S, T)(int n); 885 ///Replaces a sprite attribute. DEPRECATED 886 public void replaceSpriteAttribute(int n, BitmapAttrib attr); 887 ///Returns the displayed portion of the sprite. 888 public @nogc Coordinate getSlice(int n); 889 ///Writes the displayed portion of the sprite. 890 ///Returns the new slice, if invalid (greater than the bitmap, etc.) returns the old one. 891 public @nogc Coordinate setSlice(int n, Coordinate slice); 892 ///Returns the selected paletteID of the sprite. 893 public @nogc ushort getPaletteID(int n); 894 ///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation 895 ///Palette must exist in the parent Raster, otherwise AccessError might happen 896 public @nogc ushort setPaletteID(int n, ushort paletteID); 897 ///Scales bitmap horizontally 898 public @nogc int scaleSpriteHoriz(int n, int hScl); 899 ///Scales bitmap vertically 900 public @nogc int scaleSpriteVert(int n, int vScl); 901 } 902 /** 903 *Use it to call the collision detector 904 *DEPRECATED! 905 */ 906 public interface SpriteMovementListener{ 907 ///Called when a sprite is moved. 908 void spriteMoved(int ID); 909 } 910 /** 911 * General-purpose sprite controller and renderer. 912 */ 913 public class SpriteLayer : Layer, ISpriteLayer{ 914 /** 915 * Helps to determine the displaying properties and order of sprites. 916 */ 917 public struct DisplayListItem{ 918 Coordinate position; /// Stores the position relative to the origin point. Actual display position is determined by the scroll positions. 919 Coordinate slice; /// To compensate for the lack of scanline interrupt capabilities, this enables chopping off parts of a sprite. 920 //ABitmap sprite; /// Defines the sprite being displayed on the screen. 921 void* pixelData; /// Points to the pixel data. 922 //Color* palette; 923 int width; /// Width of the sprite 924 int height; /// Height of the sprite 925 int scaleHoriz; /// Horizontal scaling 926 int scaleVert; /// Vertical scaling 927 int priority; /// Used for automatic sorting and identification. 928 BitmapAttrib attributes; /// Horizontal and vertical mirroring. DEPRECATED 929 ubyte wordLength; /// Determines the word length of a sprite in a much quicker way than getting classinfo. 930 /** 931 * Selects the palette of the sprite (Bitmap4Bit: 4096X16 color palettes; Bitmap8Bit: 256X256 color palettes) 932 */ 933 ushort paletteSel; 934 this(Coordinate position, ABitmap sprite, int priority, ushort paletteSel = 0, int scaleHoriz = 1024, 935 int scaleVert = 1024){ 936 this.position = position; 937 this.width = sprite.width; 938 this.height = sprite.height; 939 this.priority = priority; 940 this.paletteSel = paletteSel; 941 this.scaleVert = scaleVert; 942 this.scaleHoriz = scaleHoriz; 943 slice = Coordinate(0,0,sprite.width,sprite.height); 944 if(sprite.classinfo == typeid(Bitmap4Bit)){ 945 wordLength = 4; 946 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 947 }else if(sprite.classinfo == typeid(Bitmap8Bit)){ 948 wordLength = 8; 949 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 950 }else if(sprite.classinfo == typeid(Bitmap16Bit)){ 951 wordLength = 16; 952 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 953 }else if(sprite.classinfo == typeid(Bitmap32Bit)){ 954 wordLength = 32; 955 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 956 } 957 } 958 this(Coordinate position, Coordinate slice, ABitmap sprite, int priority, int scaleHoriz = 1024, 959 int scaleVert = 1024){ 960 this.position = position; 961 //this.sprite = sprite; 962 //palette = sprite.getPalettePtr(); 963 this.priority = priority; 964 //this.attributes = attributes; 965 this.scaleVert = scaleVert; 966 this.scaleHoriz = scaleHoriz; 967 if(slice.top < 0) 968 slice.top = 0; 969 if(slice.left < 0) 970 slice.left = 0; 971 if(slice.right >= sprite.width) 972 slice.right = sprite.width - 1; 973 if(slice.bottom >= sprite.height) 974 slice.bottom = sprite.height - 1; 975 this.slice = slice; 976 if(sprite.classinfo == typeid(Bitmap4Bit)){ 977 wordLength = 4; 978 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 979 //palette = sprite.getPalettePtr(); 980 }else if(sprite.classinfo == typeid(Bitmap8Bit)){ 981 wordLength = 8; 982 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 983 //palette = sprite.getPalettePtr(); 984 }else if(sprite.classinfo == typeid(Bitmap16Bit)){ 985 wordLength = 16; 986 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 987 }else if(sprite.classinfo == typeid(Bitmap32Bit)){ 988 wordLength = 32; 989 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 990 } 991 } 992 /** 993 * Resets the slice to its original position. 994 */ 995 @nogc void resetSlice(){ 996 slice.left = 0; 997 slice.top = 0; 998 slice.right = position.width; 999 slice.bottom = position.height; 1000 } 1001 /** 1002 * Replaces the sprite with a new one. 1003 * If the sizes are mismatching, the top-left coordinates are left as is, but the slicing is reset. 1004 */ 1005 void replaceSprite(ABitmap sprite){ 1006 //this.sprite = sprite; 1007 //palette = sprite.getPalettePtr(); 1008 if(this.width != sprite.width || this.height != sprite.height){ 1009 this.width = sprite.width; 1010 this.height = sprite.height; 1011 position.right = position.left + cast(int)scaleNearestLength(width, scaleHoriz); 1012 position.bottom = position.top + cast(int)scaleNearestLength(height, scaleVert); 1013 } 1014 resetSlice(); 1015 if(sprite.classinfo == typeid(Bitmap4Bit)){ 1016 wordLength = 4; 1017 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 1018 //palette = sprite.getPalettePtr(); 1019 }else if(sprite.classinfo == typeid(Bitmap8Bit)){ 1020 wordLength = 8; 1021 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 1022 //palette = sprite.getPalettePtr(); 1023 }else if(sprite.classinfo == typeid(Bitmap16Bit)){ 1024 wordLength = 16; 1025 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 1026 }else if(sprite.classinfo == typeid(Bitmap32Bit)){ 1027 wordLength = 32; 1028 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 1029 } 1030 } 1031 @nogc int opCmp(in DisplayListItem d) const{ 1032 return priority - d.priority; 1033 } 1034 @nogc bool opEquals(in DisplayListItem d) const{ 1035 return priority == d.priority; 1036 } 1037 string toString() const{ 1038 return "[Position: " ~ position.toString ~ ";\nDisplayed portion: " ~ slice.toString ~";\nPriority" ~ 1039 conv.to!string(priority) ~ "; PixelData: " ~ conv.to!string(pixelData) ~ "; Attributes: " ~ attributes.toString ~ 1040 "; PaletteSel: " ~ conv.to!string(paletteSel) ~ "; WordLenght: " ~ conv.to!string(wordLength) ~ "]"; 1041 } 1042 } 1043 protected DisplayListItem[] displayList; ///Stores the display data 1044 Color[2048] src; ///Local buffer for scaling 1045 //size_t[8] prevSize; 1046 1047 public this(LayerRenderingMode renderMode = LayerRenderingMode.ALPHA_BLENDING){ 1048 setRenderingMode(renderMode); 1049 //src[0].length = 1024; 1050 } 1051 /+~this(){ 1052 foreach(p; src){ 1053 if(p) 1054 free(p); 1055 } 1056 }+/ 1057 override public void setRasterizer(int rX,int rY) { 1058 super.setRasterizer(rX,rY); 1059 /+for(int i; i < src.length; i++){ 1060 src[i].length=rY; 1061 }+/ 1062 //src.length = rY; 1063 } 1064 ///Returns the displayed portion of the sprite. 1065 public @nogc Coordinate getSlice(int n){ 1066 for(int i ; i < displayList.length ; i++){ 1067 if(displayList[i].priority == n){ 1068 return displayList[i].slice; 1069 } 1070 } 1071 return Coordinate(0,0,0,0); 1072 } 1073 ///Writes the displayed portion of the sprite. 1074 ///Returns the new slice, if invalid (greater than the bitmap, etc.) returns the old one. 1075 public @nogc Coordinate setSlice(int n, Coordinate slice){ 1076 for(int i ; i < displayList.length ; i++){ 1077 if(displayList[i].priority == n){ 1078 //test if inputted slice is valid 1079 if(slice.top >= 0 && slice.bottom >= 0 && slice.left >= 0 && slice.right >= 0 && slice.top <= slice.bottom && 1080 slice.left <= slice.right && slice.width <= displayList[i].slice.width && slice.height <= 1081 displayList[i].slice.height){ 1082 displayList[i].slice = slice; 1083 } 1084 return displayList[i].slice; 1085 } 1086 } 1087 return Coordinate(0,0,0,0); 1088 } 1089 ///Returns the selected paletteID of the sprite. 1090 public @nogc ushort getPaletteID(int n){ 1091 for(int i ; i < displayList.length ; i++){ 1092 if(displayList[i].priority == n){ 1093 return displayList[i].paletteSel; 1094 } 1095 } 1096 return 0; 1097 } 1098 ///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation 1099 ///Palette must exist in the parent Raster, otherwise AccessError might happen 1100 public @nogc ushort setPaletteID(int n, ushort paletteID){ 1101 for(int i ; i < displayList.length ; i++){ 1102 if(displayList[i].priority == n){ 1103 if(displayList[i].wordLength == 4){ 1104 displayList[i].paletteSel = paletteID & 0x0F_FF; 1105 }else if(displayList[i].wordLength == 8){ 1106 displayList[i].paletteSel = paletteID & 0x00_FF; 1107 } 1108 return displayList[i].paletteSel; 1109 } 1110 } 1111 return 0; 1112 } 1113 1114 public void addSprite(ABitmap s, int n, Coordinate c, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024){ 1115 import std.algorithm.sorting; 1116 import std.algorithm.searching; 1117 DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert); 1118 if(canFind(displayList, d)){ 1119 throw new SpritePriorityException("Sprite number already exists!"); 1120 }else{ 1121 displayList ~= d; 1122 displayList.sort!"a > b"(); 1123 } 1124 } 1125 1126 public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, int scaleVert = 1024){ 1127 import std.algorithm.sorting; 1128 import std.algorithm.searching; 1129 Coordinate c = Coordinate(x,y,x+s.width,y+s.height); 1130 DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert); 1131 if(canFind(displayList, d)){ 1132 throw new SpritePriorityException("Sprite number already exists!"); 1133 }else{ 1134 displayList ~= d; 1135 displayList.sort!"a > b"(); 1136 } 1137 } 1138 public void editSpriteAttribute(string S, T)(int n, T value){ 1139 for(int i; i < displayList.length ; i++){ 1140 if(displayList[i].priority == n){ 1141 mixin("displayList[i]."~S~" = value;"); 1142 return; 1143 } 1144 } 1145 } 1146 ///Reads a sprite attribute 1147 public T readSpriteAttribute(string S, T)(int n){ 1148 for(int i; i < displayList.length ; i++){ 1149 if(displayList[i].priority == n){ 1150 mixin("return displayList[i]."~S~";"); 1151 } 1152 } 1153 return T.init; 1154 } 1155 public void replaceSpriteAttribute(int n, BitmapAttrib attr){ 1156 for(int i; i < displayList.length ; i++){ 1157 if(displayList[i].priority == n){ 1158 displayList[i].attributes = attr; 1159 return; 1160 } 1161 } 1162 } 1163 public void replaceSprite(ABitmap s, int n){ 1164 for(int i; i < displayList.length ; i++){ 1165 if(displayList[i].priority == n){ 1166 displayList[i].replaceSprite(s); 1167 return; 1168 } 1169 } 1170 } 1171 1172 public void replaceSprite(ABitmap s, int n, int x, int y){ 1173 for(int i; i < displayList.length ; i++){ 1174 if(displayList[i].priority == n){ 1175 displayList[i].replaceSprite(s); 1176 return; 1177 } 1178 } 1179 } 1180 1181 public void replaceSprite(ABitmap s, int n, Coordinate c){ 1182 for(int i; i < displayList.length ; i++){ 1183 if(displayList[i].priority == n){ 1184 displayList[i].replaceSprite(s); 1185 return; 1186 } 1187 } 1188 } 1189 1190 /*public ushort getTransparencyIndex(){ 1191 return transparencyIndex; 1192 }*/ 1193 1194 public void removeSprite(int n){ 1195 DisplayListItem[] ndl; 1196 ndl.reserve(displayList.length); 1197 for(int i; i < displayList.length ; i++){ 1198 if(displayList[i].priority != n){ 1199 ndl ~= displayList[i]; 1200 } 1201 } 1202 displayList = ndl; 1203 } 1204 public @nogc void moveSprite(int n, int x, int y){ 1205 for(int i; i < displayList.length ; i++){ 1206 if(displayList[i].priority == n){ 1207 displayList[i].position.move(x,y); 1208 return; 1209 } 1210 } 1211 } 1212 public @nogc void relMoveSprite(int n, int x, int y){ 1213 for(int i; i < displayList.length ; i++){ 1214 if(displayList[i].priority == n){ 1215 displayList[i].position.relMove(x,y); 1216 return; 1217 } 1218 } 1219 } 1220 1221 public @nogc Coordinate getSpriteCoordinate(int n){ 1222 for(int i; i < displayList.length ; i++){ 1223 if(displayList[i].priority == n){ 1224 return displayList[i].position; 1225 } 1226 } 1227 return Coordinate(0,0,0,0); 1228 } 1229 ///Scales sprite horizontally. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 1230 public @nogc int scaleSpriteHoriz(int n, int hScl){ 1231 if (!hScl) return -1; 1232 for(int i; i < displayList.length ; i++){ 1233 if(displayList[i].priority == n){ 1234 displayList[i].scaleHoriz = hScl; 1235 const int newWidth = cast(int)scaleNearestLength(displayList[i].width, hScl); 1236 displayList[i].slice.right = newWidth; 1237 return displayList[i].position.right = displayList[i].position.left + newWidth; 1238 } 1239 } 1240 return -2; 1241 } 1242 ///Scales sprite vertically. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 1243 public @nogc int scaleSpriteVert(int n, int vScl){ 1244 if (!vScl) return -1; 1245 for(int i; i < displayList.length ; i++){ 1246 if(displayList[i].priority == n){ 1247 displayList[i].scaleVert = vScl; 1248 const int newHeight = cast(int)scaleNearestLength(displayList[i].height, vScl); 1249 displayList[i].slice.bottom = newHeight; 1250 return displayList[i].position.bottom = displayList[i].position.top + newHeight; 1251 } 1252 } 1253 return -2; 1254 } 1255 1256 public override @nogc void updateRaster(void* workpad, int pitch, Color* palette){ 1257 foreach(i ; displayList){ 1258 const int left = i.position.left + i.slice.left; 1259 const int top = i.position.top + i.slice.top; 1260 const int right = i.position.left + i.slice.right; 1261 const int bottom = i.position.top + i.slice.bottom; 1262 /+if((i.position.right > sX && i.position.bottom > sY) && (i.position.left < sX + rasterX && i.position.top < sY + 1263 rasterY)){+/ 1264 if((right > sX && left < sX + rasterX) && (bottom > sY && top < sY + rasterY) && i.slice.width && i.slice.height){ 1265 int offsetXA = sX > left ? sX - left : 0;//Left hand side offset, zero if not obscured 1266 const int offsetXB = sX + rasterX < right ? right - rasterX : 0; //Right hand side offset, zero if not obscured 1267 const int offsetYA = sY > top ? sY - top : 0; //top offset of sprite, zero if not obscured 1268 const int offsetYB = sY + rasterY < bottom ? bottom - rasterY : 0; //bottom offset of sprite, zero if not obscured 1269 //const int offsetYB0 = cast(int)scaleNearestLength(offsetYB, i.scaleVert); 1270 const int sizeX = i.slice.width(); //total displayed width 1271 const int offsetX = left - sX; 1272 const int length = sizeX - offsetXA - offsetXB; 1273 //int lengthY = i.slice.height - offsetYA - offsetYB; 1274 //const int lfour = length * 4; 1275 const int offsetY = sY < top ? (top-sY)*pitch : 0; //used if top portion of the sprite is off-screen 1276 //offset = i.scaleVert % 1024; 1277 const int scaleVertAbs = i.scaleVert * (i.scaleVert < 0 ? -1 : 1); //absolute value of vertical offset, used in various calculations 1278 //int offset, prevOffset; 1279 const int offsetAmount = scaleVertAbs <= 1024 ? 1024 : scaleVertAbs; //used to limit the amount of re-rendering every line 1280 //offset = offsetYA<<10; 1281 const int offsetYA0 = cast(int)(cast(double)offsetYA / (1024.0 / cast(double)scaleVertAbs)); //amount of skipped lines (I think) TODO: remove floating-point arithmetic 1282 const int sizeXOffset = i.width * (i.scaleVert < 0 ? -1 : 1); 1283 int prevOffset = offsetYA0 * offsetAmount; // 1284 int offset = offsetYA0 * scaleVertAbs; 1285 const size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB); //determines offset based on mirroring 1286 const int scalelength = i.position.width < 2048 ? i.width : 2048; //limit width to 2048, the minimum required for this scaling method to work 1287 void* dest = workpad + (offsetX + offsetXA)*4 + offsetY; 1288 switch(i.wordLength){ 1289 case 4: 1290 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * ((i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0)>>1); 1291 for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){ 1292 horizontalScaleNearest4BitAndCLU(p0, src.ptr, palette + (i.paletteSel<<4), scalelength, offsetXA & 1, 1293 i.scaleHoriz); 1294 prevOffset += offsetAmount; 1295 for(; offset < prevOffset; offset += scaleVertAbs){ 1296 y++; 1297 mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length); 1298 dest += pitch; 1299 } 1300 p0 += sizeXOffset >>> 1; 1301 } 1302 //} 1303 break; 1304 case 8: 1305 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 1306 for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){ 1307 horizontalScaleNearestAndCLU(p0, src.ptr, palette + (i.paletteSel<<8), scalelength, i.scaleHoriz); 1308 prevOffset += 1024; 1309 for(; offset < prevOffset; offset += scaleVertAbs){ 1310 y++; 1311 mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length); 1312 dest += pitch; 1313 } 1314 p0 += sizeXOffset; 1315 } 1316 break; 1317 case 16: 1318 ushort* p0 = cast(ushort*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 1319 for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){ 1320 horizontalScaleNearestAndCLU(p0, src.ptr, palette, scalelength, i.scaleHoriz); 1321 prevOffset += 1024; 1322 for(; offset < prevOffset; offset += scaleVertAbs){ 1323 y++; 1324 mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length); 1325 dest += pitch; 1326 } 1327 p0 += sizeXOffset; 1328 } 1329 break; 1330 case 32: 1331 Color* p0 = cast(Color*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 1332 for(int y = offsetYA ; y < i.slice.height - offsetYB ; ){ 1333 horizontalScaleNearest(p0, src.ptr, scalelength, i.scaleHoriz); 1334 prevOffset += 1024; 1335 for(; offset < prevOffset; offset += scaleVertAbs){ 1336 y++; 1337 mainRenderingFunction(cast(uint*)src.ptr + p0offset, cast(uint*)dest, length); 1338 dest += pitch; 1339 } 1340 p0 += sizeXOffset; 1341 } 1342 //} 1343 break; 1344 default: 1345 break; 1346 } 1347 1348 } 1349 } 1350 //foreach(int threadOffset; threads.parallel) 1351 //free(src[threadOffset]); 1352 } 1353 1354 } 1355 /** 1356 * Puts various effects on the framebuffer (XOR blitter, etc). 1357 */ 1358 public class EffectLayer : Layer{ 1359 /** 1360 * Stores various commands for effects 1361 */ 1362 public class EffectLayerCommand{ 1363 public CommandType command; 1364 public Coordinate[] coordinates; 1365 public Color[] colors; 1366 public ushort[] indexedColors; 1367 public int[] values; 1368 public this(CommandType command, Coordinate[] coordinates, Color[] colors, int[] values = null){ 1369 this.command = command; 1370 this.coordinates = coordinates; 1371 this.indexedColors = null; 1372 this.colors = colors; 1373 this.values = values; 1374 } 1375 public this(CommandType command, Coordinate[] coordinates, ushort[] indexedColors, int[] values = null){ 1376 this.command = command; 1377 this.coordinates = coordinates; 1378 this.indexedColors = indexedColors; 1379 this.colors = null; 1380 this.values = values; 1381 } 1382 } 1383 public enum CommandType : ubyte{ 1384 /// Does nothing, placeholder command. 1385 NONE = 0, 1386 /** 1387 * Does a XOR blitter line. Parameters: 1388 * coordinate[0]: Begins the line from the top-left corner until the right corner. Bottom value is discarded. 1389 * color[0]: The 32 bit colorvector. 1390 */ 1391 XORBLITTERLINE = 1, 1392 /** 1393 * Does a XOR blitter box. Parameters: 1394 * coordinate[0]: The coordinates where the box should be drawn. 1395 * color[0]: The 32 bit colorvector. 1396 */ 1397 XORBLITTERBOX = 2, 1398 /** 1399 * Offsets a line by a given value. Parameters: 1400 * coordinate[0]: Begins the line from the top-left corner until the right corner. Bottom value is discarded. 1401 * value[0]: The amount which the line will be offsetted. 1402 * 1403 * NOTE: Be careful with this operation, if the algorithm has to write out from the screen, it'll cause a MemoryAccessViolationError. 1404 * Overscanning will enable to write outside of it as well as offsetting otherwise off-screen elements onto the screen. 1405 */ 1406 LINEOFFSET = 3 1407 } 1408 private EffectLayerCommand[int] commandList; 1409 private int[] commandListPriorities; 1410 public this(){ 1411 1412 } 1413 /** 1414 * Adds a new command with the specified values. 1415 */ 1416 public void addCommand(int priority, EffectLayerCommand command){ 1417 import std.algorithm.sorting; 1418 commandList[priority] = command; 1419 commandListPriorities ~= priority; 1420 commandListPriorities.sort(); 1421 } 1422 /** 1423 * Removes a command at the specified priority. 1424 */ 1425 public void removeCommand(int priority){ 1426 commandList.remove(priority); 1427 int[] newCommandListPriorities; 1428 for(int i ; i < commandListPriorities.length ; i++){ 1429 if(commandListPriorities[i] != priority){ 1430 newCommandListPriorities ~= commandListPriorities[i]; 1431 } 1432 } 1433 commandListPriorities = newCommandListPriorities; 1434 } 1435 1436 override public void updateRaster(void* workpad,int pitch,Color* palette) { 1437 /*foreach(int i; commandListPriorities){ 1438 switch(commandList[i].command){ 1439 case CommandType.XORBLITTERLINE: 1440 int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left; 1441 if(commandList[i].indexedColors is null){ 1442 xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width()); 1443 }else{ 1444 xorBlitter(workpad + offset,palette[commandList[i].indexedColors[0]],commandList[i].coordinates[0].width()); 1445 } 1446 break; 1447 case CommandType.XORBLITTERBOX: 1448 int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left; 1449 if(commandList[i].indexedColors is null){ 1450 for(int y = commandList[i].coordinates[0].top; y < commandList[i].coordinates[0].bottom; y++){ 1451 xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width()); 1452 offset += pitch; 1453 } 1454 }else{ 1455 for(int y = commandList[i].coordinates[0].top; y < commandList[i].coordinates[0].bottom; y++){ 1456 xorBlitter(workpad + offset,commandList[i].colors[0],commandList[i].coordinates[0].width()); 1457 offset += pitch; 1458 } 1459 } 1460 break; 1461 case CommandType.LINEOFFSET: 1462 int offset = (commandList[i].coordinates[0].top * pitch) + commandList[i].coordinates[0].left; 1463 copyRegion(workpad + offset, workpad + offset + commandList[i].values[0], commandList[i].coordinates[0].width()); 1464 break; 1465 default: 1466 break; 1467 } 1468 }*/ 1469 } 1470 1471 } 1472 unittest{ 1473 TransformableTileLayer test = new TransformableTileLayer(); 1474 }