FabGL
ESP32 Display Controller and Graphics Library
displaycontroller.h
Go to the documentation of this file.
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 #pragma once
28 
29 
30 
39 #include <stdint.h>
40 #include <stddef.h>
41 
42 #include "freertos/FreeRTOS.h"
43 #include "freertos/queue.h"
44 #include "freertos/task.h"
45 
46 #include "fabglconf.h"
47 #include "fabutils.h"
48 
49 
50 
51 
52 namespace fabgl {
53 
54 
55 
56 /*
57  Notes:
58  - all positions can have negative and outofbound coordinates. Shapes are always clipped correctly.
59 */
60 enum PrimitiveCmd : uint8_t {
61 
62  // Needed to send the updated area of screen buffer on some displays (ie SSD1306)
63  Flush,
64 
65  // Refresh display. Some displays (ie SSD1306) aren't repainted if there aren't primitives
66  // so posting Refresh allows to resend the screenbuffer
67  // params: rect (rectangle to refresh)
68  Refresh,
69 
70  // Reset paint state
71  // params: none
72  Reset,
73 
74  // Set current pen color
75  // params: color
76  SetPenColor,
77 
78  // Set current brush color
79  // params: color
80  SetBrushColor,
81 
82  // Paint a pixel at specified coordinates, using current pen color
83  // params: color
84  SetPixel,
85 
86  // Paint a pixel at specified coordinates using the specified color
87  // params: pixelDesc
88  SetPixelAt,
89 
90  // Move current position to the specified one
91  // params: point
92  MoveTo,
93 
94  // Draw a line from current position to the specified one, using current pen color. Update current position.
95  // params: point
96  LineTo,
97 
98  // Fill a rectangle using current brush color
99  // params: rect
100  FillRect,
101 
102  // Draw a rectangle using current pen color
103  // params: rect
104  DrawRect,
105 
106  // Fill an ellipse, current position is the center, using current brush color
107  // params: size
108  FillEllipse,
109 
110  // Draw an ellipse, current position is the center, using current pen color
111  // params: size
112  DrawEllipse,
113 
114  // Fill viewport with brush color
115  // params: none
116  Clear,
117 
118  // Scroll vertically without copying buffers
119  // params: ivalue (scroll amount, can be negative)
120  VScroll,
121 
122  // Scroll horizontally (time consuming operation!)
123  // params: ivalue (scroll amount, can be negative)
124  HScroll,
125 
126  // Draw a glyph (BW image)
127  // params: glyph
128  DrawGlyph,
129 
130  // Set paint options
131  // params: glyphOptions
132  SetGlyphOptions,
133 
134  // Set gluph options
135  // params: paintOptions
136  SetPaintOptions,
137 
138  // Invert a rectangle
139  // params: rect
140  InvertRect,
141 
142  // Copy (overlapping) rectangle to current position
143  // params: rect (source rectangle)
144  CopyRect,
145 
146  // Set scrolling region
147  // params: rect
148  SetScrollingRegion,
149 
150  // Swap foreground (pen) and background (brush) colors of all pixels inside the specified rectangles. Other colors remain untaltered.
151  // params: rect
152  SwapFGBG,
153 
154  // Render glyphs buffer
155  // params: glyphsBufferRenderInfo
156  RenderGlyphsBuffer,
157 
158  // Draw a bitmap
159  // params: bitmapDrawingInfo
160  DrawBitmap,
161 
162  // Refresh sprites
163  // no params
164  RefreshSprites,
165 
166  // Swap buffers (m_doubleBuffered must be True)
167  // params: notifyTask
168  SwapBuffers,
169 
170  // Fill a path, using current brush color
171  // params: path
172  FillPath,
173 
174  // Draw a path, using current pen color
175  // params: path
176  DrawPath,
177 
178  // Set axis origin
179  // params: point
180  SetOrigin,
181 
182  // Set clipping rectangle
183  // params: rect
184  SetClippingRect,
185 
186  // Set pen width
187  // params: ivalue
188  SetPenWidth,
189 
190  // Set line ends
191  // params: lineEnds
192  SetLineEnds,
193 };
194 
195 
196 
202 enum Color {
204  Red,
219 };
220 
221 
222 
228 struct RGB888 {
229  uint8_t R;
230  uint8_t G;
231  uint8_t B;
233  RGB888() : R(0), G(0), B(0) { }
234  RGB888(Color color);
235  RGB888(uint8_t red, uint8_t green, uint8_t blue) : R(red), G(green), B(blue) { }
236 } __attribute__ ((packed));
237 
238 
239 inline bool operator==(RGB888 const& lhs, RGB888 const& rhs)
240 {
241  return lhs.R == rhs.R && lhs.G == rhs.G && lhs.B == rhs.B;
242 }
243 
244 
245 inline bool operator!=(RGB888 const& lhs, RGB888 const& rhs)
246 {
247  return lhs.R != rhs.R || lhs.G != rhs.G || lhs.B == rhs.B;
248 }
249 
250 
251 
257 struct RGBA8888 {
258  uint8_t R;
259  uint8_t G;
260  uint8_t B;
261  uint8_t A;
263  RGBA8888() : R(0), G(0), B(0), A(0) { }
264  RGBA8888(int red, int green, int blue, int alpha) : R(red), G(green), B(blue), A(alpha) { }
265 };
266 
267 
268 
275 struct RGB222 {
276  uint8_t R : 2;
277  uint8_t G : 2;
278  uint8_t B : 2;
280  RGB222() : R(0), G(0), B(0) { }
281  RGB222(uint8_t red, uint8_t green, uint8_t blue) : R(red), G(green), B(blue) { }
282  RGB222(RGB888 const & value);
283 
284  static bool lowBitOnly; // true= 8 colors, false 64 colors
285 };
286 
287 
288 inline bool operator==(RGB222 const& lhs, RGB222 const& rhs)
289 {
290  return lhs.R == rhs.R && lhs.G == rhs.G && lhs.B == rhs.B;
291 }
292 
293 
294 inline bool operator!=(RGB222 const& lhs, RGB222 const& rhs)
295 {
296  return lhs.R != rhs.R || lhs.G != rhs.G || lhs.B == rhs.B;
297 }
298 
299 
305 struct RGBA2222 {
306  uint8_t R : 2;
307  uint8_t G : 2;
308  uint8_t B : 2;
309  uint8_t A : 2;
311  RGBA2222(int red, int green, int blue, int alpha) : R(red), G(green), B(blue), A(alpha) { }
312 };
313 
314 
315 // 0 .. 63 => 0
316 // 64 .. 127 => 1
317 // 128 .. 191 => 2
318 // 192 .. 255 => 3
319 uint8_t RGB888toPackedRGB222(RGB888 const & rgb);
327 struct Glyph {
328  int16_t X;
329  int16_t Y;
330  uint8_t width;
331  uint8_t height;
332  uint8_t const * data;
334  Glyph() : X(0), Y(0), width(0), height(0), data(nullptr) { }
335  Glyph(int X_, int Y_, int width_, int height_, uint8_t const * data_) : X(X_), Y(Y_), width(width_), height(height_), data(data_) { }
336 } __attribute__ ((packed));
337 
339 
344  struct {
345  uint16_t fillBackground : 1;
346  uint16_t bold : 1;
347  uint16_t reduceLuminosity : 1;
348  uint16_t italic : 1;
349  uint16_t invert : 1;
350  uint16_t blank : 1;
351  uint16_t underline : 1;
352  uint16_t doubleWidth : 2;
353  uint16_t userOpt1 : 1;
354  uint16_t userOpt2 : 1;
355  };
356  uint16_t value;
357 
359  GlyphOptions & FillBackground(bool value) { fillBackground = value; return *this; }
360 
362  GlyphOptions & Bold(bool value) { bold = value; return *this; }
363 
365  GlyphOptions & Italic(bool value) { italic = value; return *this; }
366 
368  GlyphOptions & Underline(bool value) { underline = value; return *this; }
369 
371  GlyphOptions & DoubleWidth(uint8_t value) { doubleWidth = value; return *this; }
372 
374  GlyphOptions & Invert(uint8_t value) { invert = value; return *this; }
375 
377  GlyphOptions & Blank(uint8_t value) { blank = value; return *this; }
378 } __attribute__ ((packed));
379 
380 
381 
382 // GlyphsBuffer.map support functions
383 // 0 .. 7 : index
384 // 8 .. 11 : BG color (Color)
385 // 12 .. 15 : FG color (Color)
386 // 16 .. 31 : options (GlyphOptions)
387 // note: volatile pointer to avoid optimizer to get less than 32 bit from 32 bit access only memory
388 #define GLYPHMAP_INDEX_BIT 0
389 #define GLYPHMAP_BGCOLOR_BIT 8
390 #define GLYPHMAP_FGCOLOR_BIT 12
391 #define GLYPHMAP_OPTIONS_BIT 16
392 #define GLYPHMAP_ITEM_MAKE(index, bgColor, fgColor, options) (((uint32_t)(index) << GLYPHMAP_INDEX_BIT) | ((uint32_t)(bgColor) << GLYPHMAP_BGCOLOR_BIT) | ((uint32_t)(fgColor) << GLYPHMAP_FGCOLOR_BIT) | ((uint32_t)((options).value) << GLYPHMAP_OPTIONS_BIT))
393 
394 inline uint8_t glyphMapItem_getIndex(uint32_t const volatile * mapItem) { return *mapItem >> GLYPHMAP_INDEX_BIT & 0xFF; }
395 inline uint8_t glyphMapItem_getIndex(uint32_t const & mapItem) { return mapItem >> GLYPHMAP_INDEX_BIT & 0xFF; }
396 
397 inline Color glyphMapItem_getBGColor(uint32_t const volatile * mapItem) { return (Color)(*mapItem >> GLYPHMAP_BGCOLOR_BIT & 0x0F); }
398 inline Color glyphMapItem_getBGColor(uint32_t const & mapItem) { return (Color)(mapItem >> GLYPHMAP_BGCOLOR_BIT & 0x0F); }
399 
400 inline Color glyphMapItem_getFGColor(uint32_t const volatile * mapItem) { return (Color)(*mapItem >> GLYPHMAP_FGCOLOR_BIT & 0x0F); }
401 inline Color glyphMapItem_getFGColor(uint32_t const & mapItem) { return (Color)(mapItem >> GLYPHMAP_FGCOLOR_BIT & 0x0F); }
402 
403 inline GlyphOptions glyphMapItem_getOptions(uint32_t const volatile * mapItem) { return (GlyphOptions){.value = (uint16_t)(*mapItem >> GLYPHMAP_OPTIONS_BIT & 0xFFFF)}; }
404 inline GlyphOptions glyphMapItem_getOptions(uint32_t const & mapItem) { return (GlyphOptions){.value = (uint16_t)(mapItem >> GLYPHMAP_OPTIONS_BIT & 0xFFFF)}; }
405 
406 inline void glyphMapItem_setOptions(uint32_t volatile * mapItem, GlyphOptions const & options) { *mapItem = (*mapItem & ~((uint32_t)0xFFFF << GLYPHMAP_OPTIONS_BIT)) | ((uint32_t)(options.value) << GLYPHMAP_OPTIONS_BIT); }
408 struct GlyphsBuffer {
409  int16_t glyphsWidth;
410  int16_t glyphsHeight;
411  uint8_t const * glyphsData;
412  int16_t columns;
413  int16_t rows;
414  uint32_t * map; // look at glyphMapItem_... inlined functions
415 };
416 
417 
418 struct GlyphsBufferRenderInfo {
419  int16_t itemX; // starts from 0
420  int16_t itemY; // starts from 0
421  GlyphsBuffer const * glyphsBuffer;
422 
423  GlyphsBufferRenderInfo(int itemX_, int itemY_, GlyphsBuffer const * glyphsBuffer_) : itemX(itemX_), itemY(itemY_), glyphsBuffer(glyphsBuffer_) { }
424 } __attribute__ ((packed));
425 
426 
430 enum class NativePixelFormat : uint8_t {
431  Mono,
432  SBGR2222,
433  RGB565BE,
434  PALETTE2,
435  PALETTE4,
436  PALETTE8,
437  PALETTE16,
438 };
439 
440 
444 enum class PixelFormat : uint8_t {
445  Undefined,
446  Native,
447  Mask,
448  RGBA2222,
449  RGBA8888
450 };
451 
452 
456 enum class LineEnds : uint8_t {
457  None,
458  Circle,
459 };
460 
461 
465 struct Bitmap {
466  int16_t width;
467  int16_t height;
470  uint8_t * data;
473  Bitmap() : width(0), height(0), format(PixelFormat::Undefined), foregroundColor(RGB888(255, 255, 255)), data(nullptr), dataAllocated(false) { }
474  Bitmap(int width_, int height_, void const * data_, PixelFormat format_, bool copy = false);
475  Bitmap(int width_, int height_, void const * data_, PixelFormat format_, RGB888 foregroundColor_, bool copy = false);
476  ~Bitmap();
477 
478  void setPixel(int x, int y, int value); // use with PixelFormat::Mask. value can be 0 or not 0
479  void setPixel(int x, int y, RGBA2222 value); // use with PixelFormat::RGBA2222
480  void setPixel(int x, int y, RGBA8888 value); // use with PixelFormat::RGBA8888
481 
482  int getAlpha(int x, int y);
483 
484 private:
485  void allocate();
486  void copyFrom(void const * srcData);
487 };
488 
489 
490 struct BitmapDrawingInfo {
491  int16_t X;
492  int16_t Y;
493  Bitmap const * bitmap;
494 
495  BitmapDrawingInfo(int X_, int Y_, Bitmap const * bitmap_) : X(X_), Y(Y_), bitmap(bitmap_) { }
496 } __attribute__ ((packed));
497 
498 
502 enum CursorName : uint8_t {
522 };
523 
524 
528 struct Cursor {
529  int16_t hotspotX;
530  int16_t hotspotY;
532 };
533 
534 
535 struct QuadTreeObject;
536 
537 
546 struct Sprite {
547  volatile int16_t x;
548  volatile int16_t y;
549  Bitmap * * frames; // array of pointer to Bitmap
550  int16_t framesCount;
551  int16_t currentFrame;
552  int16_t savedX;
553  int16_t savedY;
554  int16_t savedBackgroundWidth;
555  int16_t savedBackgroundHeight;
556  uint8_t * savedBackground;
557  QuadTreeObject * collisionDetectorObject;
558  struct {
559  uint8_t visible: 1;
560  // A static sprite should be positioned before dynamic sprites.
561  // It is never re-rendered unless allowDraw is 1. Static sprites always sets allowDraw=0 after drawings.
562  uint8_t isStatic: 1;
563  // This is always '1' for dynamic sprites and always '0' for static sprites.
564  uint8_t allowDraw: 1;
565  };
566 
567  Sprite();
568  ~Sprite();
569  Bitmap * getFrame() { return frames ? frames[currentFrame] : nullptr; }
570  int getFrameIndex() { return currentFrame; }
571  void nextFrame() { ++currentFrame; if (currentFrame >= framesCount) currentFrame = 0; }
572  Sprite * setFrame(int frame) { currentFrame = frame; return this; }
573  Sprite * addBitmap(Bitmap * bitmap);
574  Sprite * addBitmap(Bitmap * bitmap[], int count);
575  void clearBitmaps();
576  int getWidth() { return frames[currentFrame]->width; }
577  int getHeight() { return frames[currentFrame]->height; }
578  Sprite * moveBy(int offsetX, int offsetY);
579  Sprite * moveBy(int offsetX, int offsetY, int wrapAroundWidth, int wrapAroundHeight);
580  Sprite * moveTo(int x, int y);
581 };
582 
583 
584 struct Path {
585  Point const * points;
586  int pointsCount;
587  bool freePoints; // deallocate points after drawing
588 } __attribute__ ((packed));
589 
590 
594 struct PaintOptions {
595  uint8_t swapFGBG : 1;
596  uint8_t NOT : 1;
598  PaintOptions() : swapFGBG(false), NOT(false) { }
599 } __attribute__ ((packed));
600 
601 
602 struct PixelDesc {
603  Point pos;
604  RGB888 color;
605 } __attribute__ ((packed));
606 
607 
608 struct Primitive {
609  PrimitiveCmd cmd;
610  union {
611  int16_t ivalue;
612  RGB888 color;
613  Point position;
614  Size size;
615  Glyph glyph;
616  Rect rect;
617  GlyphOptions glyphOptions;
618  PaintOptions paintOptions;
619  GlyphsBufferRenderInfo glyphsBufferRenderInfo;
620  BitmapDrawingInfo bitmapDrawingInfo;
621  Path path;
622  PixelDesc pixelDesc;
623  LineEnds lineEnds;
624  TaskHandle_t notifyTask;
625  } __attribute__ ((packed));
626 
627  Primitive() { }
628  Primitive(PrimitiveCmd cmd_) : cmd(cmd_) { }
629  Primitive(PrimitiveCmd cmd_, Rect const & rect_) : cmd(cmd_), rect(rect_) { }
630 } __attribute__ ((packed));
631 
632 
633 struct PaintState {
634  RGB888 penColor;
635  RGB888 brushColor;
636  Point position; // value already traslated to "origin"
637  GlyphOptions glyphOptions;
638  PaintOptions paintOptions;
639  Rect scrollingRegion;
640  Point origin;
641  Rect clippingRect; // relative clipping rectangle
642  Rect absClippingRect; // actual absolute clipping rectangle (calculated when setting "origin" or "clippingRect")
643  int16_t penWidth;
644  LineEnds lineEnds;
645 };
646 
647 
648 
653  Textual,
654  Bitmapped,
655 };
656 
657 
658 
663 
664 public:
665 
666  virtual void setResolution(char const * modeline, int viewPortWidth = -1, int viewPortHeight = -1, bool doubleBuffered = false) = 0;
667 
668  virtual void begin() = 0;
669 
676 
682  virtual int colorsCount() = 0;
683 
689  int getScreenWidth() { return m_screenWidth; }
690 
696  int getScreenHeight() { return m_screenHeight; }
697 
698 protected:
699 
700  // inherited classes should call setScreenSize once display size is known
701  void setScreenSize(int width, int height) { m_screenWidth = width; m_screenHeight = height; }
702 
703 private:
704 
705  // we store here these info to avoid to have virtual methods (due the -vtables in flash- problem)
706  int16_t m_screenWidth;
707  int16_t m_screenHeight;
708 };
709 
710 
711 
716 
717 public:
718 
720 
721  virtual int getColumns() = 0;
722  virtual int getRows() = 0;
723  virtual void adjustMapSize(int * columns, int * rows) = 0;
724  virtual void setTextMap(uint32_t const * map, int rows) = 0;
725  virtual void enableCursor(bool value) = 0;
726  virtual void setCursorPos(int row, int col) = 0; // row and col starts from 0
727  virtual void setCursorForeground(Color value) = 0;
728  virtual void setCursorBackground(Color value) = 0;
729  virtual FontInfo const * getFont() = 0;
730 };
731 
732 
733 
738 
739 public:
740 
742  virtual ~BitmappedDisplayController();
743 
745 
751  virtual int getViewPortWidth() = 0;
752 
758  virtual int getViewPortHeight() = 0;
759 
765  virtual NativePixelFormat nativePixelFormat() = 0;
766 
767  PaintState & paintState() { return m_paintState; }
768 
769  void addPrimitive(Primitive & primitive);
770 
771  void primitivesExecutionWait();
772 
784  void enableBackgroundPrimitiveExecution(bool value);
785 
793  void enableBackgroundPrimitiveTimeout(bool value) { m_backgroundPrimitiveTimeoutEnabled = value; }
794 
795  bool backgroundPrimitiveTimeoutEnabled() { return m_backgroundPrimitiveTimeoutEnabled; }
796 
805  virtual void suspendBackgroundPrimitiveExecution() = 0;
806 
812  virtual void resumeBackgroundPrimitiveExecution() = 0;
813 
820  void processPrimitives();
821 
845  template <typename T>
846  void setSprites(T * sprites, int count) {
847  setSprites(sprites, count, sizeof(T));
848  }
849 
855  void removeSprites() { setSprites(nullptr, 0, 0); }
856 
865  void refreshSprites();
866 
872  bool isDoubleBuffered() { return m_doubleBuffered; }
873 
879  void setMouseCursor(Cursor * cursor);
880 
890  void setMouseCursor(CursorName cursorName);
891 
898  void setMouseCursorPos(int X, int Y);
899 
900  virtual void readScreen(Rect const & rect, RGB888 * destBuf) = 0;
901 
902 
903  // statics (used for common default properties)
904 
912  static int queueSize;
913 
914 
915 protected:
916 
918 
919  virtual void setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect) = 0;
920 
921  virtual void absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color) = 0;
922 
923  virtual void rawFillRow(int y, int x1, int x2, RGB888 color) = 0;
924 
925  virtual void drawEllipse(Size const & size, Rect & updateRect) = 0;
926 
927  virtual void clear(Rect & updateRect) = 0;
928 
929  virtual void VScroll(int scroll, Rect & updateRect) = 0;
930 
931  virtual void HScroll(int scroll, Rect & updateRect) = 0;
932 
933  virtual void drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect) = 0;
934 
935  virtual void invertRect(Rect const & rect, Rect & updateRect) = 0;
936 
937  virtual void swapFGBG(Rect const & rect, Rect & updateRect) = 0;
938 
939  virtual void copyRect(Rect const & source, Rect & updateRect) = 0;
940 
941  virtual void swapBuffers() = 0;
942 
943  virtual int getBitmapSavePixelSize() = 0;
944 
945  virtual void rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount) = 0;
946 
947  virtual void rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
948 
949  virtual void rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
950 
951  virtual void rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
952 
954 
955  void execPrimitive(Primitive const & prim, Rect & updateRect, bool insideISR);
956 
957  void updateAbsoluteClippingRect();
958 
959  RGB888 getActualPenColor();
960 
961  RGB888 getActualBrushColor();
962 
963  void lineTo(Point const & position, Rect & updateRect);
964 
965  void drawRect(Rect const & rect, Rect & updateRect);
966 
967  void drawPath(Path const & path, Rect & updateRect);
968 
969  void absDrawThickLine(int X1, int Y1, int X2, int Y2, int penWidth, RGB888 const & color);
970 
971  void fillRect(Rect const & rect, RGB888 const & color, Rect & updateRect);
972 
973  void fillEllipse(int centerX, int centerY, Size const & size, RGB888 const & color, Rect & updateRect);
974 
975  void fillPath(Path const & path, RGB888 const & color, Rect & updateRect);
976 
977  void renderGlyphsBuffer(GlyphsBufferRenderInfo const & glyphsBufferRenderInfo, Rect & updateRect);
978 
979  void setSprites(Sprite * sprites, int count, int spriteSize);
980 
981  Sprite * getSprite(int index);
982 
983  int spritesCount() { return m_spritesCount; }
984 
985  void hideSprites(Rect & updateRect);
986 
987  void showSprites(Rect & updateRect);
988 
989  void drawBitmap(BitmapDrawingInfo const & bitmapDrawingInfo, Rect & updateRect);
990 
991  void absDrawBitmap(int destX, int destY, Bitmap const * bitmap, void * saveBackground, bool ignoreClippingRect);
992 
993  void setDoubleBuffered(bool value);
994 
995  bool getPrimitive(Primitive * primitive, int timeOutMS = 0);
996 
997  bool getPrimitiveISR(Primitive * primitive);
998 
999  void waitForPrimitives();
1000 
1001  Sprite * mouseCursor() { return &m_mouseCursor; }
1002 
1003  void resetPaintState();
1004 
1005 private:
1006 
1007  void primitiveReplaceDynamicBuffers(Primitive & primitive);
1008 
1009 
1010  PaintState m_paintState;
1011 
1012  volatile bool m_doubleBuffered;
1013  volatile QueueHandle_t m_execQueue;
1014 
1015  bool m_backgroundPrimitiveExecutionEnabled; // when False primitives are execute immediately
1016  volatile bool m_backgroundPrimitiveTimeoutEnabled; // when False VSyncInterrupt() has not timeout
1017 
1018  void * m_sprites; // pointer to array of sprite structures
1019  int m_spriteSize; // size of sprite structure
1020  int m_spritesCount; // number of sprites in m_sprites array
1021  bool m_spritesHidden; // true between hideSprites() and showSprites()
1022 
1023  // mouse cursor (mouse pointer) support
1024  Sprite m_mouseCursor;
1025  int16_t m_mouseHotspotX;
1026  int16_t m_mouseHotspotY;
1027 
1028  // memory pool used to allocate buffers of primitives
1029  LightMemoryPool m_primDynMemPool;
1030 
1031 };
1032 
1033 
1034 
1035 
1038 // GenericBitmappedDisplayController
1039 
1040 
1041 class GenericBitmappedDisplayController : public BitmappedDisplayController {
1042 
1043 protected:
1044 
1045 
1046  template <typename TPreparePixel, typename TRawSetPixel>
1047  void genericSetPixelAt(PixelDesc const & pixelDesc, Rect & updateRect, TPreparePixel preparePixel, TRawSetPixel rawSetPixel)
1048  {
1049  const int x = pixelDesc.pos.X + paintState().origin.X;
1050  const int y = pixelDesc.pos.Y + paintState().origin.Y;
1051 
1052  const int clipX1 = paintState().absClippingRect.X1;
1053  const int clipY1 = paintState().absClippingRect.Y1;
1054  const int clipX2 = paintState().absClippingRect.X2;
1055  const int clipY2 = paintState().absClippingRect.Y2;
1056 
1057  if (x >= clipX1 && x <= clipX2 && y >= clipY1 && y <= clipY2) {
1058  updateRect = updateRect.merge(Rect(x, y, x, y));
1059  hideSprites(updateRect);
1060  rawSetPixel(x, y, preparePixel(pixelDesc.color));
1061  }
1062  }
1063 
1064 
1065  // coordinates are absolute values (not relative to origin)
1066  // line clipped on current absolute clipping rectangle
1067  template <typename TPreparePixel, typename TRawFillRow, typename TRawInvertRow, typename TRawSetPixel, typename TRawInvertPixel>
1068  void genericAbsDrawLine(int X1, int Y1, int X2, int Y2, RGB888 const & color, TPreparePixel preparePixel, TRawFillRow rawFillRow, TRawInvertRow rawInvertRow, TRawSetPixel rawSetPixel, TRawInvertPixel rawInvertPixel)
1069  {
1070  if (paintState().penWidth > 1) {
1071  absDrawThickLine(X1, Y1, X2, Y2, paintState().penWidth, color);
1072  return;
1073  }
1074  auto pattern = preparePixel(color);
1075  if (Y1 == Y2) {
1076  // horizontal line
1077  if (Y1 < paintState().absClippingRect.Y1 || Y1 > paintState().absClippingRect.Y2)
1078  return;
1079  if (X1 > X2)
1080  tswap(X1, X2);
1081  if (X1 > paintState().absClippingRect.X2 || X2 < paintState().absClippingRect.X1)
1082  return;
1083  X1 = iclamp(X1, paintState().absClippingRect.X1, paintState().absClippingRect.X2);
1084  X2 = iclamp(X2, paintState().absClippingRect.X1, paintState().absClippingRect.X2);
1085  if (paintState().paintOptions.NOT)
1086  rawInvertRow(Y1, X1, X2);
1087  else
1088  rawFillRow(Y1, X1, X2, pattern);
1089  } else if (X1 == X2) {
1090  // vertical line
1091  if (X1 < paintState().absClippingRect.X1 || X1 > paintState().absClippingRect.X2)
1092  return;
1093  if (Y1 > Y2)
1094  tswap(Y1, Y2);
1095  if (Y1 > paintState().absClippingRect.Y2 || Y2 < paintState().absClippingRect.Y1)
1096  return;
1097  Y1 = iclamp(Y1, paintState().absClippingRect.Y1, paintState().absClippingRect.Y2);
1098  Y2 = iclamp(Y2, paintState().absClippingRect.Y1, paintState().absClippingRect.Y2);
1099  if (paintState().paintOptions.NOT) {
1100  for (int y = Y1; y <= Y2; ++y)
1101  rawInvertPixel(X1, y);
1102  } else {
1103  for (int y = Y1; y <= Y2; ++y)
1104  rawSetPixel(X1, y, pattern);
1105  }
1106  } else {
1107  // other cases (Bresenham's algorithm)
1108  // TODO: to optimize
1109  // Unfortunately here we cannot clip exactly using Sutherland-Cohen algorithm (as done before)
1110  // because the starting line (got from clipping algorithm) may not be the same of Bresenham's
1111  // line (think to continuing an existing line).
1112  // Possible solutions:
1113  // - "Yevgeny P. Kuzmin" algorithm:
1114  // https://stackoverflow.com/questions/40884680/how-to-use-bresenhams-line-drawing-algorithm-with-clipping
1115  // https://github.com/ktfh/ClippedLine/blob/master/clip.hpp
1116  // For now Sutherland-Cohen algorithm is only used to check the line is actually visible,
1117  // then test for every point inside the main Bresenham's loop.
1118  if (!clipLine(X1, Y1, X2, Y2, paintState().absClippingRect, true)) // true = do not change line coordinates!
1119  return;
1120  const int dx = abs(X2 - X1);
1121  const int dy = abs(Y2 - Y1);
1122  const int sx = X1 < X2 ? 1 : -1;
1123  const int sy = Y1 < Y2 ? 1 : -1;
1124  int err = (dx > dy ? dx : -dy) / 2;
1125  while (true) {
1126  if (paintState().absClippingRect.contains(X1, Y1)) {
1127  if (paintState().paintOptions.NOT)
1128  rawInvertPixel(X1, Y1);
1129  else
1130  rawSetPixel(X1, Y1, pattern);
1131  }
1132  if (X1 == X2 && Y1 == Y2)
1133  break;
1134  int e2 = err;
1135  if (e2 > -dx) {
1136  err -= dy;
1137  X1 += sx;
1138  }
1139  if (e2 < dy) {
1140  err += dx;
1141  Y1 += sy;
1142  }
1143  }
1144  }
1145  }
1146 
1147 
1148  // McIlroy's algorithm
1149  template <typename TPreparePixel, typename TRawSetPixel>
1150  void genericDrawEllipse(Size const & size, Rect & updateRect, TPreparePixel preparePixel, TRawSetPixel rawSetPixel)
1151  {
1152  auto pattern = preparePixel(getActualPenColor());
1153 
1154  const int clipX1 = paintState().absClippingRect.X1;
1155  const int clipY1 = paintState().absClippingRect.Y1;
1156  const int clipX2 = paintState().absClippingRect.X2;
1157  const int clipY2 = paintState().absClippingRect.Y2;
1158 
1159  const int centerX = paintState().position.X;
1160  const int centerY = paintState().position.Y;
1161 
1162  const int halfWidth = size.width / 2;
1163  const int halfHeight = size.height / 2;
1164 
1165  updateRect = updateRect.merge(Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth, centerY + halfHeight));
1166  hideSprites(updateRect);
1167 
1168  const int a2 = halfWidth * halfWidth;
1169  const int b2 = halfHeight * halfHeight;
1170  const int crit1 = -(a2 / 4 + halfWidth % 2 + b2);
1171  const int crit2 = -(b2 / 4 + halfHeight % 2 + a2);
1172  const int crit3 = -(b2 / 4 + halfHeight % 2);
1173  const int d2xt = 2 * b2;
1174  const int d2yt = 2 * a2;
1175  int x = 0; // travels from 0 up to halfWidth
1176  int y = halfHeight; // travels from halfHeight down to 0
1177  int t = -a2 * y;
1178  int dxt = 2 * b2 * x;
1179  int dyt = -2 * a2 * y;
1180 
1181  while (y >= 0 && x <= halfWidth) {
1182  const int col1 = centerX - x;
1183  const int col2 = centerX + x;
1184  const int row1 = centerY - y;
1185  const int row2 = centerY + y;
1186 
1187  if (col1 >= clipX1 && col1 <= clipX2) {
1188  if (row1 >= clipY1 && row1 <= clipY2)
1189  rawSetPixel(col1, row1, pattern);
1190  if (row2 >= clipY1 && row2 <= clipY2)
1191  rawSetPixel(col1, row2, pattern);
1192  }
1193  if (col2 >= clipX1 && col2 <= clipX2) {
1194  if (row1 >= clipY1 && row1 <= clipY2)
1195  rawSetPixel(col2, row1, pattern);
1196  if (row2 >= clipY1 && row2 <= clipY2)
1197  rawSetPixel(col2, row2, pattern);
1198  }
1199 
1200  if (t + b2 * x <= crit1 || t + a2 * y <= crit3) {
1201  x++;
1202  dxt += d2xt;
1203  t += dxt;
1204  } else if (t - a2 * y > crit2) {
1205  y--;
1206  dyt += d2yt;
1207  t += dyt;
1208  } else {
1209  x++;
1210  dxt += d2xt;
1211  t += dxt;
1212  y--;
1213  dyt += d2yt;
1214  t += dyt;
1215  }
1216  }
1217  }
1218 
1219 
1220  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1221  void genericDrawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1222  {
1223  if (!glyphOptions.bold && !glyphOptions.italic && !glyphOptions.blank && !glyphOptions.underline && !glyphOptions.doubleWidth && glyph.width <= 32)
1224  genericDrawGlyph_light(glyph, glyphOptions, penColor, brushColor, updateRect, preparePixel, rawGetRow, rawSetPixelInRow);
1225  else
1226  genericDrawGlyph_full(glyph, glyphOptions, penColor, brushColor, updateRect, preparePixel, rawGetRow, rawSetPixelInRow);
1227  }
1228 
1229 
1230  // TODO: Italic doesn't work well when clipping rect is specified
1231  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1232  void genericDrawGlyph_full(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1233  {
1234  const int clipX1 = paintState().absClippingRect.X1;
1235  const int clipY1 = paintState().absClippingRect.Y1;
1236  const int clipX2 = paintState().absClippingRect.X2;
1237  const int clipY2 = paintState().absClippingRect.Y2;
1238 
1239  const int origX = paintState().origin.X;
1240  const int origY = paintState().origin.Y;
1241 
1242  const int glyphX = glyph.X + origX;
1243  const int glyphY = glyph.Y + origY;
1244 
1245  if (glyphX > clipX2 || glyphY > clipY2)
1246  return;
1247 
1248  int16_t glyphWidth = glyph.width;
1249  int16_t glyphHeight = glyph.height;
1250  uint8_t const * glyphData = glyph.data;
1251  int16_t glyphWidthByte = (glyphWidth + 7) / 8;
1252  int16_t glyphSize = glyphHeight * glyphWidthByte;
1253 
1254  bool fillBackground = glyphOptions.fillBackground;
1255  bool bold = glyphOptions.bold;
1256  bool italic = glyphOptions.italic;
1257  bool blank = glyphOptions.blank;
1258  bool underline = glyphOptions.underline;
1259  int doubleWidth = glyphOptions.doubleWidth;
1260 
1261  // modify glyph to handle top half and bottom half double height
1262  // doubleWidth = 1 is handled directly inside drawing routine
1263  if (doubleWidth > 1) {
1264  uint8_t * newGlyphData = (uint8_t*) alloca(glyphSize);
1265  // doubling top-half or doubling bottom-half?
1266  int offset = (doubleWidth == 2 ? 0 : (glyphHeight >> 1));
1267  for (int y = 0; y < glyphHeight ; ++y)
1268  for (int x = 0; x < glyphWidthByte; ++x)
1269  newGlyphData[x + y * glyphWidthByte] = glyphData[x + (offset + (y >> 1)) * glyphWidthByte];
1270  glyphData = newGlyphData;
1271  }
1272 
1273  // a very simple and ugly skew (italic) implementation!
1274  int skewAdder = 0, skewH1 = 0, skewH2 = 0;
1275  if (italic) {
1276  skewAdder = 2;
1277  skewH1 = glyphHeight / 3;
1278  skewH2 = skewH1 * 2;
1279  }
1280 
1281  int16_t X1 = 0;
1282  int16_t XCount = glyphWidth;
1283  int16_t destX = glyphX;
1284 
1285  if (destX < clipX1) {
1286  X1 = (clipX1 - destX) / (doubleWidth ? 2 : 1);
1287  destX = clipX1;
1288  }
1289  if (X1 >= glyphWidth)
1290  return;
1291 
1292  if (destX + XCount + skewAdder > clipX2 + 1)
1293  XCount = clipX2 + 1 - destX - skewAdder;
1294  if (X1 + XCount > glyphWidth)
1295  XCount = glyphWidth - X1;
1296 
1297  int16_t Y1 = 0;
1298  int16_t YCount = glyphHeight;
1299  int destY = glyphY;
1300 
1301  if (destY < clipY1) {
1302  Y1 = clipY1 - destY;
1303  destY = clipY1;
1304  }
1305  if (Y1 >= glyphHeight)
1306  return;
1307 
1308  if (destY + YCount > clipY2 + 1)
1309  YCount = clipY2 + 1 - destY;
1310  if (Y1 + YCount > glyphHeight)
1311  YCount = glyphHeight - Y1;
1312 
1313  updateRect = updateRect.merge(Rect(destX, destY, destX + XCount + skewAdder - 1, destY + YCount - 1));
1314  hideSprites(updateRect);
1315 
1316  if (glyphOptions.invert ^ paintState().paintOptions.swapFGBG)
1317  tswap(penColor, brushColor);
1318 
1319  // a very simple and ugly reduce luminosity (faint) implementation!
1320  if (glyphOptions.reduceLuminosity) {
1321  if (penColor.R > 128) penColor.R = 128;
1322  if (penColor.G > 128) penColor.G = 128;
1323  if (penColor.B > 128) penColor.B = 128;
1324  }
1325 
1326  auto penPattern = preparePixel(penColor);
1327  auto brushPattern = preparePixel(brushColor);
1328  auto boldPattern = bold ? preparePixel(RGB888(penColor.R / 2 + 1,
1329  penColor.G / 2 + 1,
1330  penColor.B / 2 + 1))
1331  : preparePixel(RGB888(0, 0, 0));
1332 
1333  for (int y = Y1; y < Y1 + YCount; ++y, ++destY) {
1334 
1335  // true if previous pixel has been set
1336  bool prevSet = false;
1337 
1338  auto dstrow = rawGetRow(destY);
1339  auto srcrow = glyphData + y * glyphWidthByte;
1340 
1341  if (underline && y == glyphHeight - FABGLIB_UNDERLINE_POSITION - 1) {
1342 
1343  for (int x = X1, adestX = destX + skewAdder; x < X1 + XCount && adestX <= clipX2; ++x, ++adestX) {
1344  rawSetPixelInRow(dstrow, adestX, blank ? brushPattern : penPattern);
1345  if (doubleWidth) {
1346  ++adestX;
1347  if (adestX > clipX2)
1348  break;
1349  rawSetPixelInRow(dstrow, adestX, blank ? brushPattern : penPattern);
1350  }
1351  }
1352 
1353  } else {
1354 
1355  for (int x = X1, adestX = destX + skewAdder; x < X1 + XCount && adestX <= clipX2; ++x, ++adestX) {
1356  if ((srcrow[x >> 3] << (x & 7)) & 0x80 && !blank) {
1357  rawSetPixelInRow(dstrow, adestX, penPattern);
1358  prevSet = true;
1359  } else if (bold && prevSet) {
1360  rawSetPixelInRow(dstrow, adestX, boldPattern);
1361  prevSet = false;
1362  } else if (fillBackground) {
1363  rawSetPixelInRow(dstrow, adestX, brushPattern);
1364  prevSet = false;
1365  } else {
1366  prevSet = false;
1367  }
1368  if (doubleWidth) {
1369  ++adestX;
1370  if (adestX > clipX2)
1371  break;
1372  if (fillBackground)
1373  rawSetPixelInRow(dstrow, adestX, prevSet ? penPattern : brushPattern);
1374  else if (prevSet)
1375  rawSetPixelInRow(dstrow, adestX, penPattern);
1376  }
1377  }
1378 
1379  }
1380 
1381  if (italic && (y == skewH1 || y == skewH2))
1382  --skewAdder;
1383 
1384  }
1385  }
1386 
1387 
1388  // assume:
1389  // glyph.width <= 32
1390  // glyphOptions.fillBackground = 0 or 1
1391  // glyphOptions.invert : 0 or 1
1392  // glyphOptions.reduceLuminosity: 0 or 1
1393  // glyphOptions.... others = 0
1394  // paintState().paintOptions.swapFGBG: 0 or 1
1395  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1396  void genericDrawGlyph_light(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1397  {
1398  const int clipX1 = paintState().absClippingRect.X1;
1399  const int clipY1 = paintState().absClippingRect.Y1;
1400  const int clipX2 = paintState().absClippingRect.X2;
1401  const int clipY2 = paintState().absClippingRect.Y2;
1402 
1403  const int origX = paintState().origin.X;
1404  const int origY = paintState().origin.Y;
1405 
1406  const int glyphX = glyph.X + origX;
1407  const int glyphY = glyph.Y + origY;
1408 
1409  if (glyphX > clipX2 || glyphY > clipY2)
1410  return;
1411 
1412  int16_t glyphWidth = glyph.width;
1413  int16_t glyphHeight = glyph.height;
1414  uint8_t const * glyphData = glyph.data;
1415  int16_t glyphWidthByte = (glyphWidth + 7) / 8;
1416 
1417  int16_t X1 = 0;
1418  int16_t XCount = glyphWidth;
1419  int16_t destX = glyphX;
1420 
1421  int16_t Y1 = 0;
1422  int16_t YCount = glyphHeight;
1423  int destY = glyphY;
1424 
1425  if (destX < clipX1) {
1426  X1 = clipX1 - destX;
1427  destX = clipX1;
1428  }
1429  if (X1 >= glyphWidth)
1430  return;
1431 
1432  if (destX + XCount > clipX2 + 1)
1433  XCount = clipX2 + 1 - destX;
1434  if (X1 + XCount > glyphWidth)
1435  XCount = glyphWidth - X1;
1436 
1437  if (destY < clipY1) {
1438  Y1 = clipY1 - destY;
1439  destY = clipY1;
1440  }
1441  if (Y1 >= glyphHeight)
1442  return;
1443 
1444  if (destY + YCount > clipY2 + 1)
1445  YCount = clipY2 + 1 - destY;
1446  if (Y1 + YCount > glyphHeight)
1447  YCount = glyphHeight - Y1;
1448 
1449  updateRect = updateRect.merge(Rect(destX, destY, destX + XCount - 1, destY + YCount - 1));
1450  hideSprites(updateRect);
1451 
1452  if (glyphOptions.invert ^ paintState().paintOptions.swapFGBG)
1453  tswap(penColor, brushColor);
1454 
1455  // a very simple and ugly reduce luminosity (faint) implementation!
1456  if (glyphOptions.reduceLuminosity) {
1457  if (penColor.R > 128) penColor.R = 128;
1458  if (penColor.G > 128) penColor.G = 128;
1459  if (penColor.B > 128) penColor.B = 128;
1460  }
1461 
1462  bool fillBackground = glyphOptions.fillBackground;
1463 
1464  auto penPattern = preparePixel(penColor);
1465  auto brushPattern = preparePixel(brushColor);
1466 
1467  for (int y = Y1; y < Y1 + YCount; ++y, ++destY) {
1468  auto dstrow = rawGetRow(destY);
1469  uint8_t const * srcrow = glyphData + y * glyphWidthByte;
1470 
1471  uint32_t src = (srcrow[0] << 24) | (srcrow[1] << 16) | (srcrow[2] << 8) | (srcrow[3]);
1472  src <<= X1;
1473  if (fillBackground) {
1474  // filled background
1475  for (int x = X1, adestX = destX; x < X1 + XCount; ++x, ++adestX, src <<= 1)
1476  rawSetPixelInRow(dstrow, adestX, src & 0x80000000 ? penPattern : brushPattern);
1477  } else {
1478  // transparent background
1479  for (int x = X1, adestX = destX; x < X1 + XCount; ++x, ++adestX, src <<= 1)
1480  if (src & 0x80000000)
1481  rawSetPixelInRow(dstrow, adestX, penPattern);
1482  }
1483  }
1484  }
1485 
1486 
1487  template <typename TRawInvertRow>
1488  void genericInvertRect(Rect const & rect, Rect & updateRect, TRawInvertRow rawInvertRow)
1489  {
1490  const int origX = paintState().origin.X;
1491  const int origY = paintState().origin.Y;
1492 
1493  const int clipX1 = paintState().absClippingRect.X1;
1494  const int clipY1 = paintState().absClippingRect.Y1;
1495  const int clipX2 = paintState().absClippingRect.X2;
1496  const int clipY2 = paintState().absClippingRect.Y2;
1497 
1498  const int x1 = iclamp(rect.X1 + origX, clipX1, clipX2);
1499  const int y1 = iclamp(rect.Y1 + origY, clipY1, clipY2);
1500  const int x2 = iclamp(rect.X2 + origX, clipX1, clipX2);
1501  const int y2 = iclamp(rect.Y2 + origY, clipY1, clipY2);
1502 
1503  updateRect = updateRect.merge(Rect(x1, y1, x2, y2));
1504  hideSprites(updateRect);
1505 
1506  for (int y = y1; y <= y2; ++y)
1507  rawInvertRow(y, x1, x2);
1508  }
1509 
1510 
1511  template <typename TPreparePixel, typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1512  void genericSwapFGBG(Rect const & rect, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1513  {
1514  auto penPattern = preparePixel(paintState().penColor);
1515  auto brushPattern = preparePixel(paintState().brushColor);
1516 
1517  int origX = paintState().origin.X;
1518  int origY = paintState().origin.Y;
1519 
1520  const int clipX1 = paintState().absClippingRect.X1;
1521  const int clipY1 = paintState().absClippingRect.Y1;
1522  const int clipX2 = paintState().absClippingRect.X2;
1523  const int clipY2 = paintState().absClippingRect.Y2;
1524 
1525  const int x1 = iclamp(rect.X1 + origX, clipX1, clipX2);
1526  const int y1 = iclamp(rect.Y1 + origY, clipY1, clipY2);
1527  const int x2 = iclamp(rect.X2 + origX, clipX1, clipX2);
1528  const int y2 = iclamp(rect.Y2 + origY, clipY1, clipY2);
1529 
1530  updateRect = updateRect.merge(Rect(x1, y1, x2, y2));
1531  hideSprites(updateRect);
1532 
1533  for (int y = y1; y <= y2; ++y) {
1534  auto row = rawGetRow(y);
1535  for (int x = x1; x <= x2; ++x) {
1536  auto px = rawGetPixelInRow(row, x);
1537  if (px == penPattern)
1538  rawSetPixelInRow(row, x, brushPattern);
1539  else if (px == brushPattern)
1540  rawSetPixelInRow(row, x, penPattern);
1541  }
1542  }
1543  }
1544 
1545 
1546  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1547  void genericCopyRect(Rect const & source, Rect & updateRect, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1548  {
1549  const int clipX1 = paintState().absClippingRect.X1;
1550  const int clipY1 = paintState().absClippingRect.Y1;
1551  const int clipX2 = paintState().absClippingRect.X2;
1552  const int clipY2 = paintState().absClippingRect.Y2;
1553 
1554  int origX = paintState().origin.X;
1555  int origY = paintState().origin.Y;
1556 
1557  int srcX = source.X1 + origX;
1558  int srcY = source.Y1 + origY;
1559  int width = source.X2 - source.X1 + 1;
1560  int height = source.Y2 - source.Y1 + 1;
1561  int destX = paintState().position.X;
1562  int destY = paintState().position.Y;
1563  int deltaX = destX - srcX;
1564  int deltaY = destY - srcY;
1565 
1566  int incX = deltaX < 0 ? 1 : -1;
1567  int incY = deltaY < 0 ? 1 : -1;
1568 
1569  int startX = deltaX < 0 ? destX : destX + width - 1;
1570  int startY = deltaY < 0 ? destY : destY + height - 1;
1571 
1572  updateRect = updateRect.merge(Rect(srcX, srcY, srcX + width - 1, srcY + height - 1));
1573  updateRect = updateRect.merge(Rect(destX, destY, destX + width - 1, destY + height - 1));
1574  hideSprites(updateRect);
1575 
1576  for (int y = startY, i = 0; i < height; y += incY, ++i) {
1577  if (y >= clipY1 && y <= clipY2) {
1578  auto srcRow = rawGetRow(y - deltaY);
1579  auto dstRow = rawGetRow(y);
1580  for (int x = startX, j = 0; j < width; x += incX, ++j) {
1581  if (x >= clipX1 && x <= clipX2)
1582  rawSetPixelInRow(dstRow, x, rawGetPixelInRow(srcRow, x - deltaX));
1583  }
1584  }
1585  }
1586  }
1587 
1588 
1589  template <typename TRawGetRow, typename TRawSetPixelInRow, typename TDataType>
1590  void genericRawDrawBitmap_Native(int destX, int destY, TDataType * data, int width, int X1, int Y1, int XCount, int YCount,
1591  TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1592  {
1593  const int yEnd = Y1 + YCount;
1594  const int xEnd = X1 + XCount;
1595  for (int y = Y1; y < yEnd; ++y, ++destY) {
1596  auto dstrow = rawGetRow(destY);
1597  auto src = data + y * width + X1;
1598  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src)
1599  rawSetPixelInRow(dstrow, adestX, *src);
1600  }
1601  }
1602 
1603 
1604 
1605  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1606  void genericRawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1607  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1608  {
1609  const int width = bitmap->width;
1610  const int yEnd = Y1 + YCount;
1611  const int xEnd = X1 + XCount;
1612  auto data = bitmap->data;
1613  const int rowlen = (bitmap->width + 7) / 8;
1614 
1615  if (saveBackground) {
1616 
1617  // save background and draw the bitmap
1618  for (int y = Y1; y < yEnd; ++y, ++destY) {
1619  auto dstrow = rawGetRow(destY);
1620  auto savePx = saveBackground + y * width + X1;
1621  auto src = data + y * rowlen;
1622  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx) {
1623  *savePx = rawGetPixelInRow(dstrow, adestX);
1624  if ((src[x >> 3] << (x & 7)) & 0x80)
1625  rawSetPixelInRow(dstrow, adestX);
1626  }
1627  }
1628 
1629  } else {
1630 
1631  // just draw the bitmap
1632  for (int y = Y1; y < yEnd; ++y, ++destY) {
1633  auto dstrow = rawGetRow(destY);
1634  auto src = data + y * rowlen;
1635  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX) {
1636  if ((src[x >> 3] << (x & 7)) & 0x80)
1637  rawSetPixelInRow(dstrow, adestX);
1638  }
1639  }
1640 
1641  }
1642  }
1643 
1644 
1645  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1646  void genericRawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1647  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1648  {
1649  const int width = bitmap->width;
1650  const int yEnd = Y1 + YCount;
1651  const int xEnd = X1 + XCount;
1652  auto data = bitmap->data;
1653 
1654  if (saveBackground) {
1655 
1656  // save background and draw the bitmap
1657  for (int y = Y1; y < yEnd; ++y, ++destY) {
1658  auto dstrow = rawGetRow(destY);
1659  auto savePx = saveBackground + y * width + X1;
1660  auto src = data + y * width + X1;
1661  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx, ++src) {
1662  *savePx = rawGetPixelInRow(dstrow, adestX);
1663  if (*src & 0xc0) // alpha > 0 ?
1664  rawSetPixelInRow(dstrow, adestX, *src);
1665  }
1666  }
1667 
1668  } else {
1669 
1670  // just draw the bitmap
1671  for (int y = Y1; y < yEnd; ++y, ++destY) {
1672  auto dstrow = rawGetRow(destY);
1673  auto src = data + y * width + X1;
1674  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src) {
1675  if (*src & 0xc0) // alpha > 0 ?
1676  rawSetPixelInRow(dstrow, adestX, *src);
1677  }
1678  }
1679 
1680  }
1681  }
1682 
1683 
1684  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1685  void genericRawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1686  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1687  {
1688  const int width = bitmap->width;
1689  const int yEnd = Y1 + YCount;
1690  const int xEnd = X1 + XCount;
1691  auto data = (RGBA8888 const *) bitmap->data;
1692 
1693  if (saveBackground) {
1694 
1695  // save background and draw the bitmap
1696  for (int y = Y1; y < yEnd; ++y, ++destY) {
1697  auto dstrow = rawGetRow(destY);
1698  auto savePx = saveBackground + y * width + X1;
1699  auto src = data + y * width + X1;
1700  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx, ++src) {
1701  *savePx = rawGetPixelInRow(dstrow, adestX);
1702  if (src->A)
1703  rawSetPixelInRow(dstrow, adestX, *src);
1704  }
1705  }
1706 
1707  } else {
1708 
1709  // just draw the bitmap
1710  for (int y = Y1; y < yEnd; ++y, ++destY) {
1711  auto dstrow = rawGetRow(destY);
1712  auto src = data + y * width + X1;
1713  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src) {
1714  if (src->A)
1715  rawSetPixelInRow(dstrow, adestX, *src);
1716  }
1717  }
1718 
1719  }
1720  }
1721 
1722 
1723  // Scroll is done copying and filling rows
1724  // scroll < 0 -> scroll UP
1725  // scroll > 0 -> scroll DOWN
1726  template <typename TRawCopyRow, typename TRawFillRow>
1727  void genericVScroll(int scroll, Rect & updateRect,
1728  TRawCopyRow rawCopyRow, TRawFillRow rawFillRow)
1729  {
1730  hideSprites(updateRect);
1731  RGB888 color = getActualBrushColor();
1732  int Y1 = paintState().scrollingRegion.Y1;
1733  int Y2 = paintState().scrollingRegion.Y2;
1734  int X1 = paintState().scrollingRegion.X1;
1735  int X2 = paintState().scrollingRegion.X2;
1736  int height = Y2 - Y1 + 1;
1737 
1738  if (scroll < 0) {
1739 
1740  // scroll UP
1741 
1742  for (int i = 0; i < height + scroll; ++i) {
1743  // copy X1..X2 of (Y1 + i - scroll) to (Y1 + i)
1744  rawCopyRow(X1, X2, (Y1 + i - scroll), (Y1 + i));
1745  }
1746  // fill lower area with brush color
1747  for (int i = height + scroll; i < height; ++i)
1748  rawFillRow(Y1 + i, X1, X2, color);
1749 
1750  } else if (scroll > 0) {
1751 
1752  // scroll DOWN
1753  for (int i = height - scroll - 1; i >= 0; --i) {
1754  // copy X1..X2 of (Y1 + i) to (Y1 + i + scroll)
1755  rawCopyRow(X1, X2, (Y1 + i), (Y1 + i + scroll));
1756  }
1757 
1758  // fill upper area with brush color
1759  for (int i = 0; i < scroll; ++i)
1760  rawFillRow(Y1 + i, X1, X2, color);
1761 
1762  }
1763  }
1764 
1765 
1766  // scroll is done swapping rows and rows pointers
1767  // scroll < 0 -> scroll UP
1768  // scroll > 0 -> scroll DOWN
1769  template <typename TSwapRowsCopying, typename TSwapRowsPointers, typename TRawFillRow>
1770  void genericVScroll(int scroll, Rect & updateRect,
1771  TSwapRowsCopying swapRowsCopying, TSwapRowsPointers swapRowsPointers, TRawFillRow rawFillRow)
1772  {
1773  hideSprites(updateRect);
1774  RGB888 color = getActualBrushColor();
1775  const int Y1 = paintState().scrollingRegion.Y1;
1776  const int Y2 = paintState().scrollingRegion.Y2;
1777  const int X1 = paintState().scrollingRegion.X1;
1778  const int X2 = paintState().scrollingRegion.X2;
1779  const int height = Y2 - Y1 + 1;
1780 
1781  const int viewPortWidth = getViewPortWidth();
1782 
1783  if (scroll < 0) {
1784 
1785  // scroll UP
1786 
1787  for (int i = 0; i < height + scroll; ++i) {
1788 
1789  // these are necessary to maintain invariate out of scrolling regions
1790  if (X1 > 0)
1791  swapRowsCopying(Y1 + i, Y1 + i - scroll, 0, X1 - 1);
1792  if (X2 < viewPortWidth - 1)
1793  swapRowsCopying(Y1 + i, Y1 + i - scroll, X2 + 1, viewPortWidth - 1);
1794 
1795  // swap scan lines
1796  swapRowsPointers(Y1 + i, Y1 + i - scroll);
1797  }
1798 
1799  // fill lower area with brush color
1800  for (int i = height + scroll; i < height; ++i)
1801  rawFillRow(Y1 + i, X1, X2, color);
1802 
1803  } else if (scroll > 0) {
1804 
1805  // scroll DOWN
1806  for (int i = height - scroll - 1; i >= 0; --i) {
1807 
1808  // these are necessary to maintain invariate out of scrolling regions
1809  if (X1 > 0)
1810  swapRowsCopying(Y1 + i, Y1 + i + scroll, 0, X1 - 1);
1811  if (X2 < viewPortWidth - 1)
1812  swapRowsCopying(Y1 + i, Y1 + i + scroll, X2 + 1, viewPortWidth - 1);
1813 
1814  // swap scan lines
1815  swapRowsPointers(Y1 + i, Y1 + i + scroll);
1816  }
1817 
1818  // fill upper area with brush color
1819  for (int i = 0; i < scroll; ++i)
1820  rawFillRow(Y1 + i, X1, X2, color);
1821 
1822  }
1823  }
1824 
1825 
1826 
1827  // Scroll is done copying and filling columns
1828  // scroll < 0 -> scroll LEFT
1829  // scroll > 0 -> scroll RIGHT
1830  template <typename TPreparePixel, typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1831  void genericHScroll(int scroll, Rect & updateRect,
1832  TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1833  {
1834  hideSprites(updateRect);
1835  auto pattern = preparePixel(getActualBrushColor());
1836 
1837  int Y1 = paintState().scrollingRegion.Y1;
1838  int Y2 = paintState().scrollingRegion.Y2;
1839  int X1 = paintState().scrollingRegion.X1;
1840  int X2 = paintState().scrollingRegion.X2;
1841 
1842  if (scroll < 0) {
1843  // scroll left
1844  for (int y = Y1; y <= Y2; ++y) {
1845  auto row = rawGetRow(y);
1846  for (int x = X1; x <= X2 + scroll; ++x) {
1847  auto c = rawGetPixelInRow(row, x - scroll);
1848  rawSetPixelInRow(row, x, c);
1849  }
1850  // fill right area with brush color
1851  for (int x = X2 + 1 + scroll; x <= X2; ++x)
1852  rawSetPixelInRow(row, x, pattern);
1853  }
1854  } else if (scroll > 0) {
1855  // scroll right
1856  for (int y = Y1; y <= Y2; ++y) {
1857  auto row = rawGetRow(y);
1858  for (int x = X2 - scroll; x >= X1; --x) {
1859  auto c = rawGetPixelInRow(row, x);
1860  rawSetPixelInRow(row, x + scroll, c);
1861  }
1862  // fill left area with brush color
1863  for (int x = X1; x < X1 + scroll; ++x)
1864  rawSetPixelInRow(row, x, pattern);
1865  }
1866  }
1867  }
1868 
1869 
1870 
1871 
1872 };
1873 
1874 
1875 
1876 } // end of namespace
1877 
1878 
1879 
int16_t X2
Definition: fabutils.h:180
Represents a 24 bit RGB color.
Defines a cursor.
Represents a sprite.
void setSprites(T *sprites, int count)
Sets the list of active sprites.
virtual void resumeBackgroundPrimitiveExecution()=0
Resumes drawings after suspendBackgroundPrimitiveExecution().
uint16_t blank
int16_t Y2
Definition: fabutils.h:181
virtual int colorsCount()=0
Determines number of colors this display can provide.
uint16_t italic
int16_t Y1
Definition: fabutils.h:179
int16_t Y
uint16_t fillBackground
uint8_t const * data
Color
This enum defines named colors.
Represents the base abstract class for bitmapped display controllers.
GlyphOptions & Invert(uint8_t value)
Helper method to set or reset foreground and background swapping.
int16_t X1
Definition: fabutils.h:178
LineEnds
This enum defines line ends when pen width is greater than 1.
uint16_t underline
GlyphOptions & DoubleWidth(uint8_t value)
Helper method to set or reset doubleWidth.
GlyphOptions & Italic(bool value)
Helper method to set or reset italic.
int getScreenWidth()
Determines the screen width in pixels.
virtual int getViewPortWidth()=0
Determines horizontal size of the viewport.
virtual int getViewPortHeight()=0
Determines vertical size of the viewport.
PixelFormat
This enum defines a pixel format.
DisplayControllerType controllerType()
Determines the display controller type.
Represents a glyph position, size and binary data.
Represents the coordinate of a point.
Definition: fabutils.h:191
Represents an image.
void refreshSprites()
Forces the sprites to be updated.
This file contains some utility classes and functions.
Definition: canvas.cpp:36
int getScreenHeight()
Determines the screen height in pixels.
GlyphOptions & Underline(bool value)
Helper method to set or reset underlined.
NativePixelFormat
This enum defines the display controller native pixel format.
void enableBackgroundPrimitiveExecution(bool value)
Enables or disables drawings inside vertical retracing time.
Represents a 32 bit RGBA color.
virtual DisplayControllerType controllerType()=0
Determines the display controller type.
Specifies various glyph painting options.
Represents a rectangle.
Definition: fabutils.h:226
CursorName
This enum defines a set of predefined mouse cursors.
void setMouseCursor(Cursor *cursor)
Sets mouse cursor and make it visible.
This file contains FabGL library configuration settings, like number of supported colors...
static int queueSize
Size of display controller primitives queue.
int16_t X
virtual void suspendBackgroundPrimitiveExecution()=0
Suspends drawings.
#define FABGLIB_UNDERLINE_POSITION
Definition: fabglconf.h:102
Represents a bidimensional size.
Definition: fabutils.h:209
DisplayControllerType controllerType()
Determines the display controller type.
void setMouseCursorPos(int X, int Y)
Sets mouse cursor position.
void processPrimitives()
Draws immediately all primitives in the queue.
bool isDoubleBuffered()
Determines whether BitmappedDisplayController is on double buffered mode.
uint8_t const * data
Represents an 8 bit ABGR color.
DisplayControllerType
This enum defines types of display controllers.
uint16_t doubleWidth
void removeSprites()
Empties the list of active sprites.
Represents a 6 bit RGB color.
PixelFormat format
GlyphOptions & Bold(bool value)
Helper method to set or reset bold.
Represents the base abstract class for all display controllers.
uint8_t height
Represents the base abstract class for textual display controllers.
GlyphOptions & Blank(uint8_t value)
Helper method to set or reset foreground and background swapping.
void enableBackgroundPrimitiveTimeout(bool value)
Enables or disables execution time limitation inside vertical retracing interrupt.
Specifies general paint options.
virtual NativePixelFormat nativePixelFormat()=0
Represents the native pixel format used by this display.
uint16_t bold
uint8_t width
GlyphOptions & FillBackground(bool value)
Helper method to set or reset fillBackground.