You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

210 lines
5.6 KiB

#include "automap_render.hpp"
namespace devilution {
namespace {
enum class DirectionX {
EAST = 1,
WEST = -1,
};
enum class DirectionY {
SOUTH = 1,
NORTH = -1,
};
template <DirectionX DirX, DirectionY DirY>
void UnsafeDrawMapLine(const CelOutputBuffer &out, Point from, int height, bool drawTipPixel, std::uint8_t colorIndex)
{
auto *dst = out.at(from.x, from.y);
const auto pitch = out.pitch();
while (height-- > 0) {
*dst = colorIndex;
dst += static_cast<int>(DirX);
*dst = colorIndex;
dst += static_cast<int>(DirX) + static_cast<int>(DirY) * pitch;
}
if (drawTipPixel)
*dst = colorIndex;
}
int Width(int height)
{
return 2 * height;
}
int Height(int width)
{
return width / 2;
}
int HeightCeil(int width)
{
return (width + 1) / 2;
}
template <DirectionX DirX, DirectionY DirY>
bool InDirectionBounds(const CelOutputBuffer &out, Point from)
{
if (DirX == DirectionX::EAST)
if (from.x >= out.w())
return false;
if (DirX == DirectionX::WEST)
if (from.x < 0)
return false;
if (DirY == DirectionY::SOUTH)
if (from.y >= out.h())
return false;
if (DirY == DirectionY::NORTH)
if (from.y < 0)
return false;
return true;
}
template <DirectionX DirX, DirectionY DirY>
void DrawMapLine(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
if (!InDirectionBounds<DirX, DirY>(out, from))
return;
bool drawFirstPixel = true; // skip the first pixel
// First clip in the X direction. Allows for a 1-2 pixel overdraw, taken care of later.
if (DirX == DirectionX::EAST) {
if (from.x < 0) {
int skip = -from.x;
if (skip % 2 != 0) {
drawFirstPixel = false;
++skip;
--height;
}
height -= Height(skip);
from.x = 0;
from.y += static_cast<int>(DirY) * Height(skip);
}
if (from.x + Width(height) > out.w()) {
height = HeightCeil(out.w() - from.x);
}
} else {
if (from.x >= out.w()) {
int skip = from.x - out.w() + 1;
if (skip % 2 != 0) {
drawFirstPixel = false;
++skip;
--height;
}
height -= Height(skip);
from.x = out.w() - 1;
from.y += static_cast<int>(DirY) * Height(skip);
}
if (from.x < Width(height)) {
height = HeightCeil(from.x + 1);
}
}
if (DirY == DirectionY::SOUTH) {
if (from.y < 0) {
const int skip = -from.y;
height -= skip;
from.y = 0;
from.x += static_cast<int>(DirX) * Width(skip);
}
if (from.y + height > out.h()) {
height = out.h() - from.y;
}
} else {
if (from.y >= out.h()) {
const int skip = from.y - out.h() + 1;
from.y = out.h() - 1;
height -= skip;
from.x += static_cast<int>(DirX) * Width(skip);
}
if (from.y < height) {
height = from.y + 1;
}
}
if (!InDirectionBounds<DirX, DirY>(out, from))
return;
const int overdrawX = DirX == DirectionX::EAST
? from.x + Width(height) + 1 - out.w()
: Width(height) + 1 - from.x;
const bool drawTipPixel = overdrawX != 1 && overdrawX != 2
&& !((DirY == DirectionY::SOUTH && from.y + height == out.h()) || (DirY == DirectionY::NORTH && from.y + 1 - height == 0));
const bool drawLastNonTipPixel = overdrawX != 2;
if (!drawFirstPixel)
SetPixel(out, { from.x + static_cast<int>(DirX), from.y }, colorIndex);
if (!drawLastNonTipPixel)
--height;
if (height >= 0)
UnsafeDrawMapLine<DirX, DirY>(out, from, height, drawTipPixel, colorIndex);
if (!drawLastNonTipPixel) {
SetPixel(
out,
{ from.x + 2 * static_cast<int>(DirX) * (height),
from.y + static_cast<int>(DirY) * (height) },
colorIndex);
}
}
template <DirectionX DirX, DirectionY DirY>
void DrawMapLine2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
// This is only used to draw small detail, so there is no unsafe version.
// We bounds-check each pixel individually instead.
while (width-- > 0) {
SetPixel(out, from, colorIndex);
from.y += static_cast<int>(DirY);
SetPixel(out, from, colorIndex);
from.y += static_cast<int>(DirY);
from.x += static_cast<int>(DirX);
}
SetPixel(out, from, colorIndex);
}
} // namespace
void DrawMapLineNE(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
DrawMapLine<DirectionX::EAST, DirectionY::NORTH>(out, from, height, colorIndex);
}
void DrawMapLineSE(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
DrawMapLine<DirectionX::EAST, DirectionY::SOUTH>(out, from, height, colorIndex);
}
void DrawMapLineNW(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
DrawMapLine<DirectionX::WEST, DirectionY::NORTH>(out, from, height, colorIndex);
}
void DrawMapLineSW(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
DrawMapLine<DirectionX::WEST, DirectionY::SOUTH>(out, from, height, colorIndex);
}
void DrawMapLineNE2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
DrawMapLine2<DirectionX::EAST, DirectionY::NORTH>(out, from, width, colorIndex);
}
void DrawMapLineSE2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
DrawMapLine2<DirectionX::EAST, DirectionY::SOUTH>(out, from, width, colorIndex);
}
void DrawMapLineNW2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
DrawMapLine2<DirectionX::WEST, DirectionY::NORTH>(out, from, width, colorIndex);
}
void DrawMapLineSW2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
DrawMapLine2<DirectionX::WEST, DirectionY::SOUTH>(out, from, width, colorIndex);
}
} // namespace devilution