Files
Frame/frame/src/render/RenderTarget.cpp
2022-03-06 03:04:32 +01:00

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