FabGL
ESP32 Display Controller and Graphics Library
canvas.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#include <stdarg.h>
28
29#include "canvas.h"
30#include "fabfonts.h"
31
32
33#pragma GCC optimize ("O2")
34
35
36namespace fabgl {
37
38
39#define INVALIDRECT Rect(-32768, -32768, -32768, -32768)
40
41
42Canvas::Canvas(BitmappedDisplayController * displayController)
43 : m_displayController(displayController),
44 m_fontInfo(nullptr),
45 m_textHorizRate(1),
46 m_origin(Point(0, 0)),
47 m_clippingRect(INVALIDRECT)
48{
49}
50
51
52void Canvas::setOrigin(int X, int Y)
53{
54 setOrigin(Point(X, Y));
55}
56
57
58void Canvas::setOrigin(Point const & origin)
59{
60 Primitive p;
61 p.cmd = PrimitiveCmd::SetOrigin;
62 p.position = m_origin = origin;
63 m_displayController->addPrimitive(p);
64}
65
66
67void Canvas::setClippingRect(Rect const & rect)
68{
69 Primitive p;
70 p.cmd = PrimitiveCmd::SetClippingRect;
71 p.rect = m_clippingRect = rect;
72 m_displayController->addPrimitive(p);
73}
74
75
76Rect Canvas::getClippingRect()
77{
78 if (m_clippingRect == INVALIDRECT)
79 m_clippingRect = Rect(0, 0, getWidth() - 1, getHeight() - 1);
80 return m_clippingRect;
81}
82
83
84void Canvas::waitCompletion(bool waitVSync)
85{
86 if (waitVSync)
87 m_displayController->primitivesExecutionWait(); // wait on VSync normal processing
88 else
89 m_displayController->processPrimitives(); // process right now!
90}
91
92
93// Warning: beginUpdate() disables vertical sync interrupts. This means that
94// the BitmappedDisplayController primitives queue is not processed, and adding primitives may
95// cause a deadlock. To avoid this a call to "Canvas.waitCompletion(false)"
96// should be performed very often.
97void Canvas::beginUpdate()
98{
99 m_displayController->suspendBackgroundPrimitiveExecution();
100}
101
102
103void Canvas::endUpdate()
104{
105 m_displayController->resumeBackgroundPrimitiveExecution();
106}
107
108
109void Canvas::clear()
110{
111 Primitive p;
112 p.cmd = PrimitiveCmd::Clear;
113 p.ivalue = 0;
114 m_displayController->addPrimitive(p);
115}
116
117
118void Canvas::reset()
119{
120 Primitive p;
121 p.cmd = PrimitiveCmd::Reset;
122 m_displayController->addPrimitive(p);
123 m_origin = Point(0, 0);
124 m_clippingRect = INVALIDRECT;
125 m_textHorizRate = 1;
126}
127
128
129void Canvas::scroll(int offsetX, int offsetY)
130{
131 Primitive p;
132 if (offsetY != 0) {
133 p.cmd = PrimitiveCmd::VScroll;
134 p.ivalue = offsetY;
135 m_displayController->addPrimitive(p);
136 }
137 if (offsetX != 0) {
138 p.cmd = PrimitiveCmd::HScroll;
139 p.ivalue = offsetX;
140 m_displayController->addPrimitive(p);
141 }
142}
143
144
145void Canvas::setScrollingRegion(int X1, int Y1, int X2, int Y2)
146{
147 Primitive p;
148 p.cmd = PrimitiveCmd::SetScrollingRegion;
149 p.rect = Rect(X1, Y1, X2, Y2);
150 m_displayController->addPrimitive(p);
151}
152
153
154void Canvas::setPixel(int X, int Y)
155{
156 Primitive p;
157 p.cmd = PrimitiveCmd::SetPixel;
158 p.position = Point(X, Y);
159 m_displayController->addPrimitive(p);
160}
161
162
163void Canvas::setPixel(int X, int Y, RGB888 const & color)
164{
165 setPixel(Point(X, Y), color);
166}
167
168
169void Canvas::setPixel(Point const & pos, RGB888 const & color)
170{
171 Primitive p;
172 p.cmd = PrimitiveCmd::SetPixelAt;
173 p.pixelDesc = { pos, color };
174 m_displayController->addPrimitive(p);
175}
176
177
178void Canvas::moveTo(int X, int Y)
179{
180 Primitive p;
181 p.cmd = PrimitiveCmd::MoveTo;
182 p.position = Point(X, Y);
183 m_displayController->addPrimitive(p);
184}
185
186
187void Canvas::setPenColor(Color color)
188{
189 setPenColor(RGB888(color));
190}
191
192
193void Canvas::setPenColor(uint8_t red, uint8_t green, uint8_t blue)
194{
195 setPenColor(RGB888(red, green, blue));
196}
197
198
199void Canvas::setPenColor(RGB888 const & color)
200{
201 Primitive p;
202 p.cmd = PrimitiveCmd::SetPenColor;
203 p.color = color;
204 m_displayController->addPrimitive(p);
205}
206
207
208void Canvas::setBrushColor(Color color)
209{
210 setBrushColor(RGB888(color));
211}
212
213
214void Canvas::setBrushColor(uint8_t red, uint8_t green, uint8_t blue)
215{
216 setBrushColor(RGB888(red, green, blue));
217}
218
219
220void Canvas::setPenWidth(int value)
221{
222 Primitive p;
223 p.cmd = PrimitiveCmd::SetPenWidth;
224 p.ivalue = value;
225 m_displayController->addPrimitive(p);
226}
227
228
229void Canvas::setLineEnds(LineEnds value)
230{
231 Primitive p;
232 p.cmd = PrimitiveCmd::SetLineEnds;
233 p.lineEnds = value;
234 m_displayController->addPrimitive(p);
235}
236
237
238void Canvas::setBrushColor(RGB888 const & color)
239{
240 Primitive p;
241 p.cmd = PrimitiveCmd::SetBrushColor;
242 p.color = color;
243 m_displayController->addPrimitive(p);
244}
245
246
247void Canvas::lineTo(int X, int Y)
248{
249 Primitive p;
250 p.cmd = PrimitiveCmd::LineTo;
251 p.position = Point(X, Y);
252 m_displayController->addPrimitive(p);
253}
254
255
256void Canvas::drawLine(int X1, int Y1, int X2, int Y2)
257{
258 moveTo(X1, Y1);
259 lineTo(X2, Y2);
260}
261
262
263void Canvas::drawRectangle(int X1, int Y1, int X2, int Y2)
264{
265 Primitive p;
266 p.cmd = PrimitiveCmd::DrawRect;
267 p.rect = Rect(X1, Y1, X2, Y2);
268 m_displayController->addPrimitive(p);
269}
270
271
272void Canvas::drawRectangle(Rect const & rect)
273{
274 drawRectangle(rect.X1, rect.Y1, rect.X2, rect.Y2);
275}
276
277
278void Canvas::fillRectangle(int X1, int Y1, int X2, int Y2)
279{
280 Primitive p;
281 p.cmd = PrimitiveCmd::FillRect;
282 p.rect = Rect(X1, Y1, X2, Y2);
283 m_displayController->addPrimitive(p);
284}
285
286
287void Canvas::fillRectangle(Rect const & rect)
288{
289 Primitive p;
290 p.cmd = PrimitiveCmd::FillRect;
291 p.rect = rect;
292 m_displayController->addPrimitive(p);
293}
294
295
296void Canvas::invertRectangle(int X1, int Y1, int X2, int Y2)
297{
298 invertRectangle(Rect(X1, Y1, X2, Y2));
299}
300
301
302void Canvas::invertRectangle(Rect const & rect)
303{
304 Primitive p;
305 p.cmd = PrimitiveCmd::InvertRect;
306 p.rect = rect;
307 m_displayController->addPrimitive(p);
308}
309
310
311void Canvas::swapRectangle(int X1, int Y1, int X2, int Y2)
312{
313 Primitive p;
314 p.cmd = PrimitiveCmd::SwapFGBG;
315 p.rect = Rect(X1, Y1, X2, Y2);
316 m_displayController->addPrimitive(p);
317}
318
319
320void Canvas::fillEllipse(int X, int Y, int width, int height)
321{
322 moveTo(X, Y);
323 Primitive p;
324 p.cmd = PrimitiveCmd::FillEllipse;
325 p.size = Size(width, height);
326 m_displayController->addPrimitive(p);
327}
328
329
330void Canvas::drawEllipse(int X, int Y, int width, int height)
331{
332 moveTo(X, Y);
333 Primitive p;
334 p.cmd = PrimitiveCmd::DrawEllipse;
335 p.size = Size(width, height);
336 m_displayController->addPrimitive(p);
337}
338
339
340void Canvas::drawGlyph(int X, int Y, int width, int height, uint8_t const * data, int index)
341{
342 Primitive p;
343 p.cmd = PrimitiveCmd::DrawGlyph;
344 p.glyph = Glyph(X, Y, width, height, data + index * height * ((width + 7) / 8));
345 m_displayController->addPrimitive(p);
346}
347
348
349void Canvas::renderGlyphsBuffer(int itemX, int itemY, GlyphsBuffer const * glyphsBuffer)
350{
351 Primitive p;
352 p.cmd = PrimitiveCmd::RenderGlyphsBuffer;
353 p.glyphsBufferRenderInfo = GlyphsBufferRenderInfo(itemX, itemY, glyphsBuffer);
354 m_displayController->addPrimitive(p);
355}
356
357
358void Canvas::setGlyphOptions(GlyphOptions options)
359{
360 Primitive p;
361 p.cmd = PrimitiveCmd::SetGlyphOptions;
362 p.glyphOptions = options;
363 m_displayController->addPrimitive(p);
364 m_textHorizRate = options.doubleWidth > 0 ? 2 : 1;
365}
366
367
368void Canvas::resetGlyphOptions()
369{
370 setGlyphOptions(GlyphOptions());
371}
372
373
374void Canvas::setPaintOptions(PaintOptions options)
375{
376 Primitive p;
377 p.cmd = PrimitiveCmd::SetPaintOptions;
378 p.paintOptions = options;
379 m_displayController->addPrimitive(p);
380}
381
382
383void Canvas::resetPaintOptions()
384{
385 setPaintOptions(PaintOptions());
386}
387
388
389void Canvas::selectFont(FontInfo const * fontInfo)
390{
391 m_fontInfo = fontInfo;
392}
393
394
395void Canvas::drawChar(int X, int Y, char c)
396{
397 drawGlyph(X, Y, m_fontInfo->width, m_fontInfo->height, m_fontInfo->data, c);
398}
399
400
401void Canvas::drawText(int X, int Y, char const * text, bool wrap)
402{
403 if (m_fontInfo == nullptr)
404 selectFont(&FONT_8x8);
405 drawText(m_fontInfo, X, Y, text, wrap);
406}
407
408
409void Canvas::drawText(FontInfo const * fontInfo, int X, int Y, char const * text, bool wrap)
410{
411 int fontWidth = fontInfo->width;
412 for (; *text; ++text, X += fontWidth * m_textHorizRate) {
413 if (wrap && X >= getWidth()) { // TODO: clipX2 instead of getWidth()?
414 X = 0;
415 Y += fontInfo->height;
416 }
417 if (fontInfo->chptr) {
418 // variable width
419 uint8_t const * chptr = fontInfo->data + fontInfo->chptr[(int)(*text)];
420 fontWidth = *chptr++;
421 drawGlyph(X, Y, fontWidth, fontInfo->height, chptr, 0);
422 } else {
423 // fixed width
424 drawGlyph(X, Y, fontInfo->width, fontInfo->height, fontInfo->data, *text);
425 }
426 }
427}
428
429
430void Canvas::drawTextWithEllipsis(FontInfo const * fontInfo, int X, int Y, char const * text, int maxX)
431{
432 int fontWidth = fontInfo->width;
433 int fontHeight = fontInfo->height;
434 for (; *text; ++text, X += fontWidth) {
435 if (X >= maxX - fontHeight) {
436 // draw ellipsis and exit
437 drawText(fontInfo, X, Y, "...");
438 break;
439 }
440 if (fontInfo->chptr) {
441 // variable width
442 uint8_t const * chptr = fontInfo->data + fontInfo->chptr[(int)(*text)];
443 fontWidth = *chptr++;
444 drawGlyph(X, Y, fontWidth, fontInfo->height, chptr, 0);
445 } else {
446 // fixed width
447 drawGlyph(X, Y, fontInfo->width, fontInfo->height, fontInfo->data, *text);
448 }
449 }
450}
451
452
453int Canvas::textExtent(FontInfo const * fontInfo, char const * text)
454{
455 int fontWidth = fontInfo->width;
456 int extent = 0;
457 for (; *text; ++text, extent += fontWidth) {
458 if (fontInfo->chptr) {
459 // variable width
460 uint8_t const * chptr = fontInfo->data + fontInfo->chptr[(int)(*text)];
461 fontWidth = *chptr;
462 }
463 }
464 return extent;
465}
466
467
468int Canvas::textExtent(char const * text)
469{
470 return textExtent(m_fontInfo, text);
471}
472
473
474void Canvas::drawTextFmt(int X, int Y, const char *format, ...)
475{
476 va_list ap;
477 va_start(ap, format);
478 int size = vsnprintf(nullptr, 0, format, ap) + 1;
479 if (size > 0) {
480 va_end(ap);
481 va_start(ap, format);
482 char buf[size + 1];
483 vsnprintf(buf, size, format, ap);
484 drawText(X, Y, buf, false);
485 }
486 va_end(ap);
487}
488
489
490void Canvas::copyRect(int sourceX, int sourceY, int destX, int destY, int width, int height)
491{
492 moveTo(destX, destY);
493 int sourceX2 = sourceX + width - 1;
494 int sourceY2 = sourceY + height - 1;
495 Primitive p;
496 p.cmd = PrimitiveCmd::CopyRect;
497 p.rect = Rect(sourceX, sourceY, sourceX2, sourceY2);
498 m_displayController->addPrimitive(p);
499}
500
501
502void Canvas::drawBitmap(int X, int Y, Bitmap const * bitmap)
503{
504 Primitive p;
505 p.cmd = PrimitiveCmd::DrawBitmap;
506 p.bitmapDrawingInfo = BitmapDrawingInfo(X, Y, bitmap);
507 m_displayController->addPrimitive(p);
508}
509
510
511void Canvas::swapBuffers()
512{
513 Primitive p;
514 p.cmd = PrimitiveCmd::SwapBuffers;
515 p.notifyTask = xTaskGetCurrentTaskHandle();
516 m_displayController->addPrimitive(p);
517 m_displayController->primitivesExecutionWait();
518}
519
520
521void Canvas::drawPath(Point const * points, int pointsCount)
522{
523 Primitive p;
524 p.cmd = PrimitiveCmd::DrawPath;
525 p.path.points = points;
526 p.path.pointsCount = pointsCount;
527 p.path.freePoints = false;
528 m_displayController->addPrimitive(p);
529}
530
531
532void Canvas::fillPath(Point const * points, int pointsCount)
533{
534 Primitive p;
535 p.cmd = PrimitiveCmd::FillPath;
536 p.path.points = points;
537 p.path.pointsCount = pointsCount;
538 p.path.freePoints = false;
539 m_displayController->addPrimitive(p);
540}
541
542
543RGB888 Canvas::getPixel(int X, int Y)
544{
545 RGB888 rgb;
546 m_displayController->readScreen(Rect(X, Y, X, Y), &rgb);
547 return rgb;
548}
549
550
551} // end of namespace
This file contains fabgl::Canvas definition.
uint8_t width
uint8_t const * data
int16_t X
int16_t Y
uint8_t height
int16_t X1
Definition: fabutils.h:0
int16_t Y2
Definition: fabutils.h:3
int16_t X2
Definition: fabutils.h:2
int16_t Y1
Definition: fabutils.h:1
LineEnds
This enum defines line ends when pen width is greater than 1.
Color
This enum defines named colors.
Represents an image.
Represents a glyph position, size and binary data.
Specifies general paint options.
Represents the coordinate of a point.
Definition: fabutils.h:213
Represents a 24 bit RGB color.
Represents a rectangle.
Definition: fabutils.h:248
Represents a bidimensional size.
Definition: fabutils.h:231
Specifies various glyph painting options.