Simple GUI Library
fill.cpp
Go to the documentation of this file.
1/**
2 * @author Nikita Mochalov (github.com/tralf-strues)
3 * @file fill.cpp
4 * @date 2021-11-07
5 *
6 * @copyright Copyright (c) 2021
7 */
8
9#include <algorithm>
10#include "sml/graphics_wrapper/primitives.h"
11#include "paint/fill.h"
12
13using namespace Sgl;
14
15//------------------------------------------------------------------------------
16// ColorFill
17//------------------------------------------------------------------------------
18ColorFill::ColorFill(Sml::Color color)
19 : m_Color(color) {}
20
21void ColorFill::fillLine(const Sml::Vec2i& start, const Sml::Vec2i& end,
22 const Sml::Rectangle<int32_t>& targetRegion) const
23{
24 Sml::Renderer::getInstance().setColor(m_Color);
25
26 Sml::Vec2i translatedStart = start + targetRegion.pos;
27 Sml::Vec2i translatedEnd = end + targetRegion.pos;
28
29 Sml::renderLine(translatedStart, translatedEnd);
30}
31
32void ColorFill::fillArea(const Sml::Rectangle<int32_t>& area,
33 const Sml::Rectangle<int32_t>& targetRegion) const
34{
35 Sml::Renderer::getInstance().setColor(m_Color);
36
37 Sml::Rectangle<int32_t> translatedArea = area;
38 translatedArea.pos += targetRegion.pos;
39
40 Sml::renderFilledRect(translatedArea);
41}
42
43void ColorFill::fillPoint(const Sml::Vec2i& point,
44 const Sml::Rectangle<int32_t>& targetRegion) const
45{
46 Sml::Renderer::getInstance().setColor(m_Color);
47
48 Sml::Vec2i translatedPoint = point + targetRegion.pos;
49 Sml::renderPoint(translatedPoint);
50}
51
52//------------------------------------------------------------------------------
53// LinearGradientFill->Stop
54//------------------------------------------------------------------------------
55const LinearGradientFill LinearGradientFill::RAINBOX_HORIZONTAL = {Sgl::LinearGradientFill::Direction::HORIZONTAL,
56 {{0.00, 0xFF'00'00'FF},
57 {0.15, 0xFF'FF'00'FF},
58 {0.33, 0x00'FF'00'FF},
59 {0.49, 0x00'FF'FF'FF},
60 {0.67, 0x00'00'FF'FF},
61 {0.84, 0xFF'00'FF'FF},
62 {1.00, 0xFF'00'00'FF}}};
63
64LinearGradientFill::Stop::Stop(float offset, Sml::Color color)
65 : m_Offset(offset), m_Color(Sml::colorToNormalized(color)) {}
66
67float LinearGradientFill::Stop::getOffset() const { return m_Offset; }
68const Sml::Vec4f& LinearGradientFill::Stop::getColorVector() const { return m_Color; }
69Sml::Color LinearGradientFill::Stop::getColor() const { return Sml::colorFromNormalized(m_Color); }
70
71bool LinearGradientFill::Stop::operator<(const LinearGradientFill::Stop& other) const
72{
73 return getOffset() < other.getOffset();
74}
75
76//------------------------------------------------------------------------------
77// LinearGradientFill
78//------------------------------------------------------------------------------
79LinearGradientFill::LinearGradientFill(const std::initializer_list<Stop>& stops)
80{
81 m_Stops.insert(m_Stops.end(), stops);
82 std::sort(m_Stops.begin(), m_Stops.end());
83}
84
85LinearGradientFill::LinearGradientFill(Direction direction, const std::initializer_list<Stop>& stops)
86 : LinearGradientFill(stops)
87{
88 setDirection(direction);
89}
90
91void LinearGradientFill::addStop(const Stop& stop)
92{
93 m_Stops.push_back(stop);
94 std::sort(m_Stops.begin(), m_Stops.end());
95}
96
97void LinearGradientFill::setDirection(Direction direction)
98{
99 m_Direction = direction;
100}
101
102void LinearGradientFill::fillLine(const Sml::Vec2i& start,
103 const Sml::Vec2i& end,
104 const Sml::Rectangle<int32_t>& targetRegion) const
105{
106 if (m_Stops.size() < 2 || start == end) { return; }
107
108 int32_t x0 = (int32_t) start.x;
109 int32_t y0 = (int32_t) start.y;
110 int32_t x1 = (int32_t) end.x;
111 int32_t y1 = (int32_t) end.y;
112
113 int32_t dx = std::abs(x1 - x0);
114 int32_t dy = std::abs(y1 - y0);
115 int32_t sx = (x0 < x1) ? 1 : -1;
116 int32_t sy = (y0 < y1) ? 1 : -1;
117 int32_t err = dx - dy;
118
119 float length = Sml::length(end - start);
120
121 int32_t prevStop = -1;
122 int32_t nextStop = 0;
123 int32_t stops = m_Stops.size();
124
125 while (true)
126 {
127 float offset = Sml::length(Sml::Vec2i(x0, y0) - start) / length;
128
129 if (nextStop != stops && offset >= m_Stops[nextStop].getOffset() && (nextStop + 1) != stops)
130 {
131 ++prevStop;
132 ++nextStop;
133 }
134
135 if (prevStop == -1 && nextStop != stops)
136 {
137 continue;
138 }
139
140 if (prevStop == stops || nextStop == stops)
141 {
142 break;
143 }
144
145 float alpha = (offset - m_Stops[prevStop].getOffset()) / (m_Stops[nextStop].getOffset() - m_Stops[prevStop].getOffset());
146 Sml::Vec4f color = (1 - alpha) * m_Stops[prevStop].getColorVector() + alpha * m_Stops[nextStop].getColorVector();
147
148 Sml::Renderer::getInstance().setColor(Sml::colorFromNormalized(color));
149 Sml::renderPoint(Sml::Vec2i(x0, y0) + targetRegion.pos);
150
151 if ((x0 == x1) && (y0 == y1))
152 {
153 break;
154 }
155
156 int e2 = 2 * err;
157 if (e2 > -dy)
158 {
159 err -= dy;
160 x0 += sx;
161 }
162
163 if (e2 < dx)
164 {
165 err += dx;
166 y0 += sy;
167 }
168 }
169}
170
171void LinearGradientFill::fillArea(const Sml::Rectangle<int32_t>& area,
172 const Sml::Rectangle<int32_t>& targetRegion) const
173{
174 if (m_Stops.size() < 2) { return; }
175
176 if (m_Direction == Direction::HORIZONTAL)
177 {
178 for (int32_t row = 0; row < area.height; ++row)
179 {
180 fillLine(Sml::Vec2i(area.pos.x, area.pos.y + row),
181 Sml::Vec2i(area.pos.x + area.width - 1, area.pos.y + row),
182 targetRegion);
183 }
184 }
185 else
186 {
187 for (int32_t column = 0; column < area.width; ++column)
188 {
189 fillLine(Sml::Vec2i(area.pos.x + column, area.pos.y),
190 Sml::Vec2i(area.pos.x + column, area.pos.y + area.height - 1),
191 targetRegion);
192 }
193 }
194}
195
196void LinearGradientFill::fillPoint(const Sml::Vec2i& point,
197 const Sml::Rectangle<int32_t>& targetRegion) const
198{
199 if (m_Stops.size() < 2) { return; }
200}