Browse Source

🎉 New map renderer

Uses integer math only: This speeds up the rendering and eliminates some
zoom artifacts.

Improves player indicator look -- it's now symmetric and more legible.
pull/1868/head
Gleb Mazovetskiy 5 years ago committed by Anders Jenbo
parent
commit
afef72f916
  1. 1
      CMakeLists.txt
  2. 155
      Source/automap.cpp
  3. 210
      Source/automap_render.cpp
  4. 77
      Source/automap_render.hpp
  5. 48
      Source/engine.cpp
  6. 25
      Source/engine.h
  7. 8
      Source/qol/common.cpp

1
CMakeLists.txt

@ -272,6 +272,7 @@ add_library(PKWare STATIC
set(devilutionx_SRCS
Source/appfat.cpp
Source/automap.cpp
Source/automap_render.cpp
Source/capture.cpp
Source/codec.cpp
Source/control.cpp

155
Source/automap.cpp

@ -7,6 +7,7 @@
#include <algorithm>
#include "automap_render.hpp"
#include "control.h"
#include "inv.h"
#include "miniwin/miniwin.h"
@ -53,35 +54,30 @@ enum MapFlags : uint8_t {
// clang-format on
};
void DrawSquare(const CelOutputBuffer &out, Point center, uint8_t color)
void DrawDiamond(const CelOutputBuffer &out, Point center, uint8_t color)
{
const Point left { center.x - AmLine16, center.y };
const Point top { center.x, center.y - AmLine8 };
const Point right { center.x + AmLine16, center.y };
const Point bottom { center.x, center.y + AmLine8 };
DrawLineTo(out, top, left, color);
DrawLineTo(out, top, right, color);
DrawLineTo(out, bottom, left, color);
DrawLineTo(out, bottom, right, color);
DrawMapLineNE(out, left, AmLine8, color);
DrawMapLineSE(out, left, AmLine8, color);
DrawMapLineSE(out, top, AmLine8, color);
DrawMapLineNE(out, bottom, AmLine8, color);
}
void DrawMapVerticalDoor(const CelOutputBuffer &out, Point center)
{
const Point offset { center.x - AmLine16, center.y - AmLine8 };
DrawLineTo(out, { center.x + AmLine16, offset.y }, { center.x + AmLine8, offset.y + AmLine4 }, MapColorsDim);
DrawLineTo(out, { offset.x, center.y + AmLine8 }, { offset.x + AmLine8, center.y + AmLine8 - AmLine4 }, MapColorsDim);
DrawSquare(out, center, MapColorsBright);
DrawMapLineNE(out, { center.x + AmLine8, center.y - AmLine4 }, AmLine4, MapColorsDim);
DrawMapLineNE(out, { center.x - AmLine16, center.y + AmLine8 }, AmLine4, MapColorsDim);
DrawDiamond(out, center, MapColorsBright);
}
void DrawMapHorizontalDoor(const CelOutputBuffer &out, Point center)
{
const Point offset { center.x + AmLine16, center.y - AmLine8 };
DrawLineTo(out, { center.x - AmLine16, offset.y }, { center.x - AmLine16 + AmLine8, offset.y + AmLine4 }, MapColorsDim);
DrawLineTo(out, { offset.x, center.y + AmLine8 }, { offset.x - AmLine8, center.y + AmLine8 - AmLine4 }, MapColorsDim);
DrawSquare(out, center, MapColorsBright);
DrawMapLineSE(out, { center.x - AmLine16, center.y - AmLine8 }, AmLine4, MapColorsDim);
DrawMapLineSE(out, { center.x + AmLine8, center.y + AmLine4 }, AmLine4, MapColorsDim);
DrawDiamond(out, center, MapColorsBright);
}
/**
@ -111,10 +107,13 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
}
if ((flags & MapFlagsStairs) != 0) {
DrawLineTo(out, { center.x - AmLine8, center.y - AmLine8 - AmLine4 }, { center.x + AmLine8 + AmLine16, center.y + AmLine4 }, MapColorsBright);
DrawLineTo(out, { center.x - AmLine16, center.y - AmLine8 }, { center.x + AmLine16, center.y + AmLine8 }, MapColorsBright);
DrawLineTo(out, { center.x - AmLine16 - AmLine8, center.y - AmLine4 }, { center.x + AmLine8, center.y + AmLine8 + AmLine4 }, MapColorsBright);
DrawLineTo(out, { center.x - AmLine32, center.y }, { center.x, center.y + AmLine16 }, MapColorsBright);
constexpr int NumStairSteps = 4;
const Point offset = { -AmLine8, AmLine4 };
Point p = { center.x - AmLine8 , center.y - AmLine8 - AmLine4 };
for (int i = 0; i < NumStairSteps; ++i) {
DrawMapLineSE(out, p, AmLine16, MapColorsBright);
p += offset;
}
}
bool drawVertical = false;
@ -123,7 +122,7 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
bool drawCaveVertical = false;
switch (automapType & MapFlagsType) {
case 1: // stand-alone column or other unpassable object
DrawSquare(out, { center.x, center.y - AmLine8 }, MapColorsDim);
DrawDiamond(out, { center.x, center.y - AmLine8 }, MapColorsDim);
break;
case 2:
case 5:
@ -162,14 +161,14 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
DrawMapVerticalDoor(out, { center.x - AmLine16, center.y - AmLine8 });
}
if ((flags & MapFlagsVerticalGrate) != 0) { // right-facing half-wall
DrawLineTo(out, { center.x - AmLine16, center.y - AmLine8 }, { center.x - AmLine32, center.y }, MapColorsDim);
DrawMapLineNE(out, { center.x - AmLine32, center.y }, AmLine8, MapColorsDim);
flags |= MapFlagsVerticalArch;
}
if ((flags & MapFlagsVerticalArch) != 0) { // window or passable column
DrawSquare(out, { center.x, center.y - AmLine8 }, MapColorsDim);
DrawDiamond(out, { center.x, center.y - AmLine8 }, MapColorsDim);
}
if ((flags & (MapFlagsMapVerticalDoor | MapFlagsVerticalGrate | MapFlagsVerticalArch)) == 0) {
DrawLineTo(out, { center.x, center.y - AmLine16 }, { center.x - AmLine32, center.y }, MapColorsDim);
DrawMapLineNE(out, { center.x - AmLine32, center.y }, AmLine16, MapColorsDim);
}
}
@ -178,14 +177,14 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
DrawMapHorizontalDoor(out, { center.x + AmLine16, center.y - AmLine8 });
}
if ((flags & MapFlagsHorizontalGrate) != 0) {
DrawLineTo(out, { center.x + AmLine16, center.y - AmLine8 }, { center.x + AmLine32, center.y }, MapColorsDim);
DrawMapLineSE(out, { center.x + AmLine16, center.y - AmLine8 }, AmLine8, MapColorsDim);
flags |= MapFlagsHorizontalArch;
}
if ((flags & MapFlagsHorizontalArch) != 0) {
DrawSquare(out, { center.x, center.y - AmLine8 }, MapColorsDim);
DrawDiamond(out, { center.x, center.y - AmLine8 }, MapColorsDim);
}
if ((flags & (MapFlagsMapHorizontalDoor | MapFlagsHorizontalGrate | MapFlagsHorizontalArch)) == 0) {
DrawLineTo(out, { center.x, center.y - AmLine16 }, { center.x + AmLine32, center.y }, MapColorsDim);
DrawMapLineSE(out, { center.x, center.y - AmLine16 }, AmLine16, MapColorsDim);
}
}
@ -194,7 +193,7 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
if ((flags & MapFlagsMapVerticalDoor) != 0) {
DrawMapHorizontalDoor(out, { center.x - AmLine16, center.y + AmLine8 });
} else {
DrawLineTo(out, { center.x, center.y + AmLine16 }, { center.x - AmLine32, center.y }, MapColorsDim);
DrawMapLineSE(out, { center.x - AmLine32, center.y }, AmLine16, MapColorsDim);
}
}
@ -202,7 +201,7 @@ void DrawAutomapTile(const CelOutputBuffer &out, Point center, uint16_t automapT
if ((flags & MapFlagsMapHorizontalDoor) != 0) {
DrawMapVerticalDoor(out, { center.x + AmLine16, center.y + AmLine8 });
} else {
DrawLineTo(out, { center.x, center.y + AmLine16 }, { center.x + AmLine32, center.y }, MapColorsDim);
DrawMapLineNE(out, { center.x, center.y + AmLine16 }, AmLine16, MapColorsDim);
}
}
}
@ -244,7 +243,7 @@ void SearchAutomapItem(const CelOutputBuffer &out)
screen.x += 160;
}
screen.y -= AmLine8;
DrawSquare(out, screen, MapColorsItem);
DrawDiamond(out, screen, MapColorsItem);
}
}
}
@ -281,57 +280,57 @@ void DrawAutomapPlr(const CelOutputBuffer &out, int playerId)
}
base.y -= AmLine8;
Point point;
Point left;
Point right;
switch (plr[playerId]._pdir) {
case DIR_N:
point = { base.x, base.y - AmLine16 };
left = { base.x - AmLine4, base.y - AmLine8 };
right = { base.x + AmLine4, base.y - AmLine8 };
break;
case DIR_NE:
point = { base.x + AmLine16, base.y - AmLine8 };
left = { base.x + AmLine8, base.y - AmLine8 };
right = { base.x + AmLine8 + AmLine4, base.y };
break;
case DIR_E:
point = { base.x + AmLine16, base.y };
left = { base.x + AmLine8, base.y - AmLine4 };
right = { base.x + AmLine8, base.y + AmLine4 };
break;
case DIR_SE:
point = { base.x + AmLine16, base.y + AmLine8 };
left = { base.x + AmLine8 + AmLine4, base.y };
right = { base.x + AmLine8, base.y + AmLine8 };
break;
case DIR_N: {
const Point point { base.x, base.y - AmLine16 };
DrawVerticalLine(out, point, AmLine16, playerColor);
DrawMapLineNE2(out, { point.x - AmLine4, point.y + 2 * AmLine4 }, AmLine4, playerColor);
DrawMapLineNW2(out, { point.x + AmLine4, point.y + 2 * AmLine4 }, AmLine4, playerColor);
} break;
case DIR_NE: {
const Point point { base.x + AmLine16, base.y - AmLine8 };
DrawHorizontalLine(out, {point.x - AmLine8, point.y}, AmLine8, playerColor);
DrawMapLineNE(out, { point.x - 2 * AmLine8, point.y + AmLine8 }, AmLine8, playerColor);
DrawMapLineSW2(out, point, AmLine4, playerColor);
} break;
case DIR_E: {
const Point point { base.x + AmLine16, base.y };
DrawMapLineNW(out, point, AmLine4, playerColor);
DrawHorizontalLine(out, { point.x - AmLine16, point.y }, AmLine16, playerColor);
DrawMapLineSW(out, point, AmLine4, playerColor);
} break;
case DIR_SE: {
const Point point { base.x + AmLine16, base.y + AmLine8 };
DrawMapLineSE(out, { point.x - 2 * AmLine8, point.y - AmLine8 }, AmLine8, playerColor);
DrawHorizontalLine(out, { point.x - (AmLine8 + 1), point.y }, AmLine8 + 1, playerColor);
DrawMapLineNW2(out, point, AmLine4, playerColor);
} break;
case DIR_S:
case DIR_OMNI:
point = { base.x, base.y + AmLine16 };
left = { base.x + AmLine4, base.y + AmLine8 };
right = { base.x - AmLine4, base.y + AmLine8 };
break;
case DIR_SW:
point = { base.x - AmLine16, base.y + AmLine8 };
left = { base.x - AmLine4 - AmLine8, base.y };
right = { base.x - AmLine8, base.y + AmLine8 };
break;
case DIR_W:
point = { base.x - AmLine16, base.y };
left = { base.x - AmLine8, base.y - AmLine4 };
right = { base.x - AmLine8, base.y + AmLine4 };
break;
case DIR_NW:
point = { base.x - AmLine16, base.y - AmLine8 };
left = { base.x - AmLine8, base.y - AmLine8 };
right = { base.x - AmLine4 - AmLine8, base.y };
break;
case DIR_OMNI: {
const Point point { base.x, base.y + AmLine16 };
DrawVerticalLine(out, { point.x, point.y - AmLine16 }, AmLine16, playerColor);
DrawMapLineSW2(out, { point.x + AmLine4, point.y - 2 * AmLine4}, AmLine4, playerColor);
DrawMapLineSE2(out, { point.x - AmLine4, point.y - 2 * AmLine4}, AmLine4, playerColor);
} break;
case DIR_SW: {
const Point point { base.x - AmLine16, base.y + AmLine8 };
DrawMapLineNE2(out, point, AmLine4, playerColor);
DrawMapLineSW(out, { point.x + 2 * AmLine8, point.y - AmLine8 }, AmLine8, playerColor);
DrawHorizontalLine(out, point, AmLine8 + 1, playerColor);
} break;
case DIR_W: {
const Point point { base.x - AmLine16, base.y };
DrawMapLineNE(out, point, AmLine4, playerColor);
DrawHorizontalLine(out, point, AmLine16 + 1, playerColor);
DrawMapLineSE(out, point, AmLine4, playerColor);
} break;
case DIR_NW: {
const Point point { base.x - AmLine16, base.y - AmLine8 };
DrawMapLineNW(out, { point.x + 2 * AmLine8, point.y + AmLine8 }, AmLine8, playerColor);
DrawHorizontalLine(out, point, AmLine8 + 1, playerColor);
DrawMapLineSE2(out, point, AmLine4, playerColor);
} break;
}
DrawLineTo(out, base, point, playerColor);
DrawLineTo(out, point, left, playerColor);
DrawLineTo(out, point, right, playerColor);
}
/**

210
Source/automap_render.cpp

@ -0,0 +1,210 @@
#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

77
Source/automap_render.hpp

@ -0,0 +1,77 @@
#include "engine.h"
namespace devilution {
/**
* @brief Draw a line in the target buffer from the given point towards north east at an `atan(1/2)` angle.
*
* Draws 2 horizontal pixels for each vertical step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x + 2 * height + 1, from.y - height }`.
*/
void DrawMapLineNE(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards south east at an `atan(1/2)` angle.
*
* Draws 2 horizontal pixels for each vertical step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x + 2 * height + 1, from.y + height }`.
*/
void DrawMapLineSE(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards north west at an `atan(1/2)` angle.
*
* Draws 2 horizontal pixels for each vertical step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x - 2 * height + 1, from.y - height }`.
*/
void DrawMapLineNW(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards south west at an `atan(1/2)` angle.
*
* Draws 2 horizontal pixels for each vertical step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x - 2 * height + 1, from.y + height }`.
*/
void DrawMapLineSW(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards north east at an `atan(1/2)` angle.
*
* Draws 2 vertical pixels for each horizontal step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x + width + 1, from.y - 2 * width }`.
*/
void DrawMapLineNE2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards south east at an `atan(2)` angle.
*
* Draws 2 vertical pixels for each horizontal step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x + width + 1, from.y + 2 * width }`.
*/
void DrawMapLineSE2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards north west at an `atan(1/2)` angle.
*
* Draws 2 vertical pixels for each horizontal step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x - (width + 1), from.y - 2 * width }`.
*/
void DrawMapLineNW2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex);
/**
* @brief Draw a line in the target buffer from the given point towards south west at an `atan(1/2)` angle.
*
* Draws 2 vertical pixels for each horizontal step, then an additional one where it draws 1 pixel.
*
* The end point is at `{ from.x - (width + 1), from.y + 2 * width }`.
*/
void DrawMapLineSW2(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex);
} // namespace devilution

48
Source/engine.cpp

@ -559,18 +559,44 @@ void SetPixel(const CelOutputBuffer &out, Point position, BYTE col)
*out.at(position.x, position.y) = col;
}
void DrawLineTo(const CelOutputBuffer &out, Point a, Point b, BYTE color_index)
void DrawHorizontalLine(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
int dx = b.x - a.x;
int dy = b.y - a.y;
int steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);
float ix = dx / (float)steps;
float iy = dy / (float)steps;
float sx = a.x;
float sy = a.y;
for (int i = 0; i <= steps; i++, sx += ix, sy += iy) {
SetPixel(out, { static_cast<int>(sx), static_cast<int>(sy) }, color_index);
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 = (from.x + width) - out.w();
return UnsafeDrawHorizontalLine(out, from, width, colorIndex);
}
void UnsafeDrawHorizontalLine(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
std::memset(out.at(from.x, from.y), colorIndex, width);
}
void DrawVerticalLine(const CelOutputBuffer &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 CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex)
{
auto *dst = out.at(from.x, from.y);
const auto pitch = out.pitch();
while (height-- > 0) {
*dst = colorIndex;
dst += pitch;
}
}

25
Source/engine.h

@ -565,13 +565,28 @@ void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite
void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Draw a line in the target buffer
* @brief Draw a horizontal line segment in the target buffer (left to right)
* @param out Target buffer
* @param a Back buffer coordinate
* @param b Back buffer coordinate
* @param color_index Color index from current palette
* @param from Start of the line segment
* @param width
* @param colorIndex Color index from current palette
*/
void DrawLineTo(const CelOutputBuffer &out, Point a, Point b, BYTE color_index);
void DrawHorizontalLine(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex);
/** Same as DrawHorizontalLine but without bounds clipping. */
void UnsafeDrawHorizontalLine(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* @brief Draw a vertical line segment in the target buffer (top to bottom)
* @param out Target buffer
* @param from Start of the line segment
* @param height
* @param colorIndex Color index from current palette
*/
void DrawVerticalLine(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/** Same as DrawVerticalLine but without bounds clipping. */
void UnsafeDrawVerticalLine(const CelOutputBuffer &out, Point from, int height, std::uint8_t colorIndex);
/**
* Draws a half-transparent rectangle by blacking out odd pixels on odd lines,

8
Source/qol/common.cpp

@ -25,16 +25,12 @@ int GetTextWidth(const char *s)
void FastDrawHorizLine(const CelOutputBuffer &out, int x, int y, int width, Uint8 col)
{
memset(out.at(x, y), col, width);
UnsafeDrawHorizontalLine(out, {x, y}, width, col);
}
void FastDrawVertLine(const CelOutputBuffer &out, int x, int y, int height, Uint8 col)
{
BYTE *p = out.at(x, y);
for (int j = 0; j < height; j++) {
*p = col;
p += out.pitch();
}
UnsafeDrawVerticalLine(out, {x, y}, height, col);
}
char *PrintWithSeparator(char *out, long long n)

Loading…
Cancel
Save