1 module pixelperfectengine.graphics.layers.spritelayer; 2 3 public import pixelperfectengine.graphics.layers.base; 4 5 import collections.treemap; 6 import collections.sortedlist; 7 import std.bitmanip : bitfields; 8 import bitleveld.datatypes; 9 10 /** 11 * General-purpose sprite controller and renderer. 12 */ 13 public class SpriteLayer : Layer, ISpriteLayer { 14 /** 15 * Helps to determine the displaying properties and order of sprites. 16 */ 17 public struct DisplayListItem { 18 Box position; /// Stores the position relative to the origin point. Actual display position is determined by the scroll positions. 19 Box slice; /// To compensate for the lack of scanline interrupt capabilities, this enables chopping off parts of a sprite. 20 void* pixelData; /// Points to the pixel data. 21 /** 22 * From version 0.10.0 onwards, each sprites can have their own rendering function set up to 23 * allow different effect on a single layer. 24 * If not specified otherwise, the layer's main rendering function will be used instead. 25 * Custom rendering functions can be written by the user, it requires knowledge of writing 26 * pixel shader-like functions using fixed-point arithmetics. Use of vector optimizatons 27 * techniques (SSE2, AVX, NEON, etc) are needed for optimal performance. 28 */ 29 @nogc pure nothrow void function(uint* src, uint* dest, size_t length, ubyte value) renderFunc; 30 int width; /// Width of the sprite 31 int height; /// Height of the sprite 32 int scaleHoriz; /// Horizontal scaling 33 int scaleVert; /// Vertical scaling 34 int priority; /// Used for automatic sorting and identification. 35 /** 36 * Selects the palette of the sprite. 37 * Amount of accessable color depends on the palette access shifting value. A value of 8 enables 38 * 256 * 256 color palettes, and a value of 4 enables 4096 * 16 color palettes. 39 * `paletteSh` can be set lower than what the bitmap is capable of storing at its maximum, this 40 * can enable the packing of more palettes within the main one, e.g. a `paletteSh` value of 7 41 * means 512 * 128 color palettes, while the bitmaps are still stored in the 8 bit "chunky" mode 42 * instead of 7 bit planar that would require way more processing power. However this doesn't 43 * limit the bitmap's ability to access 256 colors, and this can result in memory leakage if 44 * the end developer isn't careful enough. 45 */ 46 ushort paletteSel; 47 //ubyte flags; /// Flags packed into a single byte (bitmapType, paletteSh) 48 mixin(bitfields!( 49 ubyte, "paletteSh", 4, 50 ubyte, "bmpType", 4, 51 )); 52 ubyte masterAlpha = ubyte.max;/// Sets the master alpha value of the sprite, e.g. opacity 53 //ubyte wordLength; /// Determines the word length of a sprite in a much quicker way than getting classinfo. 54 //ubyte paletteSh; /// Palette shifting value. 8 is default for 8 bit, and 4 for 4 bit bitmaps. (see paletteSel for more info) 55 //static enum ubyte PALETTESH_MASK = 0x0F; /// Mask for paletteSh 56 //static enum ubyte BMPTYPE_MASK = 0x80; /// Mask for bmpType 57 /** 58 * Creates a display list item with palette selector. 59 */ 60 this(Coordinate position, ABitmap sprite, int priority, ushort paletteSel = 0, int scaleHoriz = 1024, 61 int scaleVert = 1024) pure @trusted nothrow { 62 this.position = position; 63 this.width = sprite.width; 64 this.height = sprite.height; 65 this.priority = priority; 66 this.paletteSel = paletteSel; 67 this.scaleVert = scaleVert; 68 this.scaleHoriz = scaleHoriz; 69 slice = Coordinate(0,0,sprite.width,sprite.height); 70 if (typeid(sprite) is typeid(Bitmap4Bit)) { 71 bmpType = BitmapTypes.Bmp4Bit; 72 paletteSh = 4; 73 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 74 } else if (typeid(sprite) is typeid(Bitmap8Bit)) { 75 bmpType = BitmapTypes.Bmp8Bit; 76 paletteSh = 8; 77 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 78 } else if (typeid(sprite) is typeid(Bitmap16Bit)) { 79 bmpType = BitmapTypes.Bmp16Bit; 80 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 81 } else if (typeid(sprite) is typeid(Bitmap32Bit)) { 82 bmpType = BitmapTypes.Bmp32Bit; 83 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 84 } 85 } 86 /** 87 * Creates a display list item without palette selector. 88 */ 89 this(Coordinate position, Coordinate slice, ABitmap sprite, int priority, int scaleHoriz = 1024, 90 int scaleVert = 1024) pure @trusted nothrow { 91 if(slice.top < 0) 92 slice.top = 0; 93 if(slice.left < 0) 94 slice.left = 0; 95 if(slice.right >= sprite.width) 96 slice.right = sprite.width - 1; 97 if(slice.bottom >= sprite.height) 98 slice.bottom = sprite.height - 1; 99 this.slice = slice; 100 this(position, sprite, priority, paletteSel, scaleHoriz, scaleVert); 101 } 102 /+ 103 /// Palette shifting value. 8 is default for 8 bit, and 4 for 4 bit bitmaps. (see paletteSel for more info) 104 @property ubyte paletteSh() @safe @nogc pure nothrow const { 105 return cast(ubyte)flags & PALETTESH_MASK; 106 } 107 /// Palette shifting value. 8 is default for 8 bit, and 4 for 4 bit bitmaps. (see paletteSel for more info) 108 @property ubyte paletteSh(ubyte val) @safe @nogc pure nothrow { 109 flags &= ~PALETTESH_MASK; 110 flags |= val; 111 return cast(ubyte)flags & PALETTESH_MASK; 112 } 113 /// Defines the type of bitmap the sprite is using. This method is much faster and simpler than checking the class type of the bitmap. 114 @property BitmapTypes bmpType() @safe @nogc pure nothrow const { 115 return cast(BitmapTypes)((flags & BMPTYPE_MASK) >>> 4); 116 } 117 /// Defines the type of bitmap the sprite is using. This method is much faster and simpler than checking the class type of the bitmap. 118 @property BitmapTypes bmpType(BitmapTypes val) @safe @nogc pure nothrow { 119 flags &= ~BMPTYPE_MASK; 120 flags |= cast(ubyte)val << 4; 121 return bmpType; 122 }+/ 123 /** 124 * Resets the slice to its original position. 125 */ 126 void resetSlice() pure @nogc @safe nothrow { 127 slice.left = 0; 128 slice.top = 0; 129 slice.right = position.width - 1; 130 slice.bottom = position.height - 1; 131 } 132 /** 133 * Replaces the sprite with a new one. 134 * If the sizes are mismatching, the top-left coordinates are left as is, but the slicing is reset. 135 */ 136 void replaceSprite(ABitmap sprite) @trusted pure nothrow { 137 //this.sprite = sprite; 138 //palette = sprite.getPalettePtr(); 139 if(this.width != sprite.width || this.height != sprite.height){ 140 this.width = sprite.width; 141 this.height = sprite.height; 142 position.right = position.left + cast(int)scaleNearestLength(width, scaleHoriz); 143 position.bottom = position.top + cast(int)scaleNearestLength(height, scaleVert); 144 resetSlice(); 145 } 146 if (typeid(sprite) is typeid(Bitmap4Bit)) { 147 bmpType = BitmapTypes.Bmp4Bit; 148 paletteSh = 4; 149 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 150 } else if (typeid(sprite) is typeid(Bitmap8Bit)) { 151 bmpType = BitmapTypes.Bmp8Bit; 152 paletteSh = 8; 153 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 154 } else if (typeid(sprite) is typeid(Bitmap16Bit)) { 155 bmpType = BitmapTypes.Bmp16Bit; 156 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 157 } else if (typeid(sprite) is typeid(Bitmap32Bit)) { 158 bmpType = BitmapTypes.Bmp32Bit; 159 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 160 } 161 } 162 @nogc int opCmp(in DisplayListItem d) const pure @safe nothrow { 163 return priority - d.priority; 164 } 165 @nogc bool opEquals(in DisplayListItem d) const pure @safe nothrow { 166 return priority == d.priority; 167 } 168 @nogc int opCmp(in int pri) const pure @safe nothrow { 169 return priority - pri; 170 } 171 @nogc bool opEquals(in int pri) const pure @safe nothrow { 172 return priority == pri; 173 } 174 175 string toString() const { 176 import std.conv : to; 177 return "{Position: " ~ position.toString ~ ";\nDisplayed portion: " ~ slice.toString ~";\nPriority: " ~ 178 to!string(priority) ~ "; PixelData: " ~ to!string(pixelData) ~ 179 "; PaletteSel: " ~ to!string(paletteSel) ~ "; bmpType: " ~ to!string(bmpType) ~ "}"; 180 } 181 } 182 alias DisplayList = TreeMap!(int, DisplayListItem); 183 alias OnScreenList = SortedList!(int, "a < b", false); 184 //protected DisplayListItem[] displayList; ///Stores the display data 185 protected DisplayList allSprites; ///All sprites of this layer 186 protected OnScreenList displayedSprites; ///Sprites that are being displayed 187 protected Color[2048] src; ///Local buffer for scaling 188 //size_t[8] prevSize; 189 ///Default ctor 190 public this(RenderingMode renderMode = RenderingMode.AlphaBlend) nothrow @safe { 191 setRenderingMode(renderMode); 192 //Bug workaround: Sometimes when attempting to append an element to a zero-length array, it causes an exception 193 //to be thrown, due to access errors. This bug is unstable, and as such hard to debug for (memory leakage issue?) 194 displayedSprites.reserve(128); 195 //src[0].length = 1024; 196 } 197 /** 198 * Checks all sprites for whether they're on screen or not. 199 * Called every time the layer is being scrolled. 200 */ 201 public void checkAllSprites() @safe pure nothrow { 202 foreach (key; allSprites) { 203 checkSprite(key); 204 } 205 } 206 /** 207 * Checks whether a sprite would be displayed on the screen, then updates the display list. 208 * Returns true if it's on screen. 209 */ 210 public bool checkSprite(int n) @safe pure nothrow { 211 return checkSprite(allSprites[n]); 212 } 213 ///Ditto. 214 protected bool checkSprite(DisplayListItem sprt) @safe pure nothrow { 215 //assert(sprt.bmpType != BitmapTypes.Undefined && sprt.pixelData, "DisplayList error!"); 216 if(sprt.slice.width && sprt.slice.height 217 && (sprt.position.right > sX && sprt.position.bottom > sY && 218 sprt.position.left < sX + rasterX && sprt.position.top < sY + rasterY)) { 219 displayedSprites.put(sprt.priority); 220 return true; 221 } else { 222 displayedSprites.removeByElem(sprt.priority); 223 return false; 224 } 225 } 226 /** 227 * Searches the DisplayListItem by priority and returns it. 228 * Can be used for external use without any safety issues. 229 */ 230 public DisplayListItem getDisplayListItem(int n) @nogc pure @safe nothrow { 231 return allSprites[n]; 232 } 233 /** 234 * Searches the DisplayListItem by priority and returns it. 235 * Intended for internal use, as it returns it as a reference value. 236 */ 237 protected DisplayListItem* getDisplayListItem_internal(int n) @nogc pure @safe nothrow { 238 return allSprites.ptrOf(n); 239 } 240 override public void setRasterizer(int rX,int rY) { 241 super.setRasterizer(rX,rY); 242 } 243 ///Returns the displayed portion of the sprite. 244 public Coordinate getSlice(int n) @nogc pure @safe nothrow { 245 return getDisplayListItem(n).slice; 246 } 247 ///Writes the displayed portion of the sprite. 248 ///Returns the new slice, if invalid (greater than the bitmap, etc.) returns Coordinate.init. 249 public Coordinate setSlice(int n, Coordinate slice) @safe pure nothrow { 250 DisplayListItem* sprt = allSprites.ptrOf(n); 251 if(sprt) { 252 sprt.slice = slice; 253 checkSprite(*sprt); 254 return sprt.slice; 255 } else { 256 return Coordinate.init; 257 } 258 } 259 ///Returns the selected paletteID of the sprite. 260 public ushort getPaletteID(int n) @nogc pure @safe nothrow { 261 return getDisplayListItem(n).paletteSel; 262 } 263 ///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation 264 ///Palette must exist in the parent Raster, otherwise AccessError might happen 265 public ushort setPaletteID(int n, ushort paletteID) @nogc pure @safe nothrow { 266 return getDisplayListItem_internal(n).paletteSel = paletteID; 267 } 268 /** 269 * Adds a sprite to the layer. 270 */ 271 public void addSprite(ABitmap s, int n, Box c, ushort paletteSel = 0, int scaleHoriz = 1024, 272 int scaleVert = 1024) @safe nothrow { 273 DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert); 274 d.renderFunc = mainRenderingFunction; 275 synchronized 276 allSprites[n] = d; 277 checkSprite(d); 278 } 279 ///Ditto 280 public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, 281 int scaleVert = 1024) @safe nothrow { 282 DisplayListItem d = DisplayListItem(Box(x, y, x + s.width - 1, y + s.height - 1), s, n, paletteSel, scaleHoriz, 283 scaleVert); 284 d.renderFunc = mainRenderingFunction; 285 synchronized 286 allSprites[n] = d; 287 checkSprite(d); 288 } 289 /** 290 * Replaces the bitmap of the given sprite. 291 */ 292 public void replaceSprite(ABitmap s, int n) @safe nothrow { 293 DisplayListItem* sprt = getDisplayListItem_internal(n); 294 sprt.replaceSprite(s); 295 checkSprite(*sprt); 296 } 297 ///Ditto with move 298 public void replaceSprite(ABitmap s, int n, int x, int y) @safe nothrow { 299 DisplayListItem* sprt = getDisplayListItem_internal(n); 300 sprt.replaceSprite(s); 301 sprt.position.move(x, y); 302 checkSprite(*sprt); 303 } 304 ///Ditto with move 305 public void replaceSprite(ABitmap s, int n, Coordinate c) @safe nothrow { 306 DisplayListItem* sprt = allSprites.ptrOf(n); 307 sprt.replaceSprite(s); 308 sprt.position = c; 309 checkSprite(*sprt); 310 } 311 /** 312 * Removes a sprite from both displaylists by priority. 313 */ 314 public void removeSprite(int n) @safe nothrow { 315 synchronized { 316 displayedSprites.removeByElem(n); 317 allSprites.remove(n); 318 } 319 } 320 ///Clears all sprite from the layer. 321 public void clear() @safe nothrow { 322 displayedSprites = OnScreenList.init; 323 allSprites = DisplayList.init; 324 } 325 /** 326 * Moves a sprite to the given position. 327 */ 328 public void moveSprite(int n, int x, int y) @safe nothrow { 329 DisplayListItem* sprt = allSprites.ptrOf(n); 330 sprt.position.move(x, y); 331 checkSprite(*sprt); 332 } 333 /** 334 * Moves a sprite by the given amount. 335 */ 336 public void relMoveSprite(int n, int x, int y) @safe nothrow { 337 DisplayListItem* sprt = allSprites.ptrOf(n); 338 sprt.position.relMove(x, y); 339 checkSprite(*sprt); 340 } 341 ///Sets the rendering function for the sprite (defaults to the layer's rendering function) 342 public void setSpriteRenderingMode(int n, RenderingMode mode) @safe nothrow { 343 DisplayListItem* sprt = allSprites.ptrOf(n); 344 sprt.renderFunc = getRenderingFunc(mode); 345 } 346 public @nogc Coordinate getSpriteCoordinate(int n) @safe nothrow { 347 return allSprites[n].position; 348 } 349 ///Scales sprite horizontally. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 350 public int scaleSpriteHoriz(int n, int hScl) @trusted nothrow { 351 DisplayListItem* sprt = allSprites.ptrOf(n); 352 if(!sprt) return -2; 353 else if(!hScl) return -1; 354 else { 355 sprt.scaleHoriz = hScl; 356 const int newWidth = cast(int)scaleNearestLength(sprt.width, hScl); 357 sprt.slice.right = newWidth; 358 sprt.position.right = sprt.position.left + newWidth; 359 checkSprite(*sprt); 360 return newWidth; 361 } 362 } 363 ///Scales sprite vertically. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 364 public int scaleSpriteVert(int n, int vScl) @trusted nothrow { 365 DisplayListItem* sprt = allSprites.ptrOf(n); 366 if(!sprt) return -2; 367 else if(!vScl) return -1; 368 else { 369 sprt.scaleVert = vScl; 370 const int newHeight = cast(int)scaleNearestLength(sprt.height, vScl); 371 sprt.slice.bottom = newHeight; 372 sprt.position.bottom = sprt.position.top + newHeight; 373 checkSprite(*sprt); 374 return newHeight; 375 } 376 /+if (!vScl) return -1; 377 for(int i; i < displayList.length ; i++){ 378 if(displayList[i].priority == n){ 379 displayList[i].scaleVert = vScl; 380 const int newHeight = cast(int)scaleNearestLength(displayList[i].height, vScl); 381 displayList[i].slice.bottom = newHeight; 382 return displayList[i].position.bottom = displayList[i].position.top + newHeight; 383 } 384 } 385 return -2;+/ 386 } 387 ///Gets the sprite's current horizontal scale value 388 public int getScaleSpriteHoriz(int n) @nogc @trusted nothrow { 389 return allSprites[n].scaleHoriz; 390 } 391 ///Gets the sprite's current vertical scale value 392 public int getScaleSpriteVert(int n) @nogc @trusted nothrow { 393 return allSprites[n].scaleVert; 394 } 395 public override @nogc void updateRaster(void* workpad, int pitch, Color* palette) { 396 /* 397 * BUG 1: If sprite is wider than 2048 pixels, it'll cause issues (mostly memory leaks) due to a hack. (Fixed!) 398 * BUG 2: Obscuring the top part of a sprite when scaleVert is not 1024 will cause glitches. (Fixed!!!) 399 */ 400 foreach (priority ; displayedSprites) { 401 //foreach(i ; displayList){ 402 DisplayListItem i = allSprites[priority]; 403 const int left = i.position.left + i.slice.left; 404 const int top = i.position.top + i.slice.top; 405 const int right = i.position.left + i.slice.right; 406 const int bottom = i.position.top + i.slice.bottom; 407 /+if((i.position.right > sX && i.position.bottom > sY) && (i.position.left < sX + rasterX && i.position.top < sY + 408 rasterY)){+/ 409 //if((right > sX && left < sX + rasterX) && (bottom > sY && top < sY + rasterY) && i.slice.width && i.slice.height){ 410 int offsetXA = sX > left ? sX - left : 0;//Left hand side offset, zero if not obscured 411 const int offsetXB = sX + rasterX < right ? right - (sX + rasterX) : 0; //Right hand side offset, zero if not obscured 412 const int offsetYA = sY > top ? sY - top : 0; //top offset of sprite, zero if not obscured 413 const int offsetYB = sY + rasterY < bottom ? bottom - (sY + rasterY) + 1 : 1; //bottom offset of sprite, zero if not obscured 414 //const int offsetYB0 = cast(int)scaleNearestLength(offsetYB, i.scaleVert); 415 const int sizeX = i.slice.width(); //total displayed width after slicing 416 const int offsetX = left - sX; 417 const int length = sizeX - offsetXA - offsetXB - 1; //total displayed width after considering screen borders 418 //int lengthY = i.slice.height - offsetYA - offsetYB; 419 //const int lfour = length * 4; 420 const int offsetY = sY < top ? (top-sY)*pitch : 0; //used if top portion of the sprite is off-screen 421 //offset = i.scaleVert % 1024; 422 const int scaleVertAbs = i.scaleVert * (i.scaleVert < 0 ? -1 : 1); //absolute value of vertical scaling, used in various calculations 423 const int scaleHorizAbs = i.scaleHoriz * (i.scaleHoriz < 0 ? -1 : 1); 424 //const int offsetAmount = scaleVertAbs;//= scaleVertAbs <= 1024 ? 1024 : scaleVertAbs; //used to limit the amount of re-rendering every line 425 const int offsetYA0 = (offsetYA * scaleVertAbs)>>>10; //amount of skipped lines in the bitmap source 426 //const int offsetYA0 = cast(int)(cast(double)offsetYA / (1024.0 / cast(double)scaleVertAbs)); //amount of skipped lines (I think) TODO: remove floating-point arithmetic 427 const int sizeXOffset = i.width * (i.scaleVert < 0 ? -1 : 1); 428 int offsetTarget; //the target fractional lines 429 int offset = (offsetYA * scaleVertAbs) & 1023; //the current amount of fractional lines, also contains the fractional offset bias by defauls 430 //const size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB); //determines offset based on mirroring 431 // HACK: as I couldn't figure out a better method yet I decided to scale a whole line, which has a lot of problems 432 const int scalelength = i.position.width < 2048 ? i.width : 2048; //limit width to 2048, the minimum required for this scaling method to work 433 void* dest = workpad + (offsetX + offsetXA)*4 + offsetY; 434 final switch (i.bmpType) with (BitmapTypes) { 435 case Bmp4Bit: 436 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * ((i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0)>>1); 437 const size_t _pitch = i.width>>>1; 438 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 439 /+horizontalScaleNearest4BitAndCLU(p0, src.ptr, palette + (i.paletteSel<<i.paletteSh), scalelength, offsetXA & 1, 440 i.scaleHoriz);+/ 441 horizontalScaleNearestAndCLU(QuadArray(p0[0.._pitch], i.width), src.ptr, palette + (i.paletteSel<<i.paletteSh), 442 length, i.scaleHoriz, offsetXA * scaleHorizAbs); 443 offsetTarget += 1024; 444 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 445 y++; 446 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 447 dest += pitch; 448 } 449 p0 += _pitch; 450 } 451 //} 452 break; 453 case Bmp8Bit: 454 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 455 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 456 //horizontalScaleNearestAndCLU(p0, src.ptr, palette + (i.paletteSel<<i.paletteSh), scalelength, i.scaleHoriz); 457 horizontalScaleNearestAndCLU(p0[0..i.width], src.ptr, palette + (i.paletteSel<<i.paletteSh), length, i.scaleHoriz, 458 offsetXA * scaleHorizAbs); 459 offsetTarget += 1024; 460 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 461 y++; 462 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 463 dest += pitch; 464 } 465 p0 += sizeXOffset; 466 } 467 break; 468 case Bmp16Bit: 469 ushort* p0 = cast(ushort*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 470 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 471 //horizontalScaleNearestAndCLU(p0, src.ptr, palette, scalelength, i.scaleHoriz); 472 horizontalScaleNearestAndCLU(p0[0..i.width], src.ptr, palette, length, i.scaleHoriz, offsetXA * scaleHorizAbs); 473 offsetTarget += 1024; 474 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 475 y++; 476 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 477 dest += pitch; 478 } 479 p0 += sizeXOffset; 480 } 481 break; 482 case Bmp32Bit: 483 Color* p0 = cast(Color*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 484 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 485 horizontalScaleNearest(p0[0..i.width], src, scalelength, i.scaleHoriz, offsetXA * scaleHorizAbs); 486 offsetTarget += 1024; 487 for (; offset < offsetTarget && y < i.slice.height - offsetYB; offset += scaleVertAbs) { 488 y++; 489 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 490 dest += pitch; 491 } 492 p0 += sizeXOffset; 493 } 494 //} 495 break; 496 case Undefined, Bmp1Bit, Bmp2Bit, Planar: 497 break; 498 } 499 500 //} 501 } 502 //foreach(int threadOffset; threads.parallel) 503 //free(src[threadOffset]); 504 } 505 ///Absolute scrolling. 506 public override void scroll(int x, int y) @safe pure nothrow { 507 sX = x; 508 sY = y; 509 checkAllSprites; 510 } 511 ///Relative scrolling. Positive values scrolls the layer left and up, negative values scrolls the layer down and right. 512 public override void relScroll(int x, int y) @safe pure nothrow { 513 sX += x; 514 sY += y; 515 checkAllSprites; 516 } 517 }