roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
transformation.cpp
Go to the documentation of this file.
1#include "transformation.h"
2
4
5namespace roo_display {
6
7Transformation::Transformation(bool xy_swap, int16_t x_scale, int16_t y_scale,
8 int16_t x_offset, int16_t y_offset)
9 : x_scale_(x_scale),
10 y_scale_(y_scale),
11 x_offset_(x_offset),
12 y_offset_(y_offset),
13 xy_swap_(xy_swap),
14 clipped_(false),
15 clip_box_(Box::MaximumBox()) {}
16
17Transformation::Transformation(bool xy_swap, int16_t x_scale, int16_t y_scale,
18 int16_t x_offset, int16_t y_offset, bool clipped,
19 Box clip_box)
20 : x_scale_(x_scale),
21 y_scale_(y_scale),
22 x_offset_(x_offset),
23 y_offset_(y_offset),
24 xy_swap_(xy_swap),
25 clipped_(clipped),
26 clip_box_(std::move(clip_box)) {}
27
29
31 return Transformation(!xy_swap_, y_scale_, x_scale_, y_offset_, x_offset_,
32 clipped_, clip_box_.swapXY());
33}
34
36 return Transformation(xy_swap_, -x_scale_, y_scale_, -x_offset_, y_offset_,
37 clipped_, clip_box_.flipX());
38}
39
41 return Transformation(xy_swap_, x_scale_, -y_scale_, x_offset_, -y_offset_,
42 clipped_, clip_box_.flipY());
43}
44
46 return Transformation(xy_swap_, x_scale_ * x_scale, y_scale_ * y_scale,
47 x_offset_ * x_scale, y_offset_ * y_scale, clipped_,
48 clip_box_.scale(x_scale, y_scale));
49}
50
52 int16_t y_offset) const {
53 return Transformation(xy_swap_, x_scale_, y_scale_, x_offset_ + x_offset,
54 y_offset_ + y_offset, clipped_,
55 clip_box_.translate(x_offset, y_offset));
56}
57
59 return Transformation(
60 xy_swap_, x_scale_, y_scale_, x_offset_, y_offset_, true,
61 clipped_ ? Box::Intersect(clip_box_, clip_box) : clip_box);
62}
63
65 return Transformation(xy_swap_, -x_scale_, -y_scale_, -x_offset_, -y_offset_,
66 clipped_, clip_box_.rotateUpsideDown());
67}
68
70 return Transformation(!xy_swap_, -y_scale_, x_scale_, -y_offset_, x_offset_,
71 clipped_, clip_box_.rotateRight());
72}
73
75 return Transformation(!xy_swap_, y_scale_, -x_scale_, y_offset_, -x_offset_,
76 clipped_, clip_box_.rotateLeft());
77}
78
80 switch (turns & 3) {
81 case 0:
82 return *this;
83 case 1:
84 return rotateRight();
85 case 2:
86 return rotateUpsideDown();
87 case 3:
88 default:
89 return rotateLeft();
90 }
91}
92
94 switch (turns & 3) {
95 case 0:
96 return *this;
97 case 1:
98 return rotateLeft();
99 case 2:
100 return rotateUpsideDown();
101 case 3:
102 default:
103 return rotateRight();
104 }
105}
106
108 int16_t &y1) const {
109 x0 = x0 * x_scale_ + x_offset_;
110 y0 = y0 * y_scale_ + y_offset_;
111 x1 = (x1 + 1) * x_scale_ + x_offset_;
112 y1 = (y1 + 1) * y_scale_ + y_offset_;
113 if (x1 > x0) {
114 --x1;
115 } else { // Must be x1 < x0.
116 std::swap(x0, x1);
117 ++x0;
118 }
119 if (y1 > y0) {
120 --y1;
121 } else { // Must be y1 < y0.
122 std::swap(y0, y1);
123 ++y0;
124 }
125}
126
128 int16_t x0 = in.xMin();
129 int16_t y0 = in.yMin();
130 int16_t x1 = in.xMax();
131 int16_t y1 = in.yMax();
132 if (xy_swap_) {
133 std::swap(x0, y0);
134 std::swap(x1, y1);
135 }
136 transformRectNoSwap(x0, y0, x1, y1);
137 return Box(x0, y0, x1, y1);
138}
139
140int floor_div(int a, int b) {
141 // https://stackoverflow.com/questions/3602827/what-is-the-behavior-of-integer-division
142 int d = a / b;
143 int r = a % b; /* optimizes into single division. */
144 return r ? (d - ((a < 0) ^ (b < 0))) : d;
145}
146
148 int16_t x0 = rect.xMin() - x_offset_;
149 int16_t y0 = rect.yMin() - y_offset_;
150 int16_t x1 = rect.xMax() - x_offset_;
151 int16_t y1 = rect.yMax() - y_offset_;
152 if (x_scale_ < 0) {
153 std::swap(x0, x1);
154 }
155 x0 = floor_div(x0, x_scale_);
156 x1 = floor_div(x1, x_scale_);
157 if (y_scale_ < 0) {
158 std::swap(y0, y1);
159 }
160 y0 = floor_div(y0, y_scale_);
161 y1 = floor_div(y1, y_scale_);
162 if (xy_swap_) {
163 std::swap(x0, y0);
164 std::swap(x1, y1);
165 }
166 return Box(x0, y0, x1, y1);
167}
168
170 return clipped_ ? smallestEnclosingRect(clip_box_) : Box::MaximumBox();
171}
172
174 uint16_t y1, BlendingMode mode) {
175 if (!transformation_.is_rescaled() && !transformation_.xy_swap()) {
176 delegate_.setAddress(
177 x0 + transformation_.x_offset(), y0 + transformation_.y_offset(),
178 x1 + transformation_.x_offset(), y1 + transformation_.y_offset(), mode);
179 } else {
180 addr_window_ = Box(x0, y0, x1, y1);
181 blending_mode_ = mode;
182 x_cursor_ = x0;
183 y_cursor_ = y0;
184 }
185}
186
188 if (!transformation_.is_rescaled() && !transformation_.xy_swap()) {
189 delegate_.write(color, pixel_count);
190 } else if (!transformation_.is_abs_rescaled()) {
191 ClippingBufferedPixelWriter writer(delegate_, clip_box_, blending_mode_);
192 while (pixel_count-- > 0) {
193 int16_t x = x_cursor_;
194 int16_t y = y_cursor_;
195 if (transformation_.xy_swap()) {
196 std::swap(x, y);
197 }
198 writer.writePixel(
199 x * transformation_.x_scale() + transformation_.x_offset(),
200 y * transformation_.y_scale() + transformation_.y_offset(), *color++);
201 if (x_cursor_ < addr_window_.xMax()) {
202 ++x_cursor_;
203 } else {
204 x_cursor_ = addr_window_.xMin();
205 ++y_cursor_;
206 }
207 }
208 } else {
209 ClippingBufferedRectWriter writer(delegate_, clip_box_, blending_mode_);
210 while (pixel_count-- > 0) {
211 int16_t x0 = x_cursor_;
212 int16_t y0 = y_cursor_;
213 if (transformation_.xy_swap()) {
214 std::swap(x0, y0);
215 }
216 int16_t x1 = x0;
217 int16_t y1 = y0;
218 transformation_.transformRectNoSwap(x0, y0, x1, y1);
219 writer.writeRect(x0, y0, x1, y1, *color++);
220 if (x_cursor_ < addr_window_.xMax()) {
221 ++x_cursor_;
222 } else {
223 x_cursor_ = addr_window_.xMin();
224 ++y_cursor_;
225 }
226 }
227 }
228}
229
231 if (!transformation_.is_rescaled() && !transformation_.xy_swap()) {
232 delegate_.fill(color, pixel_count);
233 } else if (!transformation_.is_abs_rescaled()) {
234 ClippingBufferedPixelFiller filler(delegate_, color, clip_box_,
235 blending_mode_);
236 while (pixel_count-- > 0) {
237 int16_t x = x_cursor_;
238 int16_t y = y_cursor_;
239 if (transformation_.xy_swap()) {
240 std::swap(x, y);
241 }
242 filler.fillPixel(
243 x * transformation_.x_scale() + transformation_.x_offset(),
244 y * transformation_.y_scale() + transformation_.y_offset());
245 if (x_cursor_ < addr_window_.xMax()) {
246 ++x_cursor_;
247 } else {
248 x_cursor_ = addr_window_.xMin();
249 ++y_cursor_;
250 }
251 }
252 } else {
253 ClippingBufferedRectFiller filler(delegate_, color, clip_box_,
254 blending_mode_);
255 while (pixel_count-- > 0) {
256 int16_t x0 = x_cursor_;
257 int16_t y0 = y_cursor_;
258 if (transformation_.xy_swap()) {
259 std::swap(x0, y0);
260 }
261 int16_t x1 = x0;
262 int16_t y1 = y0;
263 transformation_.transformRectNoSwap(x0, y0, x1, y1);
264 filler.fillRect(x0, y0, x1, y1);
265 if (x_cursor_ < addr_window_.xMax()) {
266 ++x_cursor_;
267 } else {
268 x_cursor_ = addr_window_.xMin();
269 ++y_cursor_;
270 }
271 }
272 }
273}
274
276 int16_t *x, int16_t *y,
278 if (transformation_.xy_swap()) {
279 std::swap(x, y);
280 }
281 if (!transformation_.is_rescaled()) {
282 if (!transformation_.is_translated()) {
283 delegate_.writePixels(mode, color, x, y, pixel_count);
284 } else {
285 ClippingBufferedPixelWriter writer(delegate_, clip_box_, mode);
286 int16_t dx = transformation_.x_offset();
287 int16_t dy = transformation_.y_offset();
288 while (pixel_count-- > 0) {
289 int16_t ix = *x++ + dx;
290 int16_t iy = *y++ + dy;
291 writer.writePixel(ix, iy, *color++);
292 }
293 }
294 } else if (!transformation_.is_abs_rescaled()) {
295 ClippingBufferedPixelWriter writer(delegate_, clip_box_, mode);
296 while (pixel_count-- > 0) {
297 int16_t ix =
298 transformation_.x_scale() * *x++ + transformation_.x_offset();
299 int16_t iy =
300 transformation_.y_scale() * *y++ + transformation_.y_offset();
301 writer.writePixel(ix, iy, *color++);
302 }
303 } else {
304 ClippingBufferedRectWriter writer(delegate_, clip_box_, mode);
305 while (pixel_count-- > 0) {
306 int16_t x0 = *x++;
307 int16_t y0 = *y++;
308 int16_t x1 = x0;
309 int16_t y1 = y0;
310 transformation_.transformRectNoSwap(x0, y0, x1, y1);
311 writer.writeRect(x0, y0, x1, y1, *color++);
312 }
313 }
314}
315
317 int16_t *x, int16_t *y,
319 if (transformation_.xy_swap()) {
320 std::swap(x, y);
321 }
322 if (!transformation_.is_rescaled()) {
323 if (!transformation_.is_translated()) {
324 delegate_.fillPixels(mode, color, x, y, pixel_count);
325 } else {
326 ClippingBufferedPixelFiller filler(delegate_, color, clip_box_, mode);
327 while (pixel_count-- > 0) {
328 int16_t ix = *x++ + transformation_.x_offset();
329 int16_t iy = *y++ + transformation_.y_offset();
330 filler.fillPixel(ix, iy);
331 }
332 }
333 } else if (!transformation_.is_abs_rescaled()) {
334 ClippingBufferedPixelFiller filler(delegate_, color, clip_box_, mode);
335 while (pixel_count-- > 0) {
336 int16_t ix =
337 transformation_.x_scale() * *x++ + transformation_.x_offset();
338 int16_t iy =
339 transformation_.y_scale() * *y++ + transformation_.y_offset();
340 filler.fillPixel(ix, iy);
341 }
342 } else {
343 ClippingBufferedRectFiller filler(delegate_, color, clip_box_, mode);
344 while (pixel_count-- > 0) {
345 int16_t x0 = *x++;
346 int16_t y0 = *y++;
347 int16_t x1 = x0;
348 int16_t y1 = y0;
349 transformation_.transformRectNoSwap(x0, y0, x1, y1);
350 filler.fillRect(x0, y0, x1, y1);
351 }
352 }
353}
354
356 int16_t *x0, int16_t *y0, int16_t *x1,
357 int16_t *y1, uint16_t count) {
358 ClippingBufferedRectWriter writer(delegate_, clip_box_, mode);
359 if (transformation_.xy_swap()) {
360 std::swap(x0, y0);
361 std::swap(x1, y1);
362 }
363 while (count-- > 0) {
364 int16_t xMin = *x0++;
365 int16_t yMin = *y0++;
366 int16_t xMax = *x1++;
367 int16_t yMax = *y1++;
368 transformation_.transformRectNoSwap(xMin, yMin, xMax, yMax);
369 writer.writeRect(xMin, yMin, xMax, yMax, *color++);
370 }
371}
372
374 int16_t *x0, int16_t *y0, int16_t *x1,
375 int16_t *y1, uint16_t count) {
376 ClippingBufferedRectFiller filler(delegate_, color, clip_box_, mode);
377 if (transformation_.xy_swap()) {
378 std::swap(x0, y0);
379 std::swap(x1, y1);
380 }
381 while (count-- > 0) {
382 int16_t xMin = *x0++;
383 int16_t yMin = *y0++;
384 int16_t xMax = *x1++;
385 int16_t yMax = *y1++;
386 transformation_.transformRectNoSwap(xMin, yMin, xMax, yMax);
387 filler.fillRect(xMin, yMin, xMax, yMax);
388 }
389}
390
391} // namespace roo_display
BufferedRectWriter & writer
Axis-aligned integer rectangle.
Definition box.h:12
Box rotateRight() const
Return a copy rotated 90 degrees clockwise around the origin.
Definition box.h:151
Box rotateUpsideDown() const
Return a copy rotated 180 degrees around the origin.
Definition box.h:148
int16_t xMin() const
Minimum x (inclusive).
Definition box.h:65
int16_t xMax() const
Maximum x (inclusive).
Definition box.h:71
static Box MaximumBox()
Return a large sentinel box used for unbounded extents.
Definition box.h:59
Box scale(int16_t x_scale, int16_t y_scale) const
Return a scaled copy of this box.
Definition box.h:133
Box translate(int16_t x_offset, int16_t y_offset) const
Return a translated copy of this box.
Definition box.h:127
Box flipX() const
Return a copy mirrored across the Y-axis.
Definition box.h:142
Box swapXY() const
Return a copy with x and y axes swapped.
Definition box.h:139
Box rotateLeft() const
Return a copy rotated 90 degrees counter-clockwise around the origin.
Definition box.h:154
static Box Intersect(const Box &a, const Box &b)
Return the intersection of two boxes (may be empty).
Definition box.h:25
Box flipY() const
Return a copy mirrored across the X-axis.
Definition box.h:145
Buffered pixel filler with a clipping box.
Buffered pixel writer with a clipping box.
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
virtual void write(Color *color, uint32_t pixel_count)=0
Write pixels into the current address window.
virtual void fill(Color color, uint32_t pixel_count)
Write pixel_count copies of the same color into the current address window.
Definition device.cpp:7
virtual void fillPixels(BlendingMode blending_mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels using the same color. Invalidates the address window.
virtual void writePixels(BlendingMode blending_mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels (per-pixel colors). Invalidates the address window.
void setAddress(const Box &bounds, BlendingMode blending_mode)
Convenience overload for setAddress() using a Box.
Definition device.h:45
Geometric transformation: swap, scale, translate, rotate, clip.
Box smallestBoundingRect() const
Smallest bounding rect covering the clip box in destination coordinates.
Transformation translate(int16_t x_offset, int16_t y_offset) const
Translate by offsets.
Transformation scale(int16_t x_scale, int16_t y_scale) const
Scale along both axes.
int16_t y_offset() const
Y translation.
int16_t x_offset() const
X translation.
Transformation rotateUpsideDown() const
Rotate 180 degrees.
Transformation rotateCounterClockwise(int turns) const
Rotate counter-clockwise by turns (multiples of 90 degrees).
void transformRectNoSwap(int16_t &x0, int16_t &y0, int16_t &x1, int16_t &y1) const
Apply transformation to a rectangle without swapping input coordinates.
Transformation flipY() const
Flip across the X axis.
Box transformBox(Box in) const
Apply the transformation to a box.
bool is_abs_rescaled() const
Returns true if scale differs from 1 or -1 on any axis.
int16_t x_scale() const
X scale factor.
bool xy_swap() const
Whether x/y axes are swapped.
Transformation rotateRight() const
Rotate 90 degrees clockwise.
Transformation()
Identity transformation.
Box clip_box() const
Effective clip box.
Transformation swapXY() const
Swap x/y axes.
Box smallestEnclosingRect(const Box &rect) const
Smallest rectangle in destination coordinates enclosing rect.
Transformation rotateLeft() const
Rotate 90 degrees counter-clockwise.
Transformation flipX() const
Flip across the Y axis.
bool is_translated() const
Returns true if translation is non-zero.
int16_t y_scale() const
Y scale factor.
Transformation rotateClockwise(int turns) const
Rotate clockwise by turns (multiples of 90 degrees).
bool is_rescaled() const
Returns true if scale differs from 1 on any axis.
Transformation clip(Box clip_box) const
Intersect with a clip box.
void fillRects(BlendingMode mode, Color color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t count) override
Draw the specified rectangles using the same color. Invalidates the address window.
void setAddress(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, BlendingMode mode) override
Set a rectangular window filled by subsequent calls to write().
void writeRects(BlendingMode mode, Color *color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t pixel_count) override
Draw the specified rectangles (per-rectangle colors). Invalidates the address window.
void fill(Color color, uint32_t pixel_count) override
Write pixel_count copies of the same color into the current address window.
void fillPixels(BlendingMode mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels using the same color. Invalidates the address window.
void writePixels(BlendingMode mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels (per-pixel colors). Invalidates the address window.
void write(Color *color, uint32_t pixel_count) override
Write pixels into the current address window.
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
Definition blending.h:17
int floor_div(int a, int b)
float r
Definition smooth.cpp:474