From 982260d94ec8de51e3887e7b3d8d213cd0316d06 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Wed, 16 Jun 2021 01:20:09 +0100 Subject: [PATCH] Clip control.cpp draw calls No longer crashes there when running at a resolution smaller than 640x480 --- Source/control.cpp | 27 +++++---------------------- Source/engine.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ Source/engine.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index 81b542379..ca7fcd673 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -27,6 +27,7 @@ #include "towners.h" #include "trigs.h" #include "utils/language.h" +#include "utils/sdl_geometry.h" namespace devilution { namespace { @@ -545,12 +546,7 @@ void ClearPanel() void DrawPanelBox(const CelOutputBuffer &out, SDL_Rect srcRect, Point targetPosition) { - const BYTE *src = pBtmBuff.at(srcRect.x, srcRect.y); - BYTE *dst = &out[targetPosition]; - - for (int hgt = srcRect.h; hgt != 0; hgt--, src += pBtmBuff.pitch(), dst += out.pitch()) { - memcpy(dst, src, srcRect.w); - } + out.BlitFrom(pBtmBuff, srcRect, targetPosition); } /** @@ -565,11 +561,7 @@ void DrawPanelBox(const CelOutputBuffer &out, SDL_Rect srcRect, Point targetPosi */ static void DrawFlaskTop(const CelOutputBuffer &out, Point position, const CelOutputBuffer &celBuf, int y0, int y1) { - const BYTE *src = celBuf.at(0, y0); - BYTE *dst = &out[position]; - - for (int h = y1 - y0; h != 0; --h, src += celBuf.pitch(), dst += out.pitch()) - memcpy(dst, src, celBuf.w()); + out.BlitFrom(celBuf, SDL_Rect { 0, static_cast(y0), celBuf.w(), y1 - y0 }, position); } /** @@ -584,17 +576,8 @@ static void DrawFlaskTop(const CelOutputBuffer &out, Point position, const CelOu */ static void DrawFlask(const CelOutputBuffer &out, const CelOutputBuffer &celBuf, Point sourcePosition, Point targetPosition, int h) { - const BYTE *src = &celBuf[sourcePosition]; - BYTE *dst = &out[targetPosition]; - - for (int hgt = h; hgt != 0; hgt--, src += celBuf.pitch() - 59, dst += out.pitch() - 59) { - for (int wdt = 59; wdt != 0; wdt--) { - if (*src != 0) - *dst = *src; - src++; - dst++; - } - } + constexpr int FlaskWidth = 59; + out.BlitFromSkipColorIndexZero(celBuf, MakeSdlRect(sourcePosition.x, sourcePosition.y, FlaskWidth, h), targetPosition); } void DrawLifeFlask(const CelOutputBuffer &out) diff --git a/Source/engine.cpp b/Source/engine.cpp index 10b19048f..bf4e82113 100644 --- a/Source/engine.cpp +++ b/Source/engine.cpp @@ -36,6 +36,52 @@ const uint32_t RndInc = 1; */ const uint32_t RndMult = 0x015A4E35; +namespace { + +template +void BufferBlit(const CelOutputBuffer &src, SDL_Rect srcRect, const CelOutputBuffer &dst, Point dstPosition) +{ + // We do not use `SDL_BlitSurface` here because the palettes may be different objects + // and SDL would attempt to map them. + + dst.Clip(&srcRect, &dstPosition); + if (srcRect.w <= 0 || srcRect.h <= 0) + return; + + const std::uint8_t *srcBuf = src.at(srcRect.x, srcRect.y); + const auto srcPitch = src.pitch(); + std::uint8_t *dstBuf = &dst[dstPosition]; + const auto dstPitch = dst.pitch(); + + for (unsigned h = srcRect.h; h != 0; --h) { + if (SkipColorIndexZero) { + for (unsigned w = srcRect.w; w != 0; --w) { + if (*srcBuf != 0) + *dstBuf = *srcBuf; + ++srcBuf, ++dstBuf; + } + srcBuf += srcPitch - srcRect.w; + dstBuf += dstPitch - srcRect.w; + } else { + std::memcpy(dstBuf, srcBuf, srcRect.w); + srcBuf += srcPitch; + dstBuf += dstPitch; + } + } +} + +} // namespace + +void CelOutputBuffer::BlitFrom(const CelOutputBuffer &src, SDL_Rect srcRect, Point targetPosition) const +{ + BufferBlit(src, srcRect, *this, targetPosition); +} + +void CelOutputBuffer::BlitFromSkipColorIndexZero(const CelOutputBuffer &src, SDL_Rect srcRect, Point targetPosition) const +{ + BufferBlit(src, srcRect, *this, targetPosition); +} + CelSprite LoadCel(const char *pszName, int width) { return CelSprite(LoadFileInMem(pszName), width); diff --git a/Source/engine.h b/Source/engine.h index 56374aab7..fcdff3b57 100644 --- a/Source/engine.h +++ b/Source/engine.h @@ -432,6 +432,40 @@ struct CelOutputBuffer { subregion.h = h; return CelOutputBuffer(surface, subregion); } + + /** + * @brief Clips srcRect and targetPosition to this output buffer. + */ + void Clip(SDL_Rect *srcRect, Point *targetPosition) const + { + if (targetPosition->x < 0) { + srcRect->x -= targetPosition->x; + srcRect->w += targetPosition->x; + targetPosition->x = 0; + } + if (targetPosition->y < 0) { + srcRect->y -= targetPosition->y; + srcRect->h += targetPosition->y; + targetPosition->y = 0; + } + if (targetPosition->x + srcRect->w > region.w) { + srcRect->w = region.w - targetPosition->x; + } + if (targetPosition->y + srcRect->h > region.h) { + srcRect->h = region.h - targetPosition->y; + } + } + + /** + * @brief Copies the `srcRect` portion of the given buffer to this buffer at `targetPosition`. + */ + void BlitFrom(const CelOutputBuffer &src, SDL_Rect srcRect, Point targetPosition) const; + + /** + * @brief Copies the `srcRect` portion of the given buffer to this buffer at `targetPosition`. + * Source pixels with index 0 are not copied. + */ + void BlitFromSkipColorIndexZero(const CelOutputBuffer &src, SDL_Rect srcRect, Point targetPosition) const; }; /**