#include "engine/render/primitive_render.hpp" #include #include #include #include "engine/point.hpp" #include "engine/size.hpp" #include "engine/surface.hpp" #include "utils/palette_blending.hpp" namespace devilution { namespace { void DrawHalfTransparentUnalignedBlendedRectTo(const Surface &out, unsigned sx, unsigned sy, unsigned width, unsigned height, uint8_t color) { uint8_t *pix = out.at(static_cast(sx), static_cast(sy)); const uint8_t *const lookupTable = paletteTransparencyLookup[color]; const unsigned skipX = out.pitch() - width; for (unsigned y = 0; y < height; ++y) { for (unsigned x = 0; x < width; ++x, ++pix) { *pix = lookupTable[*pix]; } pix += skipX; } } #if DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT // Expects everything to be 4-byte aligned. void DrawHalfTransparentAligned32BlendedRectTo(const Surface &out, unsigned sx, unsigned sy, unsigned width, unsigned height) { assert(out.pitch() % 4 == 0); auto *pix = reinterpret_cast(out.at(static_cast(sx), static_cast(sy))); assert(reinterpret_cast(pix) % 4 == 0); const uint16_t *lookupTable = paletteTransparencyLookupBlack16; const unsigned skipX = (out.pitch() - width) / 4; width /= 4; while (height-- > 0) { for (unsigned i = 0; i < width; ++i, ++pix) { const uint32_t v = *pix; *pix = lookupTable[v & 0xFFFF] | (lookupTable[(v >> 16) & 0xFFFF] << 16); } pix += skipX; } } void DrawHalfTransparentBlendedRectTo(const Surface &out, unsigned sx, unsigned sy, unsigned width, unsigned height) { // All SDL surfaces are 4-byte aligned and divisible by 4. // However, our coordinates and widths may not be. // First, draw the leading unaligned part. if (sx % 4 != 0) { const unsigned w = 4 - sx % 4; DrawHalfTransparentUnalignedBlendedRectTo(out, sx, sy, w, height, 0); sx += w; width -= w; } if (static_cast(sx + width) == out.w()) { // The pitch is 4-byte aligned, so we can simply extend the width to the pitch. width = out.pitch() - sx; } else if (width % 4 != 0) { // Draw the trailing unaligned part. const unsigned w = width % 4; DrawHalfTransparentUnalignedBlendedRectTo(out, sx + (width / 4) * 4, sy, w, height, 0); width -= w; } // Now everything is divisible by 4. Draw the aligned part. DrawHalfTransparentAligned32BlendedRectTo(out, sx, sy, width, height); } #else #define DrawHalfTransparentBlendedRectTo DrawHalfTransparentUnalignedBlendedRectTo #endif } // namespace void FillRect(const Surface &out, int x, int y, int width, int height, uint8_t colorIndex) { for (int j = 0; j < height; j++) { DrawHorizontalLine(out, { x, y + j }, width, colorIndex); } } void DrawHorizontalLine(const Surface &out, Point from, int width, std::uint8_t colorIndex) { if (from.y < 0 || from.y >= out.h() || from.x >= out.w() || width <= 0 || from.x + width <= 0) return; if (from.x < 0) { width += from.x; from.x = 0; } if (from.x + width > out.w()) width = out.w() - from.x; return UnsafeDrawHorizontalLine(out, from, width, colorIndex); } void UnsafeDrawHorizontalLine(const Surface &out, Point from, int width, std::uint8_t colorIndex) { std::memset(&out[from], colorIndex, width); } void DrawVerticalLine(const Surface &out, Point from, int height, std::uint8_t colorIndex) { if (from.x < 0 || from.x >= out.w() || from.y >= out.h() || height <= 0 || from.y + height <= 0) return; if (from.y < 0) { height += from.y; from.y = 0; } if (from.y + height > out.h()) height = (from.y + height) - out.h(); return UnsafeDrawVerticalLine(out, from, height, colorIndex); } void UnsafeDrawVerticalLine(const Surface &out, Point from, int height, std::uint8_t colorIndex) { auto *dst = &out[from]; const auto pitch = out.pitch(); while (height-- > 0) { *dst = colorIndex; dst += pitch; } } void DrawHalfTransparentHorizontalLine(const Surface &out, Point from, int width, uint8_t colorIndex) { // completely off-bounds? if (from.y < 0 || from.y >= out.h() || width <= 0 || from.x >= out.w() || from.x + width <= 0) return; const int x0 = std::max(0, from.x); const int x1 = std::min(out.w(), from.x + width); for (int x = x0; x < x1; ++x) { SetHalfTransparentPixel(out, { x, from.y }, colorIndex); } } // Draw a half-transparent vertical line of `height` pixels starting at `from`. void DrawHalfTransparentVerticalLine(const Surface &out, Point from, int height, uint8_t colorIndex) { // completely off-bounds? if (from.x < 0 || from.x >= out.w() || height <= 0 || from.y >= out.h() || from.y + height <= 0) return; const int y0 = std::max(0, from.y); const int y1 = std::min(out.h(), from.y + height); for (int y = y0; y < y1; ++y) { SetHalfTransparentPixel(out, { from.x, y }, colorIndex); } } void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, int height) { if (sx + width < 0) return; if (sy + height < 0) return; if (sx >= out.w()) return; if (sy >= out.h()) return; if (sx < 0) { width += sx; sx = 0; } else if (sx + width >= out.w()) { width = out.w() - sx; } if (sy < 0) { height += sy; sy = 0; } else if (sy + height >= out.h()) { height = out.h() - sy; } DrawHalfTransparentBlendedRectTo(out, sx, sy, width, height); } void DrawHalfTransparentRectTo(const Surface &out, int sx, int sy, int width, int height, uint8_t color) { if (sx + width < 0) return; if (sy + height < 0) return; if (sx >= out.w()) return; if (sy >= out.h()) return; if (sx < 0) { width += sx; sx = 0; } else if (sx + width >= out.w()) { width = out.w() - sx; } if (sy < 0) { height += sy; sy = 0; } else if (sy + height >= out.h()) { height = out.h() - sy; } DrawHalfTransparentUnalignedBlendedRectTo(out, sx, sy, width, height, color); } void SetHalfTransparentPixel(const Surface &out, Point position, uint8_t color) { if (out.InBounds(position)) { uint8_t *pix = out.at(position.x, position.y); const auto &lookupTable = paletteTransparencyLookup[color]; *pix = lookupTable[*pix]; } } void UnsafeDrawBorder2px(const Surface &out, Rectangle rect, uint8_t color) { const size_t width = rect.size.width; const size_t height = rect.size.height; uint8_t *buf = &out[rect.position]; std::memset(buf, color, width); buf += out.pitch(); std::memset(buf, color, width); buf += out.pitch(); for (size_t i = 4; i < height; ++i) { buf[0] = buf[1] = color; buf[width - 2] = buf[width - 1] = color; buf += out.pitch(); } std::memset(buf, color, width); buf += out.pitch(); std::memset(buf, color, width); } } // namespace devilution