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 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 /** 54 * Creates a display list item according to the newer architecture. 55 * Params: 56 * x = X position of the sprite. 57 * y = Y position of the sprite. 58 * sprite = The bitmap to be used as the sprite. 59 * pri = Priority identifier. 60 * paletteSel = Selects a given palette. 61 * paletteSh = Determines how many bits are being used. 62 * alpha = The transparency of the sprite. 63 * scaleHoriz = Horizontal scaling of the sprite. 1024 is the base value, anything less will stretch, greater will shrink the sprite. 64 * scaleVert = Ditto for vertical. 65 */ 66 this(int x, int y, ABitmap sprite, int priority, ushort paletteSel = 0, ubyte paletteSh = 0, ubyte alpha = ubyte.max, 67 int scaleHoriz = 1024, int scaleVert = 1024) pure @trusted nothrow { 68 this.width = sprite.width(); 69 this.height = sprite.height(); 70 this.position = Box.bySize(x, y, cast(int)scaleNearestLength(width, scaleHoriz), 71 cast(int)scaleNearestLength(height, scaleVert)); 72 this.priority = priority; 73 this.paletteSel = paletteSel; 74 this.scaleVert = scaleVert; 75 this.scaleHoriz = scaleHoriz; 76 slice = Box(0,0,sprite.width,sprite.height); 77 if (typeid(sprite) is typeid(Bitmap2Bit)) { 78 bmpType = BitmapTypes.Bmp2Bit; 79 this.paletteSh = paletteSh ? paletteSh : 2; 80 pixelData = (cast(Bitmap2Bit)(sprite)).getPtr; 81 } else if (typeid(sprite) is typeid(Bitmap4Bit)) { 82 bmpType = BitmapTypes.Bmp4Bit; 83 this.paletteSh = paletteSh ? paletteSh : 4; 84 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 85 } else if (typeid(sprite) is typeid(Bitmap8Bit)) { 86 bmpType = BitmapTypes.Bmp8Bit; 87 this.paletteSh = paletteSh ? paletteSh : 8; 88 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 89 } else if (typeid(sprite) is typeid(Bitmap16Bit)) { 90 bmpType = BitmapTypes.Bmp16Bit; 91 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 92 } else if (typeid(sprite) is typeid(Bitmap32Bit)) { 93 bmpType = BitmapTypes.Bmp32Bit; 94 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 95 } 96 } 97 /** 98 * Resets the slice to its original position. 99 */ 100 void resetSlice() pure @nogc @safe nothrow { 101 slice.left = 0; 102 slice.top = 0; 103 slice.right = position.width - 1; 104 slice.bottom = position.height - 1; 105 } 106 /** 107 * Replaces the sprite with a new one. 108 * If the sizes are mismatching, the top-left coordinates are left as is, but the slicing is reset. 109 */ 110 void replaceSprite(ABitmap sprite) @trusted pure nothrow { 111 //this.sprite = sprite; 112 //palette = sprite.getPalettePtr(); 113 if(this.width != sprite.width || this.height != sprite.height){ 114 this.width = sprite.width; 115 this.height = sprite.height; 116 position.right = position.left + cast(int)scaleNearestLength(width, scaleHoriz); 117 position.bottom = position.top + cast(int)scaleNearestLength(height, scaleVert); 118 resetSlice(); 119 } 120 if (typeid(sprite) is typeid(Bitmap2Bit)) { 121 bmpType = BitmapTypes.Bmp2Bit; 122 //paletteSh = 2; 123 pixelData = (cast(Bitmap2Bit)(sprite)).getPtr; 124 } else if (typeid(sprite) is typeid(Bitmap4Bit)) { 125 bmpType = BitmapTypes.Bmp4Bit; 126 //paletteSh = 4; 127 pixelData = (cast(Bitmap4Bit)(sprite)).getPtr; 128 } else if (typeid(sprite) is typeid(Bitmap8Bit)) { 129 bmpType = BitmapTypes.Bmp8Bit; 130 //paletteSh = 8; 131 pixelData = (cast(Bitmap8Bit)(sprite)).getPtr; 132 } else if (typeid(sprite) is typeid(Bitmap16Bit)) { 133 bmpType = BitmapTypes.Bmp16Bit; 134 pixelData = (cast(Bitmap16Bit)(sprite)).getPtr; 135 } else if (typeid(sprite) is typeid(Bitmap32Bit)) { 136 bmpType = BitmapTypes.Bmp32Bit; 137 pixelData = (cast(Bitmap32Bit)(sprite)).getPtr; 138 } 139 } 140 @nogc int opCmp(const DisplayListItem d) const pure @safe nothrow { 141 return priority - d.priority; 142 } 143 @nogc bool opEquals(const DisplayListItem d) const pure @safe nothrow { 144 return priority == d.priority; 145 } 146 @nogc int opCmp(const int pri) const pure @safe nothrow { 147 return priority - pri; 148 } 149 @nogc bool opEquals(const int pri) const pure @safe nothrow { 150 return priority == pri; 151 } 152 153 string toString() const { 154 import std.conv : to; 155 return "{Position: " ~ position.toString ~ ";\nDisplayed portion: " ~ slice.toString ~";\nPriority: " ~ 156 to!string(priority) ~ "; PixelData: " ~ to!string(pixelData) ~ 157 "; PaletteSel: " ~ to!string(paletteSel) ~ "; bmpType: " ~ to!string(bmpType) ~ "}"; 158 } 159 } 160 //alias DisplayList = TreeMap!(int, DisplayListItem); 161 alias DisplayList = SortedList!(DisplayListItem, "a < b", false); 162 protected DisplayList allSprites; ///All sprites of this layer 163 //protected OnScreenList displayedSprites; ///Sprites that are being displayed 164 protected Color[2048] src; ///Local buffer for scaling 165 //size_t[8] prevSize; 166 ///Default ctor 167 public this(RenderingMode renderMode = RenderingMode.AlphaBlend) nothrow @safe { 168 setRenderingMode(renderMode); 169 //Bug workaround: Sometimes when attempting to append an element to a zero-length array, it causes an exception 170 //to be thrown, due to access errors. This bug is unstable, and as such hard to debug for (memory leakage issue?) 171 //displayedSprites.reserve(128); 172 //src[0].length = 1024; 173 } 174 /** 175 * Checks all sprites for whether they're on screen or not. 176 * Called every time the layer is being scrolled. 177 */ 178 public void checkAllSprites() @safe nothrow { 179 foreach (key; allSprites) { 180 checkSprite(key); 181 } 182 } 183 /** 184 * Checks whether a sprite would be displayed on the screen, then updates the display list. 185 * Returns true if it's on screen. 186 */ 187 public bool checkSprite(int n) @safe nothrow { 188 return checkSprite(allSprites[n]); 189 } 190 ///Ditto. 191 protected bool checkSprite(DisplayListItem sprt) @safe nothrow { 192 //assert(sprt.bmpType != BitmapTypes.Undefined && sprt.pixelData, "DisplayList error!"); 193 if(sprt.slice.width && sprt.slice.height 194 && (sprt.position.right > sX && sprt.position.bottom > sY && 195 sprt.position.left < sX + rasterX && sprt.position.top < sY + rasterY)) { 196 //displayedSprites.put(sprt.priority); 197 return true; 198 } else { 199 //displayedSprites.removeByElem(sprt.priority); 200 return false; 201 } 202 } 203 /** 204 * Searches the DisplayListItem by priority and returns it. 205 * Can be used for external use without any safety issues. 206 */ 207 public DisplayListItem getDisplayListItem(int n) @nogc pure @trusted nothrow { 208 return allSprites[n]; 209 } 210 /* 211 * Searches the DisplayListItem by priority and returns it. 212 * Intended for internal use, as it returns it as a reference value. 213 214 protected final DisplayListItem* getDisplayListItem_internal(int n) @nogc pure @safe nothrow { 215 return allSprites.searchByPtr(n); 216 }*/ 217 /+override public void setRasterizer(int rX,int rY) { 218 super.setRasterizer(rX,rY); 219 }+/ 220 ///Returns the displayed portion of the sprite. 221 public Coordinate getSlice(int n) @nogc pure @trusted nothrow { 222 return getDisplayListItem(n).slice; 223 } 224 ///Writes the displayed portion of the sprite. 225 ///Returns the new slice, if invalid (greater than the bitmap, etc.) returns Coordinate.init. 226 public Coordinate setSlice(int n, Coordinate slice) @trusted nothrow { 227 DisplayListItem* sprt = allSprites.searchByPtr(n); 228 if(sprt) { 229 sprt.slice = slice; 230 checkSprite(*sprt); 231 return sprt.slice; 232 } else { 233 return Coordinate.init; 234 } 235 } 236 ///Returns the selected paletteID of the sprite. 237 public ushort getPaletteID(int n) @nogc pure @trusted nothrow { 238 return allSprites.searchBy(n).paletteSel; 239 } 240 ///Sets the paletteID of the sprite. Returns the new ID, which is truncated to the possible values with a simple binary and operation 241 ///Palette must exist in the parent Raster, otherwise AccessError might happen during 242 public ushort setPaletteID(int n, ushort paletteID) @nogc pure @trusted nothrow { 243 DisplayListItem* item = allSprites.searchByPtr(n); 244 if (item is null) return 0; 245 return item.paletteSel = paletteID; 246 } 247 /** 248 * Returns the sprite rendering function. 249 * Params: 250 * n = Sprite priority ID. 251 */ 252 public RenderFunc getSpriteRenderingFunc(int n) @nogc @trusted pure nothrow { 253 return allSprites[n].renderFunc; 254 } 255 /** 256 * Sets the sprite's rendering function from a predefined ones. 257 * Params: 258 * n = Sprite priority ID. 259 * mode = The rendering mode. (init for layer default) 260 * Returns: The new rendering function. 261 */ 262 public RenderFunc setSpriteRenderingMode(int n, RenderingMode mode) @nogc @trusted pure nothrow { 263 DisplayListItem* item = allSprites.searchByPtr(n); 264 if (item is null) return null; 265 return item.renderFunc = mode == RenderingMode.init ? mainRenderingFunction : getRenderingFunc(mode); 266 } 267 /** 268 * Sets the sprite's rendering function. Can be a custom one. 269 * Params: 270 * n = Sprite priority ID. 271 * mode = The rendering mode. (init for layer default) 272 * Returns: The new rendering function. 273 */ 274 public RenderFunc setSpriteRenderingFunc(int n, RenderFunc func) @nogc @trusted pure nothrow { 275 DisplayListItem* item = allSprites.searchByPtr(n); 276 if (item is null) return null; 277 return item.renderFunc = func; 278 } 279 /** 280 * Creates a sprite from a bitmap with the given data, then places it to the display list. (New architecture) 281 * Params: 282 * sprt = The bitmap to be used as the sprite. 283 * n = Priority ID of the sprite. Both identifies the sprite and decides it's display priority. Larger numbers will be drawn first, 284 * and thus will appear behind of smaller numbers, which also include negatives. 285 * x = X position of the sprite (top-left corner). 286 * y = Y position of the sprite (top-left corner). 287 * paletteSel = Selects a given palette. 288 * paletteSh = Determines how many bits are being used, and thus the palette size for selection. 289 * alpha = The transparency of the sprite. 290 * scaleHoriz = Horizontal scaling of the sprite. 1024 is the base value, anything less will stretch, greater will shrink the sprite. 291 * scaleVert = Ditto for vertical. 292 * renderMode = Determines the rendering mode of the sprite. By default, it's determined by the layer itself. Any of the default 293 * other methods can be selected here, or a specially written rendering function can be specified with a different function. 294 * Returns: The current area of the sprite. 295 */ 296 public Box addSprite(ABitmap sprt, int n, int x, int y, ushort paletteSel = 0, ubyte paletteSh = 0, 297 ubyte alpha = ubyte.max, int scaleHoriz = 1024, int scaleVert = 1024, RenderingMode renderMode = RenderingMode.init) 298 @safe nothrow { 299 DisplayListItem d = DisplayListItem(x, y, sprt, n, paletteSel, paletteSh, alpha, scaleHoriz, scaleVert); 300 if (renderMode == RenderingMode.init) 301 d.renderFunc = mainRenderingFunction; 302 else 303 d.renderFunc = getRenderingFunc(renderMode); 304 //synchronized{ 305 allSprites.put(d); 306 //checkSprite(d); 307 //} 308 return d.position; 309 } 310 /** 311 * Adds a sprite to the layer. 312 */ 313 /+public void addSprite(ABitmap s, int n, Box c, ushort paletteSel = 0, int scaleHoriz = 1024, 314 int scaleVert = 1024) @safe nothrow { 315 DisplayListItem d = DisplayListItem(c, s, n, paletteSel, scaleHoriz, scaleVert); 316 d.renderFunc = mainRenderingFunction; 317 synchronized 318 allSprites[n] = d; 319 checkSprite(d); 320 }+/ 321 ///Ditto 322 /+public void addSprite(ABitmap s, int n, int x, int y, ushort paletteSel = 0, int scaleHoriz = 1024, 323 int scaleVert = 1024) @safe nothrow { 324 DisplayListItem d = DisplayListItem(Box.bySize(x, y, cast(int)scaleNearestLength(s.width, scaleHoriz), 325 cast(int)scaleNearestLength(s.height, scaleVert)), s, n, paletteSel, scaleHoriz, 326 scaleVert); 327 d.renderFunc = mainRenderingFunction; 328 synchronized 329 allSprites[n] = d; 330 checkSprite(d); 331 }+/ 332 /** 333 * Replaces the bitmap of the given sprite. 334 */ 335 public void replaceSprite(ABitmap s, int n) @trusted nothrow { 336 DisplayListItem* item = allSprites.searchByPtr(n); 337 if (item is null) return; 338 item.replaceSprite(s); 339 } 340 ///Ditto with move 341 public void replaceSprite(ABitmap s, int n, int x, int y) @trusted nothrow { 342 DisplayListItem* item = allSprites.searchByPtr(n); 343 if (item is null) return; 344 item.replaceSprite(s); 345 item.position.move(x, y); 346 } 347 ///Ditto with move 348 public void replaceSprite(ABitmap s, int n, Coordinate c) @trusted nothrow { 349 DisplayListItem* item = allSprites.searchByPtr(n); 350 if (item is null) return; 351 item.replaceSprite(s); 352 item.position = c; 353 } 354 /** 355 * Removes a sprite from both displaylists by priority. 356 */ 357 public void removeSprite(int n) @safe nothrow { 358 allSprites.removeBy(n); 359 } 360 ///Clears all sprite from the layer. 361 public void clear() @safe nothrow { 362 allSprites = DisplayList.init; 363 } 364 /** 365 * Moves a sprite to the given position. 366 */ 367 public void moveSprite(int n, int x, int y) @trusted nothrow { 368 DisplayListItem* item = allSprites.searchByPtr(n); 369 if (item is null) return; 370 item.position.move(x, y); 371 } 372 /** 373 * Moves a sprite by the given amount. 374 */ 375 public void relMoveSprite(int n, int x, int y) @trusted nothrow { 376 DisplayListItem* item = allSprites.searchByPtr(n); 377 if (item is null) return; 378 item.position.relMove(x, y); 379 //checkSprite(*sprt); 380 } 381 /* ///Sets the rendering function for the sprite (defaults to the layer's rendering function) 382 public void setSpriteRenderingMode(int n, RenderingMode mode) @safe nothrow { 383 DisplayListItem* item = allSprites.searchByPtr(n); 384 if (item is null) return 0; 385 item.renderFunc = getRenderingFunc(mode); 386 } */ 387 public @nogc Box getSpriteCoordinate(int n) @trusted nothrow { 388 DisplayListItem* sprt = allSprites.searchByPtr(n); 389 if(!sprt) return Box.init; 390 return sprt.position; 391 } 392 ///Scales sprite horizontally. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 393 public int scaleSpriteHoriz(int n, int hScl) @trusted nothrow { 394 DisplayListItem* sprt = allSprites.searchByPtr(n); 395 if(!sprt) return -2; 396 else if(!hScl) return -1; 397 else { 398 sprt.scaleHoriz = hScl; 399 const int newWidth = cast(int)scaleNearestLength(sprt.width, hScl); 400 sprt.slice.right = newWidth; 401 sprt.position.right = sprt.position.left + newWidth; 402 checkSprite(*sprt); 403 return newWidth; 404 } 405 } 406 ///Scales sprite vertically. Returns the new size, or -1 if the scaling value is invalid, or -2 if spriteID not found. 407 public int scaleSpriteVert(int n, int vScl) @trusted nothrow { 408 DisplayListItem* sprt = allSprites.searchByPtr(n); 409 if(!sprt) return -2; 410 else if(!vScl) return -1; 411 else { 412 sprt.scaleVert = vScl; 413 const int newHeight = cast(int)scaleNearestLength(sprt.height, vScl); 414 sprt.slice.bottom = newHeight; 415 sprt.position.bottom = sprt.position.top + newHeight; 416 checkSprite(*sprt); 417 return newHeight; 418 } 419 } 420 ///Gets the sprite's current horizontal scale value 421 public int getScaleSpriteHoriz(int n) @nogc @trusted nothrow { 422 DisplayListItem* item = allSprites.searchByPtr(n); 423 if (item is null) return 0; 424 return item.scaleHoriz; 425 } 426 ///Gets the sprite's current vertical scale value 427 public int getScaleSpriteVert(int n) @nogc @trusted nothrow { 428 DisplayListItem* item = allSprites.searchByPtr(n); 429 if (item is null) return 0; 430 return item.scaleVert; 431 } 432 public override LayerType getLayerType() @nogc @safe pure nothrow const { 433 return LayerType.Sprite; 434 } 435 public override @nogc void updateRaster(void* workpad, int pitch, Color* palette) { 436 /* 437 * BUG 1: If sprite is wider than 2048 pixels, it'll cause issues (mostly memory leaks) due to a hack. (Fixed!) 438 * BUG 2: Obscuring the top part of a sprite when scaleVert is not 1024 will cause glitches. (Fixed!!!) 439 * TO DO: Replace AVL tree with an automatically sorting array with keying abilities. 440 */ 441 foreach (i ; allSprites) { 442 if(!(i.slice.width && i.slice.height 443 && (i.position.right > sX && i.position.bottom > sY && 444 i.position.left < sX + rasterX && i.position.top < sY + rasterY))) continue; 445 const int left = i.position.left + i.slice.left; 446 const int top = i.position.top + i.slice.top; 447 const int right = i.position.left + i.slice.right; 448 const int bottom = i.position.top + i.slice.bottom; 449 int offsetXA = sX > left ? sX - left : 0;//Left hand side offset, zero if not obscured 450 const int offsetXB = sX + rasterX < right ? right - (sX + rasterX) : 0; //Right hand side offset, zero if not obscured 451 const int offsetYA = sY > top ? sY - top : 0; //top offset of sprite, zero if not obscured 452 const int offsetYB = sY + rasterY < bottom ? bottom - (sY + rasterY) + 1 : 1; //bottom offset of sprite, zero if not obscured 453 const int sizeX = i.slice.width(); //total displayed width after slicing 454 const int offsetX = left - sX; 455 const int length = sizeX - offsetXA - offsetXB - 1; //total displayed width after considering screen borders 456 const int offsetY = sY < top ? (top-sY)*pitch : 0; //used if top portion of the sprite is off-screen 457 const int scaleVertAbs = i.scaleVert * (i.scaleVert < 0 ? -1 : 1); //absolute value of vertical scaling, used in various calculations 458 const int scaleHorizAbs = i.scaleHoriz * (i.scaleHoriz < 0 ? -1 : 1); 459 const int offsetYA0 = (offsetYA * scaleVertAbs)>>>10; //amount of skipped lines in the bitmap source 460 const int sizeXOffset = i.width * (i.scaleVert < 0 ? -1 : 1); 461 int offsetTarget; //the target fractional lines 462 int offset = (offsetYA * scaleVertAbs) & 1023; //the current amount of fractional lines, also contains the fractional offset bias by defauls 463 //const size_t p0offset = (i.scaleHoriz > 0 ? offsetXA : offsetXB); //determines offset based on mirroring 464 //const int scalelength = i.position.width < 2048 ? i.width : 2048; //limit width to 2048, the minimum required for this scaling method to work 465 void* dest = workpad + (offsetX + offsetXA)*4 + offsetY; 466 final switch (i.bmpType) with (BitmapTypes) { 467 case Bmp2Bit: 468 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * ((i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0)>>2); 469 const size_t _pitch = i.width>>>2; 470 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 471 /+horizontalScaleNearest4BitAndCLU(p0, src.ptr, palette + (i.paletteSel<<i.paletteSh), scalelength, offsetXA & 1, 472 i.scaleHoriz);+/ 473 horizontalScaleNearestAndCLU(QuadArray(p0[0.._pitch], i.width), src.ptr, palette + (i.paletteSel<<i.paletteSh), 474 length, i.scaleHoriz, offsetXA * scaleHorizAbs); 475 offsetTarget += 1024; 476 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 477 y++; 478 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 479 dest += pitch; 480 } 481 p0 += _pitch; 482 } 483 break; 484 case Bmp4Bit: 485 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * ((i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0)>>1); 486 const size_t _pitch = i.width>>>1; 487 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 488 /+horizontalScaleNearest4BitAndCLU(p0, src.ptr, palette + (i.paletteSel<<i.paletteSh), scalelength, offsetXA & 1, 489 i.scaleHoriz);+/ 490 horizontalScaleNearestAndCLU(NibbleArray(p0[0.._pitch], i.width), src.ptr, palette + (i.paletteSel<<i.paletteSh), 491 length, i.scaleHoriz, offsetXA * scaleHorizAbs); 492 offsetTarget += 1024; 493 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 494 y++; 495 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 496 dest += pitch; 497 } 498 p0 += _pitch; 499 } 500 break; 501 case Bmp8Bit: 502 ubyte* p0 = cast(ubyte*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 503 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 504 //horizontalScaleNearestAndCLU(p0, src.ptr, palette + (i.paletteSel<<i.paletteSh), scalelength, i.scaleHoriz); 505 horizontalScaleNearestAndCLU(p0[0..i.width], src.ptr, palette + (i.paletteSel<<i.paletteSh), length, i.scaleHoriz, 506 offsetXA * scaleHorizAbs); 507 offsetTarget += 1024; 508 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 509 y++; 510 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 511 dest += pitch; 512 } 513 p0 += sizeXOffset; 514 } 515 break; 516 case Bmp16Bit: 517 ushort* p0 = cast(ushort*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 518 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 519 //horizontalScaleNearestAndCLU(p0, src.ptr, palette, scalelength, i.scaleHoriz); 520 horizontalScaleNearestAndCLU(p0[0..i.width], src.ptr, palette, length, i.scaleHoriz, offsetXA * scaleHorizAbs); 521 offsetTarget += 1024; 522 for (; offset < offsetTarget && y < i.slice.height - offsetYB ; offset += scaleVertAbs) { 523 y++; 524 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 525 dest += pitch; 526 } 527 p0 += sizeXOffset; 528 } 529 break; 530 case Bmp32Bit: 531 Color* p0 = cast(Color*)i.pixelData + i.width * (i.scaleVert < 0 ? (i.height - offsetYA0 - 1) : offsetYA0); 532 for (int y = offsetYA ; y < i.slice.height - offsetYB ; ) { 533 horizontalScaleNearest(p0[0..i.width], src, length, i.scaleHoriz, offsetXA * scaleHorizAbs); 534 offsetTarget += 1024; 535 for (; offset < offsetTarget && y < i.slice.height - offsetYB; offset += scaleVertAbs) { 536 y++; 537 i.renderFunc(cast(uint*)src.ptr, cast(uint*)dest, length, i.masterAlpha); 538 dest += pitch; 539 } 540 p0 += sizeXOffset; 541 } 542 //} 543 break; 544 case Undefined, Bmp1Bit, Planar: 545 break; 546 } 547 548 //} 549 } 550 //foreach(int threadOffset; threads.parallel) 551 //free(src[threadOffset]); 552 } 553 ///Absolute scrolling. 554 public override void scroll(int x, int y) @safe nothrow { 555 sX = x; 556 sY = y; 557 //checkAllSprites; 558 } 559 ///Relative scrolling. Positive values scrolls the layer left and up, negative values scrolls the layer down and right. 560 public override void relScroll(int x, int y) @safe nothrow { 561 sX += x; 562 sY += y; 563 //checkAllSprites; 564 } 565 }