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-2021 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 
36 namespace fabgl {
37 
38 
39 #define INVALIDRECT Rect(-32768, -32768, -32768, -32768)
40 
41 
42 Canvas::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 
52 void Canvas::setOrigin(int X, int Y)
53 {
54  setOrigin(Point(X, Y));
55 }
56 
57 
58 void 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 
67 void 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 
76 Rect 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 
84 void 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.
97 void Canvas::beginUpdate()
98 {
99  m_displayController->suspendBackgroundPrimitiveExecution();
100 }
101 
102 
103 void Canvas::endUpdate()
104 {
105  m_displayController->resumeBackgroundPrimitiveExecution();
106 }
107 
108 
109 void Canvas::clear()
110 {
111  Primitive p;
112  p.cmd = PrimitiveCmd::Clear;
113  p.ivalue = 0;
114  m_displayController->addPrimitive(p);
115 }
116 
117 
118 void 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 
129 void 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 
145 void 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 
154 void 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 
163 void Canvas::setPixel(int X, int Y, RGB888 const & color)
164 {
165  setPixel(Point(X, Y), color);
166 }
167 
168 
169 void 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 
178 void 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 
187 void Canvas::setPenColor(Color color)
188 {
189  setPenColor(RGB888(color));
190 }
191 
192 
193 void Canvas::setPenColor(uint8_t red, uint8_t green, uint8_t blue)
194 {
195  setPenColor(RGB888(red, green, blue));
196 }
197 
198 
199 void 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 
208 void Canvas::setBrushColor(Color color)
209 {
210  setBrushColor(RGB888(color));
211 }
212 
213 
214 void Canvas::setBrushColor(uint8_t red, uint8_t green, uint8_t blue)
215 {
216  setBrushColor(RGB888(red, green, blue));
217 }
218 
219 
220 void 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 
229 void 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 
238 void 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 
247 void 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 
256 void Canvas::drawLine(int X1, int Y1, int X2, int Y2)
257 {
258  moveTo(X1, Y1);
259  lineTo(X2, Y2);
260 }
261 
262 
263 void 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 
272 void Canvas::drawRectangle(Rect const & rect)
273 {
274  drawRectangle(rect.X1, rect.Y1, rect.X2, rect.Y2);
275 }
276 
277 
278 void 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 
287 void 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 
296 void Canvas::invertRectangle(int X1, int Y1, int X2, int Y2)
297 {
298  invertRectangle(Rect(X1, Y1, X2, Y2));
299 }
300 
301 
302 void 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 
311 void 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 
320 void 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 
330 void 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 
340 void 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 
349 void 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 
358 void 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 
368 void Canvas::resetGlyphOptions()
369 {
370  setGlyphOptions(GlyphOptions());
371 }
372 
373 
374 void 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 
383 void Canvas::resetPaintOptions()
384 {
385  setPaintOptions(PaintOptions());
386 }
387 
388 
389 void Canvas::selectFont(FontInfo const * fontInfo)
390 {
391  m_fontInfo = fontInfo;
392 }
393 
394 
395 void 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 
401 void 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 
409 void 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 
430 void 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 
453 int 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 
468 int Canvas::textExtent(char const * text)
469 {
470  return textExtent(m_fontInfo, text);
471 }
472 
473 
474 void 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 
490 void 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 
502 void 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 
511 void 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 
521 void 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 
532 void 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 
543 RGB888 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
int16_t X2
Definition: fabutils.h:180
Represents a 24 bit RGB color.
int16_t Y2
Definition: fabutils.h:181
int16_t Y1
Definition: fabutils.h:179
int16_t Y
uint8_t const * data
Color
This enum defines named colors.
This file contains fabgl::Canvas definition.
int16_t X1
Definition: fabutils.h:178
LineEnds
This enum defines line ends when pen width is greater than 1.
Represents a glyph position, size and binary data.
Represents the coordinate of a point.
Definition: fabutils.h:191
Represents an image.
Definition: canvas.cpp:36
Specifies various glyph painting options.
Represents a rectangle.
Definition: fabutils.h:226
int16_t X
Represents a bidimensional size.
Definition: fabutils.h:209
uint8_t height
Specifies general paint options.
uint8_t width