diff --git a/src/openrct2/drawing/Drawing.Sprite.BMP.cpp b/src/openrct2/drawing/Drawing.Sprite.BMP.cpp index c1e9bd91cb..d7a936d422 100644 --- a/src/openrct2/drawing/Drawing.Sprite.BMP.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.BMP.cpp @@ -11,26 +11,25 @@ template static void FASTCALL DrawBMPSpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args) { - auto& g1 = args.SourceImage; - auto src = g1.offset + ((static_cast(g1.width) * args.SrcY) + args.SrcX); - auto dst = args.DestinationBits; auto& paletteMap = args.PalMap; - auto zoomLevel = dpi.zoom_level; - size_t srcLineWidth = g1.width; - size_t dstLineWidth = dpi.LineStride(); - uint8_t zoom = zoomLevel.ApplyInversedTo(1); - auto width = zoomLevel.ApplyInversedTo(args.Width); - auto height = zoomLevel.ApplyInversedTo(args.Height); - for (; height > 0; height -= zoom) + auto src0 = args.SourceImage.offset; + auto dst = args.DestinationBits; + auto srcX = args.SrcX; + auto srcY = args.SrcY; + auto width = args.Width; + auto height = args.Height; + auto zoom = dpi.zoom_level; + auto dstLineWidth = dpi.LineStride(); + auto srcLineWidth = args.SourceImage.width; + + for (int32_t y = 0; y < height; y++) { - auto nextSrc = src + srcLineWidth; - auto nextDst = dst + (dstLineWidth * zoom); - for (int32_t widthRemaining = width; widthRemaining > 0; widthRemaining -= zoom, src++, dst += zoom) + auto nextDst = dst + dstLineWidth; + for (int32_t x = 0; x < width; x++, dst++) { - // Copy src to a block of zoom * zoom on dst - BlitPixels(src, dst, paletteMap, zoom, dstLineWidth); + auto src = src0 + (srcLineWidth * zoom.ApplyTo(srcY + y) + zoom.ApplyTo(srcX + x)); + BlitPixel(src, dst, paletteMap); } - src = nextSrc; dst = nextDst; } } diff --git a/src/openrct2/drawing/Drawing.Sprite.RLE.cpp b/src/openrct2/drawing/Drawing.Sprite.RLE.cpp index 46db4bd11e..1e4d675535 100644 --- a/src/openrct2/drawing/Drawing.Sprite.RLE.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.RLE.cpp @@ -12,74 +12,46 @@ #include #include -template -static void FASTCALL DrawRLESpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args) +template static void FASTCALL DrawRLESpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args) { + auto& paletteMap = args.PalMap; + auto lineOffsets = reinterpret_cast(args.SourceImage.offset); auto src0 = args.SourceImage.offset; - auto dst0 = args.DestinationBits; + auto dst = args.DestinationBits; auto srcX = args.SrcX; auto srcY = args.SrcY; auto width = args.Width; auto height = args.Height; - auto& paletteMap = args.PalMap; - auto zoom = 1 << TZoom; - auto dstLineWidth = static_cast(dpi.LineStride()); + auto zoom = dpi.zoom_level; + auto dstLineWidth = dpi.LineStride(); - // Move up to the first line of the image if source_y_start is negative. Why does this even occur? - if (srcY < 0) + for (int32_t y = 0; y < height; y++) { - srcY += zoom; - height -= zoom; - dst0 += dstLineWidth; - } + uint8_t* nextDst = dst + dstLineWidth; + const int32_t rowNum = zoom.ApplyTo(srcY + y); + const uint8_t* data8 = src0 + lineOffsets[rowNum]; - // For every line in the image - for (int32_t i = 0; i < height; i++) - { - int32_t y = srcY + i; - - // The first part of the source pointer is a list of offsets to different lines - // This will move the pointer to the correct source line. - uint16_t lineOffset = src0[y * 2] | (src0[y * 2 + 1] << 8); - auto nextRun = src0 + lineOffset; - auto dstLineStart = dst0 + ((dstLineWidth * i) << TZoom); - - // For every data chunk in the line - bool isEndOfLine = false; - while (!isEndOfLine) + bool lastDataForLine = false; + int32_t numPixels = 0; + uint8_t pixelRunStart = 0; + for (int32_t x = 0; x < width; x++) { - // Read chunk metadata - auto src = nextRun; - auto dataSize = *src++; - auto firstPixelX = *src++; - isEndOfLine = (dataSize & 0x80) != 0; - dataSize &= 0x7F; + const int32_t colNum = zoom.ApplyTo(srcX + x); - // Have our next source pointer point to the next data section - nextRun = src + dataSize; - - int32_t x = firstPixelX - srcX; - int32_t numPixels = dataSize; - if (x < 0) + while (colNum >= pixelRunStart + numPixels && !lastDataForLine) { - src += -x; - numPixels += x; - x = 0; - } - - // If the end position is further out than the whole image - // end position then we need to shorten the line again - numPixels = std::min(numPixels, width - x); - - auto dst = dstLineStart + (static_cast(x) << TZoom); - while (numPixels > 0) - { - BlitPixels(src, dst, paletteMap, zoom, dstLineWidth); - src++; - dst += zoom; - numPixels--; + data8 += numPixels; + numPixels = *data8++; + pixelRunStart = *data8++; + lastDataForLine = numPixels & 0x80; + numPixels &= 0x7F; } + if (pixelRunStart <= colNum && colNum < pixelRunStart + numPixels) + BlitPixel(data8 + colNum - pixelRunStart, dst, paletteMap); + dst++; } + + dst = nextDst; } } @@ -184,10 +156,8 @@ template static void FASTCALL DrawRLESprite(DrawPixelInfo& switch (zoom_level) { case -2: - DrawRLESpriteMagnify(dpi, args); - break; case -1: - DrawRLESpriteMagnify(dpi, args); + DrawRLESpriteMagnify(dpi, args); break; case 0: DrawRLESpriteMinify(dpi, args); diff --git a/src/openrct2/drawing/Drawing.Sprite.cpp b/src/openrct2/drawing/Drawing.Sprite.cpp index 014008cb70..5fc13fd9c5 100644 --- a/src/openrct2/drawing/Drawing.Sprite.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.cpp @@ -513,6 +513,7 @@ void FASTCALL GfxDrawSpriteSoftware(DrawPixelInfo& dpi, const ImageId imageId, c void FASTCALL GfxDrawSpritePaletteSetSoftware( DrawPixelInfo& dpi, const ImageId imageId, const ScreenCoordsXY& coords, const PaletteMap& paletteMap) { + const auto zoomLevel = dpi.zoom_level; int32_t x = coords.x; int32_t y = coords.y; @@ -522,7 +523,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( return; } - if (dpi.zoom_level > ZoomLevel{ 0 } && (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)) + if (zoomLevel > ZoomLevel{ 0 } && (g1->flags & G1_FLAG_HAS_ZOOM_SPRITE)) { DrawPixelInfo zoomed_dpi = dpi; zoomed_dpi.bits = dpi.bits; @@ -531,24 +532,52 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( zoomed_dpi.SetHeight(dpi.ScreenHeight()); zoomed_dpi.SetWidth(dpi.ScreenWidth()); zoomed_dpi.pitch = dpi.pitch; - zoomed_dpi.zoom_level = dpi.zoom_level - 1; + zoomed_dpi.zoom_level = zoomLevel - 1; - const auto spriteCoords = ScreenCoordsXY{ x >> 1, y >> 1 }; + const auto spriteCoords = ScreenCoordsXY{ coords.x / 2, coords.y / 2 }; GfxDrawSpritePaletteSetSoftware( zoomed_dpi, imageId.WithIndex(imageId.GetIndex() - g1->zoomed_offset), spriteCoords, paletteMap); return; } - if (dpi.zoom_level > ZoomLevel{ 0 } && (g1->flags & G1_FLAG_NO_ZOOM_DRAW)) + if (zoomLevel > ZoomLevel{ 0 } && (g1->flags & G1_FLAG_NO_ZOOM_DRAW)) { return; } - // Its used super often so we will define it to a separate variable. - const auto zoom_level = dpi.zoom_level; - const int32_t zoom_mask = zoom_level > ZoomLevel{ 0 } ? zoom_level.ApplyTo(0xFFFFFFFF) : 0xFFFFFFFF; + // mber: There should not be two separate code paths for minifying and magnifying sprites. + // I haven't been able to refactor the code in a way that handles both cases properly with one code path. + // For the moment, I've added this block here just for magnification with the old code continuing below. + if (zoomLevel < ZoomLevel{ 0 }) + { + ScreenCoordsXY spriteTopLeft = { zoomLevel.ApplyInversedTo(coords.x + g1->x_offset), + zoomLevel.ApplyInversedTo(coords.y + g1->y_offset) }; - if (zoom_level > ZoomLevel{ 0 } && g1->flags & G1_FLAG_RLE_COMPRESSION) + ScreenCoordsXY spriteBottomLeft{ zoomLevel.ApplyInversedTo(coords.x + g1->x_offset + g1->width), + zoomLevel.ApplyInversedTo(coords.y + g1->y_offset + g1->height) }; + + const int32_t width = std::min(spriteBottomLeft.x, dpi.ScreenX() + dpi.ScreenWidth()) + - std::max(spriteTopLeft.x, dpi.ScreenX()); + const int32_t height = std::min(spriteBottomLeft.y, dpi.ScreenY() + dpi.ScreenHeight()) + - std::max(spriteTopLeft.y, dpi.ScreenY()); + + if (width <= 0 || height <= 0) + return; + + const int32_t offsetX = dpi.ScreenX() - spriteTopLeft.x; + const int32_t offsetY = dpi.ScreenY() - spriteTopLeft.y; + const int32_t srcX = std::max(0, offsetX); + const int32_t srcY = std::max(0, offsetY); + uint8_t* dst = dpi.bits + std::max(0, -offsetX) + std::max(0, -offsetY) * dpi.LineStride(); + + DrawSpriteArgs args(imageId, paletteMap, *g1, srcX, srcY, width, height, dst); + GfxSpriteToBuffer(dpi, args); + return; + } + + const int32_t zoom_mask = zoomLevel > ZoomLevel{ 0 } ? zoomLevel.ApplyTo(0xFFFFFFFF) : 0xFFFFFFFF; + + if (zoomLevel > ZoomLevel{ 0 } && g1->flags & G1_FLAG_RLE_COMPRESSION) { x -= ~zoom_mask; y -= ~zoom_mask; @@ -590,7 +619,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( } else { - if ((g1->flags & G1_FLAG_RLE_COMPRESSION) && zoom_level > ZoomLevel{ 0 }) + if ((g1->flags & G1_FLAG_RLE_COMPRESSION) && zoomLevel > ZoomLevel{ 0 }) { source_start_y -= dest_start_y & ~zoom_mask; height += dest_start_y & ~zoom_mask; @@ -609,7 +638,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( if (height <= 0) return; - dest_start_y = zoom_level.ApplyInversedTo(dest_start_y); + dest_start_y = zoomLevel.ApplyInversedTo(dest_start_y); // This will be the width of the drawn image int32_t width = g1->width; @@ -636,7 +665,7 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( } else { - if ((g1->flags & G1_FLAG_RLE_COMPRESSION) && zoom_level > ZoomLevel{ 0 }) + if ((g1->flags & G1_FLAG_RLE_COMPRESSION) && zoomLevel > ZoomLevel{ 0 }) { source_start_x -= dest_start_x & ~zoom_mask; } @@ -654,11 +683,11 @@ void FASTCALL GfxDrawSpritePaletteSetSoftware( return; } - dest_start_x = zoom_level.ApplyInversedTo(dest_start_x); + dest_start_x = zoomLevel.ApplyInversedTo(dest_start_x); uint8_t* dest_pointer = dpi.bits; // Move the pointer to the start point of the destination - dest_pointer += (zoom_level.ApplyInversedTo(dpi.WorldWidth()) + dpi.pitch) * dest_start_y + dest_start_x; + dest_pointer += (zoomLevel.ApplyInversedTo(dpi.WorldWidth()) + dpi.pitch) * dest_start_y + dest_start_x; DrawSpriteArgs args(imageId, paletteMap, *g1, source_start_x, source_start_y, width, height, dest_pointer); GfxSpriteToBuffer(dpi, args); diff --git a/src/openrct2/paint/Paint.Entity.cpp b/src/openrct2/paint/Paint.Entity.cpp index 654dfb02a7..d5148d144d 100644 --- a/src/openrct2/paint/Paint.Entity.cpp +++ b/src/openrct2/paint/Paint.Entity.cpp @@ -101,11 +101,11 @@ void EntityPaintSetup(PaintSession& session, const CoordsXY& pos) screenCoords - ScreenCoordsXY{ spr->SpriteData.Width, spr->SpriteData.HeightMin }, screenCoords + ScreenCoordsXY{ spr->SpriteData.Width, spr->SpriteData.HeightMax }); - // We must compare with world DPI coordinates as spriteRect is not adjusted for the DPI zoom. - if (session.DPI.WorldY() + session.DPI.WorldHeight() <= spriteRect.GetTop() - || spriteRect.GetBottom() <= session.DPI.WorldY() - || session.DPI.WorldX() + session.DPI.WorldWidth() <= spriteRect.GetLeft() - || spriteRect.GetRight() <= session.DPI.WorldX()) + const ZoomLevel zoom = session.DPI.zoom_level; + if (session.DPI.ScreenY() + session.DPI.ScreenHeight() <= zoom.ApplyInversedTo(spriteRect.GetTop()) + || zoom.ApplyInversedTo(spriteRect.GetBottom()) <= session.DPI.ScreenY() + || session.DPI.ScreenX() + session.DPI.ScreenWidth() <= zoom.ApplyInversedTo(spriteRect.GetLeft()) + || zoom.ApplyInversedTo(spriteRect.GetRight()) <= session.DPI.ScreenX()) { continue; }