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