304 lines
8.6 KiB
C++
304 lines
8.6 KiB
C++
#include "RenderTarget.hpp"
|
|
|
|
#include <cmath>
|
|
|
|
namespace frame::render
|
|
{
|
|
|
|
RenderTarget::RenderTarget(Size size)
|
|
: image(size)
|
|
, root_size{0, 0, (int)size.width, (int)size.height}
|
|
{
|
|
}
|
|
|
|
void RenderTarget::DrawPixel(Vector const& position, Color color)
|
|
{
|
|
auto current = getCurrentSize();
|
|
|
|
if(position.x < 0 || position.y < 0 || position.x >= current.width
|
|
|| position.y >= current.height
|
|
|| (position.x + current.left) >= root_size.width
|
|
|| (position.y + current.top) >= root_size.height)
|
|
return;
|
|
image.at(position.x + current.left, position.y + current.top) = color;
|
|
}
|
|
|
|
void RenderTarget::DrawLine(Vector const& start,
|
|
Vector const& end,
|
|
Color color)
|
|
{
|
|
auto deltaX = std::abs(start.x - end.x);
|
|
auto deltaY = std::abs(start.y - end.y);
|
|
|
|
auto rX = start.x < end.x ? 1 : -1;
|
|
auto rY = start.y < end.y ? 1 : -1;
|
|
|
|
bool swap = false;
|
|
|
|
if(deltaX < deltaY)
|
|
{
|
|
swap = true;
|
|
std::swap(deltaX, deltaY);
|
|
std::swap(rX, rY);
|
|
}
|
|
|
|
auto pos = start;
|
|
auto E = 2 * deltaY - deltaX;
|
|
|
|
DrawPixel(pos, color);
|
|
|
|
while(pos != end)
|
|
{
|
|
if(E <= 0)
|
|
{
|
|
if(!swap)
|
|
pos.x += rX;
|
|
else
|
|
pos.y += rX;
|
|
|
|
E += 2 * deltaY;
|
|
} else
|
|
{
|
|
if(!swap)
|
|
{
|
|
pos.x += rX;
|
|
pos.y += rY;
|
|
} else
|
|
{
|
|
pos.y += rX;
|
|
pos.x += rY;
|
|
}
|
|
|
|
E += 2 * deltaY - 2 * deltaX;
|
|
}
|
|
|
|
if(pos.x < 0 || pos.y < 0 || pos.x >= image.getWidth()
|
|
|| pos.y >= image.getHeight())
|
|
break;
|
|
|
|
DrawPixel(pos, color);
|
|
}
|
|
}
|
|
|
|
void RenderTarget::DrawCircle(Vector const& center,
|
|
int32_t radius,
|
|
int32_t line,
|
|
Color color)
|
|
{
|
|
int32_t const r_2 = radius * radius;
|
|
Vector pos{0, -radius};
|
|
|
|
for(auto i = 0; i < line; ++i)
|
|
{
|
|
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
|
}
|
|
|
|
while(pos.x <= radius / std::sqrt(2.f))
|
|
{
|
|
float E = pos.x * pos.x
|
|
+ ((float)pos.y + 0.5f) * ((float)pos.y + 0.5f) - r_2;
|
|
|
|
if(E <= 0)
|
|
{
|
|
pos.x += 1;
|
|
|
|
E += 2 * pos.x + 3;
|
|
} else
|
|
{
|
|
pos.x += 1;
|
|
pos.y += 1;
|
|
|
|
E += 2 * pos.x - 2 * pos.y + 5;
|
|
}
|
|
|
|
for(auto i = 0; i < line; ++i)
|
|
{
|
|
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderTarget::DrawRectFilled(Vector const& LeftTop,
|
|
Vector const& BottomRight,
|
|
Color color)
|
|
{
|
|
auto pos = LeftTop;
|
|
|
|
while(pos.y <= BottomRight.y)
|
|
{
|
|
while(pos.x <= BottomRight.x)
|
|
{
|
|
DrawPixel(pos, color);
|
|
pos.x++;
|
|
}
|
|
pos.x = LeftTop.x;
|
|
pos.y++;
|
|
}
|
|
}
|
|
|
|
void RenderTarget::DrawRect(Vector const& TopLeft,
|
|
Vector const& BottomRight,
|
|
uint8_t LineWidth,
|
|
Color color)
|
|
{
|
|
auto pos = TopLeft;
|
|
|
|
auto const lw = LineWidth - 1;
|
|
|
|
DrawRectFilled(TopLeft, {BottomRight.x, TopLeft.y + lw}, color);
|
|
DrawRectFilled({TopLeft.x, BottomRight.y - lw}, BottomRight);
|
|
|
|
DrawRectFilled({TopLeft.x, TopLeft.y + lw},
|
|
{TopLeft.x + lw, BottomRight.y - lw});
|
|
DrawRectFilled({BottomRight.x - lw, TopLeft.y + lw},
|
|
{BottomRight.x, BottomRight.y - lw});
|
|
}
|
|
|
|
void RenderTarget::DrawImage(Vector topLeft, Image const& image)
|
|
{
|
|
auto pos = topLeft;
|
|
|
|
for(auto y = 0; y < image.getHeight(); ++y)
|
|
{
|
|
for(auto x = 0; x < image.getWidth(); ++x)
|
|
{
|
|
DrawPixel({pos.x + x, pos.y + y}, (Color)image.at(x, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector RenderTarget::DrawGlyph(Vector centerLine,
|
|
font::Glyph const& g,
|
|
Color color)
|
|
{
|
|
auto const& height = g.height;
|
|
|
|
Vector topleft = centerLine;
|
|
topleft.x += g.x_offset;
|
|
topleft.y += g.y_offset;
|
|
|
|
DrawImage(topleft, g.image);
|
|
centerLine.x += g.advance;
|
|
return centerLine;
|
|
}
|
|
|
|
void RenderTarget::DrawText(std::string_view pText,
|
|
Vector pCenterLineStart,
|
|
font::Font const& pFont,
|
|
uint32_t size)
|
|
{
|
|
auto pos = pCenterLineStart;
|
|
for(auto c : pText)
|
|
{
|
|
pos = DrawGlyph(pos, pFont.getGlyph(size, c));
|
|
}
|
|
}
|
|
|
|
void RenderTarget::DrawText(Rect rect,
|
|
std::string pText,
|
|
font::Font const& pFont,
|
|
uint32_t size,
|
|
AlignHorizontal alignHorizontal,
|
|
AlignVertical alignVertical)
|
|
{
|
|
auto const lineSpacing = pFont.getLineSpacing(size);
|
|
auto const lineSpacing_3 = lineSpacing / 3;
|
|
|
|
Vector start{rect.top, rect.left};
|
|
auto text_height = pFont.getHeight(pText, size);
|
|
auto length = pFont.getLength(pText, size);
|
|
auto y_offset = pFont.getMaxYOffset(pText, size);
|
|
|
|
switch(alignVertical)
|
|
{
|
|
case AlignVertical::TOP: start.y -= y_offset; break;
|
|
case AlignVertical::CENTER:
|
|
start.y += rect.height / 2;
|
|
start.y += text_height / 2;
|
|
start.y -= y_offset + text_height;
|
|
break;
|
|
case AlignVertical::BOTTOM:
|
|
start.y += rect.height;
|
|
start.y -= text_height + y_offset;
|
|
break;
|
|
}
|
|
|
|
switch(alignHorizontal)
|
|
{
|
|
case AlignHorizontal::CENTER:
|
|
start.x += rect.width / 2;
|
|
start.x -= length / 2;
|
|
break;
|
|
|
|
case AlignHorizontal::RIGHT: start.x += rect.width - length; break;
|
|
}
|
|
|
|
DrawText(pText, start, pFont, size);
|
|
}
|
|
|
|
void RenderTarget::DrawTextGlyphBounds(std::string_view pText,
|
|
Vector pCenterLineStart,
|
|
font::Font const& pFont,
|
|
uint32_t size)
|
|
{
|
|
auto pos = pCenterLineStart;
|
|
|
|
auto lineSpacing = pFont.getLineSpacing(size);
|
|
auto _3 = lineSpacing / 3;
|
|
for(auto c : pText)
|
|
{
|
|
auto const& g = pFont.getGlyph(size, c);
|
|
DrawRect({int32_t(pos.x), int32_t(pos.y - _3 * 2)},
|
|
{int32_t(pos.x + g.advance), int32_t(pos.y + _3)},
|
|
1);
|
|
|
|
pos.x += g.advance;
|
|
}
|
|
}
|
|
|
|
void RenderTarget::Clear(Color color)
|
|
{
|
|
image.Clear(color);
|
|
}
|
|
|
|
Rect RenderTarget::getCurrentSize() const
|
|
{
|
|
if(Scissor.size() == 0)
|
|
{
|
|
return root_size;
|
|
}
|
|
return Scissor.top();
|
|
}
|
|
|
|
void RenderTarget::pushViewport(Rect rect)
|
|
{
|
|
auto current = getCurrentSize();
|
|
|
|
Scissor.push({current.top + rect.top,
|
|
current.left + rect.left,
|
|
rect.width,
|
|
rect.height});
|
|
}
|
|
|
|
void RenderTarget::popViewport()
|
|
{
|
|
Scissor.pop();
|
|
}
|
|
|
|
void RenderTarget::DrawPointsMirrorCircle(Vector const& center,
|
|
Vector const& pos,
|
|
Color color)
|
|
{
|
|
DrawPixel(center + pos, color);
|
|
DrawPixel(center + Vector{pos.x, pos.y * -1}, color);
|
|
DrawPixel(center + Vector{pos.x * -1, pos.y * -1}, color);
|
|
DrawPixel(center + Vector{pos.x * -1, pos.y}, color);
|
|
|
|
DrawPixel(center + Vector{pos.y, pos.x}, color);
|
|
DrawPixel(center + Vector{pos.y, pos.x * -1}, color);
|
|
DrawPixel(center + Vector{pos.y * -1, pos.x * -1}, color);
|
|
DrawPixel(center + Vector{pos.y * -1, pos.x}, color);
|
|
}
|
|
|
|
} // namespace frame::render
|