1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Rewrite software sprite drawing to handle new DPI coords

This commit is contained in:
Michael Bernardi
2024-09-19 23:44:24 +10:00
parent 8212c83965
commit f64ef9488d
4 changed files with 89 additions and 91 deletions

View File

@@ -11,26 +11,25 @@
template<DrawBlendOp TBlendOp> static void FASTCALL DrawBMPSpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args)
{
auto& g1 = args.SourceImage;
auto src = g1.offset + ((static_cast<size_t>(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<TBlendOp>(src, dst, paletteMap, zoom, dstLineWidth);
auto src = src0 + (srcLineWidth * zoom.ApplyTo(srcY + y) + zoom.ApplyTo(srcX + x));
BlitPixel<TBlendOp>(src, dst, paletteMap);
}
src = nextSrc;
dst = nextDst;
}
}

View File

@@ -12,74 +12,46 @@
#include <cassert>
#include <cstring>
template<DrawBlendOp TBlendOp, size_t TZoom>
static void FASTCALL DrawRLESpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args)
template<DrawBlendOp TBlendOp> static void FASTCALL DrawRLESpriteMagnify(DrawPixelInfo& dpi, const DrawSpriteArgs& args)
{
auto& paletteMap = args.PalMap;
auto lineOffsets = reinterpret_cast<const uint16_t*>(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<size_t>(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<size_t>(x) << TZoom);
while (numPixels > 0)
{
BlitPixels<TBlendOp>(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<TBlendOp>(data8 + colNum - pixelRunStart, dst, paletteMap);
dst++;
}
dst = nextDst;
}
}
@@ -184,10 +156,8 @@ template<DrawBlendOp TBlendOp> static void FASTCALL DrawRLESprite(DrawPixelInfo&
switch (zoom_level)
{
case -2:
DrawRLESpriteMagnify<TBlendOp, 2>(dpi, args);
break;
case -1:
DrawRLESpriteMagnify<TBlendOp, 1>(dpi, args);
DrawRLESpriteMagnify<TBlendOp>(dpi, args);
break;
case 0:
DrawRLESpriteMinify<TBlendOp, 0>(dpi, args);

View File

@@ -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);

View File

@@ -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;
}