#include "RenderTarget.hpp" #include 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