6#include "fifechan/backends/sdl2/graphics.hpp"
8#include <SDL2/SDL_render.h>
18#include "fifechan/backends/sdl2/image.hpp"
19#include "fifechan/backends/sdl2/truetypefont.hpp"
20#include "fifechan/exception.hpp"
21#include "fifechan/font.hpp"
22#include "fifechan/image.hpp"
27 Graphics::Graphics() : mAlpha(false) { }
29 Graphics::~Graphics() =
default;
63 rect.w = clip_rect.
width;
84 rect.w = clip_rect.
width;
99 "Clip stack is empty, perhaps you"
100 "called a draw function outside of _beginDraw() and _endDraw()?");
115 auto const * srcImage =
dynamic_cast<Image const *
>(image);
117 if (srcImage ==
nullptr) {
118 throwException(
"Trying to draw an image of unknown format, must be an Image.");
121 SDL_Texture* texture = srcImage->
getTexture();
122 if (texture !=
nullptr) {
131 "Clip stack is empty, perhaps you"
132 "called a draw function outside of _beginDraw() and _endDraw()?");
161 "Clip stack is empty, perhaps you"
162 "called a draw function outside of _beginDraw() and _endDraw()?");
184 "Clip stack is empty, perhaps you"
185 "called a draw function outside of _beginDraw() and _endDraw()?");
193 if (y < top.y || y >= top.
y + top.
height) {
210 if (top.
x + top.
width <= x2) {
211 if (top.
x + top.
width <= x1) {
214 x2 = top.
x + top.
width - 1;
227 "Clip stack is empty, perhaps you"
228 "called a draw function outside of _beginDraw() and _endDraw()?");
236 if (x < top.x || x >= top.
x + top.
width) {
253 if (top.
y + top.
height <= y2) {
254 if (top.
y + top.
height <= y1) {
268 int const x1 = rectangle.
x;
269 int const x2 = rectangle.
x + rectangle.
width - 1;
270 int const y1 = rectangle.
y;
271 int const y2 = rectangle.
y + rectangle.
height - 1;
285 "Clip stack is empty, perhaps you"
286 "called a draw function outside of _beginDraw() and _endDraw()?");
305 "Clip stack is empty, perhaps you"
306 "called a draw function outside of _beginDraw() and _endDraw()?");
320 if (x1 == x2 && y1 == y2) {
328 auto const dx =
static_cast<float>(x2 - x1);
329 auto const dy =
static_cast<float>(y2 - y1);
330 float const length = std::sqrt((dx * dx) + (dy * dy));
331 float const offsetX = (dy / length) * (
static_cast<float>(width) / 2.0F);
332 float const offsetY = (dx / length) * (
static_cast<float>(width) / 2.0F);
334 for (
int i = -
static_cast<int>(width) / 2; i <= static_cast<int>(width) / 2; ++i) {
335 int const startX =
static_cast<int>(x1 + (
static_cast<float>(i) * offsetX));
336 int const startY =
static_cast<int>(y1 - (
static_cast<float>(i) * offsetY));
337 int const endX =
static_cast<int>(x2 + (
static_cast<float>(i) * offsetX));
338 int const endY =
static_cast<int>(y2 - (
static_cast<float>(i) * offsetY));
339 SDL_RenderDrawLine(
mRenderTarget, startX, startY, endX, endY);
349 "Clip stack is empty, perhaps you"
350 "called a draw function outside of _beginDraw() and _endDraw()?");
367 int const dx = x2 - x1;
368 int const dy = y2 - y1;
369 int const radius = std::max(1,
static_cast<int>(width) / 2);
370 int const stepCount = std::max(std::abs(dx), std::abs(dy));
372 auto drawFilledDisk = [&](
int centerX,
int centerY) {
373 for (
int offsetY = -radius; offsetY <= radius; ++offsetY) {
374 for (
int offsetX = -radius; offsetX <= radius; ++offsetX) {
375 if ((offsetX * offsetX) + (offsetY * offsetY) <= (radius * radius)) {
376 SDL_RenderDrawPoint(
mRenderTarget, centerX + offsetX, centerY + offsetY);
382 if (stepCount == 0) {
383 drawFilledDisk(x1, y1);
388 for (
int step = 0; step <= stepCount; ++step) {
389 int const centerX = x1 + ((dx * step) / stepCount);
390 int const centerY = y1 + ((dy * step) / stepCount);
391 drawFilledDisk(centerX, centerY);
401 "Clip stack is empty, perhaps you"
402 "called a draw function outside of _beginDraw() and _endDraw()?");
406 int const x0 = center.x + top.
xOffset;
407 int const y0 = center.y + top.
yOffset;
412 for (
int y = -radius; std::cmp_less_equal(y, radius); y++) {
413 for (
int x = -radius; std::cmp_less_equal(x, radius); x++) {
414 if (x * x + y * y <= radius * radius) {
425 fcn::Point bezierPoint(std::vector<fcn::Point>
const & controlPoints,
float t)
427 std::vector<fcn::Point> points = controlPoints;
428 while (points.size() > 1) {
429 std::vector<fcn::Point> nextPoints;
430 for (
size_t i = 0; i < points.size() - 1; ++i) {
431 int const x =
static_cast<int>(((1 - t) * points[i].x) + (t * points[i + 1].x));
432 int const y =
static_cast<int>(((1 - t) * points[i].y) + (t * points[i + 1].y));
433 nextPoints.emplace_back(x, y);
440 constexpr float degToRad(
float degrees)
442 return degrees * std::numbers::pi_v<float> / 180.0F;
450 "Clip stack is empty, perhaps you"
451 "called a draw function outside of _beginDraw() and _endDraw()?");
455 int const x0 = center.x + top.
xOffset;
456 int const y0 = center.y + top.
yOffset;
461 startAngle = startAngle % 360;
462 endAngle = endAngle % 360;
464 if (endAngle < startAngle) {
468 for (
int y = -radius; std::cmp_less_equal(y, radius); y++) {
469 for (
int x = -radius; std::cmp_less_equal(x, radius); x++) {
470 if (x * x + y * y <= radius * radius) {
471 float angle = std::atan2(y, x) * 180.0F / std::numbers::pi_v<float>;
476 if (angle >= startAngle && angle <= endAngle) {
488 int normalizeAngle(
int angle)
502 "Clip stack is empty, perhaps you"
503 "called a draw function outside of _beginDraw() and _endDraw()?");
507 int const x0 = center.x + top.
xOffset;
508 int const y0 = center.y + top.
yOffset;
533 p = p + 2 * y - 2 * x + 1;
544 "Clip stack is empty, perhaps you"
545 "called a draw function outside of _beginDraw() and _endDraw()?");
549 int const x0 = center.x + top.
xOffset;
550 int const y0 = center.y + top.
yOffset;
555 startAngle = normalizeAngle(startAngle);
556 endAngle = normalizeAngle(endAngle);
558 if (endAngle < startAngle) {
566 auto isInSegment = [&](
int x,
int y) {
567 float angle = std::atan2(
static_cast<float>(y),
static_cast<float>(x)) * 180.0F / std::numbers::pi_v<float>;
571 return angle >= startAngle && angle <= endAngle;
575 if (isInSegment(x, y)) {
578 if (isInSegment(-x, y)) {
581 if (isInSegment(x, -y)) {
584 if (isInSegment(-x, -y)) {
587 if (isInSegment(y, x)) {
590 if (isInSegment(-y, x)) {
593 if (isInSegment(y, -x)) {
596 if (isInSegment(-y, -x)) {
605 p = p + 2 * y - 2 * x + 1;
616 "Clip stack is empty, perhaps you"
617 "called a draw function outside of _beginDraw() and _endDraw()?");
622 fcn::Point previousPoint = bezierPoint(controlPoints, 0.0F);
624 for (
int i = 1; i <= segments; ++i) {
625 float const t =
static_cast<float>(i) /
static_cast<float>(segments);
626 fcn::Point const currentPoint = bezierPoint(controlPoints, t);
628 drawLine(previousPoint.x, previousPoint.y, currentPoint.x, currentPoint.y, width);
630 previousPoint = currentPoint;
640 "Clip stack is empty, perhaps you"
641 "called a draw function outside of _beginDraw() and _endDraw()?");
644 if (points.size() < 2) {
651 for (
size_t i = 0; i < points.size() - 1; ++i) {
652 drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, width);
672 return std::make_shared<TrueTypeFont>(filename, size);
679 "Clip stack is empty, perhaps you"
680 "called a draw function outside of _beginDraw() and _endDraw()?");
687 destination.w = source.w;
688 destination.h = source.h;
690 SDL_RenderCopy(
mRenderTarget, texture, &source, &destination);
A rectangle specifically used for clipping rendering regions.
int xOffset
Holds the x offset of the x coordinate.
int yOffset
Holds the y offset of the y coordinate.
uint8_t a
Alpha color component (0-255).
virtual void popClipArea()
Removes the top most clip area from the stack.
virtual bool pushClipArea(Rectangle area)
Pushes a clip area onto the stack.
std::stack< ClipRectangle > mClipStack
Holds the clip area stack.
Abstract holder for image data.
Represents a 2D coordinate (X, Y).
Represents a rectangular area (X, Y, Width, Height).
bool isContaining(int x, int y) const
Checks the rectangle contains a point.
int width
Holds the width of the rectangle.
int y
Holds the x coordinate of the rectangle.
int x
Holds the x coordinate of the rectangle.
int height
Holds the height of the rectangle.
bool isIntersecting(Rectangle const &rectangle) const
Checks if another rectangle intersects with the rectangle.
Color mColor
Current drawing color.
void drawPolyLine(PointVector const &points, unsigned int width) override
Draws lines between points with given width.
Uint8 r
Cached renderer color components (previous renderer color).
Uint8 a
Previous alpha component from renderer.
virtual void drawSDLTexture(SDL_Texture *texture, SDL_Rect source, SDL_Rect destination)
Draws an SDL_Texture on the target surface.
void drawLine(int x1, int y1, int x2, int y2) override
Draws a line.
void popClipArea() override
Removes the top most clip area from the stack.
void drawFillCircleSegment(Point const ¢er, unsigned int radius, int startAngle, int endAngle) override
Draws a filled circle segment.
void restoreRenderColor()
Restore the rendering color after drawing.
void drawRoundStroke(int x1, int y1, int x2, int y2, unsigned int width) override
Draws a round brush stroke along the line segment.
void _beginDraw() override
Initializes drawing.
virtual void setTarget(SDL_Renderer *renderer, int width, int height)
Sets the target SDL_Renderer to use for drawing.
int mHeight
Screen height.
void drawHorizontalLine(int x1, int y, int x2)
Draws a horizontal line.
void saveRenderColor()
Save the current rendering color before drawing.
void drawFillCircle(Point const ¢er, unsigned int radius) override
Draws a filled circle.
void drawImage(fcn::Image const *image, int srcX, int srcY, int dstX, int dstY, int width, int height) override
Draws a part of an image.
std::shared_ptr< Font > createFont(std::string const &filename, int size) override
Creates a font for this graphics backend.
Uint8 g
Previous green component from renderer.
SDL_Renderer * mRenderTarget
The SDL_Renderer used for accelerated drawing.
bool pushClipArea(fcn::Rectangle area) override
Pushes a clip area onto the stack.
void drawPoint(int x, int y) override
Draws a single point/pixel.
Color const & getColor() const override
Gets the color to use when drawing.
void drawCircleSegment(Point const ¢er, unsigned int radius, int startAngle, int endAngle) override
Draws a simple, non-filled circle segment with a one pixel width.
Uint8 b
Previous blue component from renderer.
void setColor(Color const &color) override
Sets the color to use when drawing.
void drawVerticalLine(int x, int y1, int y2)
Draws a vertical line.
bool mAlpha
Whether alpha blending is enabled.
void drawRectangle(Rectangle const &rectangle) override
Draws a simple, non-filled rectangle with a one pixel width.
virtual SDL_Renderer * getRenderTarget() const
Gets the target SDL_Renderer.
void _endDraw() override
Deinitializes the drawing process.
void fillRectangle(Rectangle const &rectangle) override
Draws a filled rectangle.
void drawCircle(Point const ¢er, unsigned int radius) override
Draws a simple, non-filled circle with a one pixel width.
void drawBezier(PointVector const &points, int segments, unsigned int width) override
Draws a bezier curve.
SDL2-specific implementation of Image.
virtual SDL_Texture * getTexture() const
Gets the SDL texture for the image.
Unified header for the SDL backend.