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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user