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