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. Feel free to use FabGL in free software and hardware:
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  int getScreenWidth() { return m_screenWidth; }
683 
689  int getScreenHeight() { return m_screenHeight; }
690 
691 protected:
692 
693  // inherited classes should call setScreenSize once display size is known
694  void setScreenSize(int width, int height) { m_screenWidth = width; m_screenHeight = height; }
695 
696 private:
697 
698  // we store here these info to avoid to have virtual methods (due the -vtables in flash- problem)
699  int16_t m_screenWidth;
700  int16_t m_screenHeight;
701 };
702 
703 
704 
709 
710 public:
711 
713 
714  virtual int getColumns() = 0;
715  virtual int getRows() = 0;
716  virtual void adjustMapSize(int * columns, int * rows) = 0;
717  virtual void setTextMap(uint32_t const * map, int rows) = 0;
718  virtual void enableCursor(bool value) = 0;
719  virtual void setCursorPos(int row, int col) = 0; // row and col starts from 0
720  virtual void setCursorForeground(Color value) = 0;
721  virtual void setCursorBackground(Color value) = 0;
722  virtual FontInfo const * getFont() = 0;
723 };
724 
725 
726 
731 
732 public:
733 
735  virtual ~BitmappedDisplayController();
736 
738 
744  virtual int getViewPortWidth() = 0;
745 
751  virtual int getViewPortHeight() = 0;
752 
758  virtual NativePixelFormat nativePixelFormat() = 0;
759 
760  PaintState & paintState() { return m_paintState; }
761 
762  void addPrimitive(Primitive & primitive);
763 
764  void primitivesExecutionWait();
765 
777  void enableBackgroundPrimitiveExecution(bool value);
778 
786  void enableBackgroundPrimitiveTimeout(bool value) { m_backgroundPrimitiveTimeoutEnabled = value; }
787 
788  bool backgroundPrimitiveTimeoutEnabled() { return m_backgroundPrimitiveTimeoutEnabled; }
789 
798  virtual void suspendBackgroundPrimitiveExecution() = 0;
799 
805  virtual void resumeBackgroundPrimitiveExecution() = 0;
806 
813  void processPrimitives();
814 
838  template <typename T>
839  void setSprites(T * sprites, int count) {
840  setSprites(sprites, count, sizeof(T));
841  }
842 
848  void removeSprites() { setSprites(nullptr, 0, 0); }
849 
858  void refreshSprites();
859 
865  bool isDoubleBuffered() { return m_doubleBuffered; }
866 
872  void setMouseCursor(Cursor * cursor);
873 
883  void setMouseCursor(CursorName cursorName);
884 
891  void setMouseCursorPos(int X, int Y);
892 
893  virtual void readScreen(Rect const & rect, RGB888 * destBuf) = 0;
894 
895 
896  // statics (used for common default properties)
897 
905  static int queueSize;
906 
907 
908 protected:
909 
911 
912  virtual void setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect) = 0;
913 
914  virtual void absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color) = 0;
915 
916  virtual void rawFillRow(int y, int x1, int x2, RGB888 color) = 0;
917 
918  virtual void drawEllipse(Size const & size, Rect & updateRect) = 0;
919 
920  virtual void clear(Rect & updateRect) = 0;
921 
922  virtual void VScroll(int scroll, Rect & updateRect) = 0;
923 
924  virtual void HScroll(int scroll, Rect & updateRect) = 0;
925 
926  virtual void drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect) = 0;
927 
928  virtual void invertRect(Rect const & rect, Rect & updateRect) = 0;
929 
930  virtual void swapFGBG(Rect const & rect, Rect & updateRect) = 0;
931 
932  virtual void copyRect(Rect const & source, Rect & updateRect) = 0;
933 
934  virtual void swapBuffers() = 0;
935 
936  virtual int getBitmapSavePixelSize() = 0;
937 
938  virtual void rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount) = 0;
939 
940  virtual void rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
941 
942  virtual void rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
943 
944  virtual void rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount) = 0;
945 
947 
948  void execPrimitive(Primitive const & prim, Rect & updateRect, bool insideISR);
949 
950  void updateAbsoluteClippingRect();
951 
952  RGB888 getActualPenColor();
953 
954  RGB888 getActualBrushColor();
955 
956  void lineTo(Point const & position, Rect & updateRect);
957 
958  void drawRect(Rect const & rect, Rect & updateRect);
959 
960  void drawPath(Path const & path, Rect & updateRect);
961 
962  void absDrawThickLine(int X1, int Y1, int X2, int Y2, int penWidth, RGB888 const & color);
963 
964  void fillRect(Rect const & rect, RGB888 const & color, Rect & updateRect);
965 
966  void fillEllipse(int centerX, int centerY, Size const & size, RGB888 const & color, Rect & updateRect);
967 
968  void fillPath(Path const & path, RGB888 const & color, Rect & updateRect);
969 
970  void renderGlyphsBuffer(GlyphsBufferRenderInfo const & glyphsBufferRenderInfo, Rect & updateRect);
971 
972  void setSprites(Sprite * sprites, int count, int spriteSize);
973 
974  Sprite * getSprite(int index);
975 
976  int spritesCount() { return m_spritesCount; }
977 
978  void hideSprites(Rect & updateRect);
979 
980  void showSprites(Rect & updateRect);
981 
982  void drawBitmap(BitmapDrawingInfo const & bitmapDrawingInfo, Rect & updateRect);
983 
984  void absDrawBitmap(int destX, int destY, Bitmap const * bitmap, void * saveBackground, bool ignoreClippingRect);
985 
986  void setDoubleBuffered(bool value);
987 
988  bool getPrimitive(Primitive * primitive, int timeOutMS = 0);
989 
990  bool getPrimitiveISR(Primitive * primitive);
991 
992  void waitForPrimitives();
993 
994  Sprite * mouseCursor() { return &m_mouseCursor; }
995 
996  void resetPaintState();
997 
998 private:
999 
1000  void primitiveReplaceDynamicBuffers(Primitive & primitive);
1001 
1002 
1003  PaintState m_paintState;
1004 
1005  volatile bool m_doubleBuffered;
1006  volatile QueueHandle_t m_execQueue;
1007 
1008  bool m_backgroundPrimitiveExecutionEnabled; // when False primitives are execute immediately
1009  volatile bool m_backgroundPrimitiveTimeoutEnabled; // when False VSyncInterrupt() has not timeout
1010 
1011  void * m_sprites; // pointer to array of sprite structures
1012  int m_spriteSize; // size of sprite structure
1013  int m_spritesCount; // number of sprites in m_sprites array
1014  bool m_spritesHidden; // true between hideSprites() and showSprites()
1015 
1016  // mouse cursor (mouse pointer) support
1017  Sprite m_mouseCursor;
1018  int16_t m_mouseHotspotX;
1019  int16_t m_mouseHotspotY;
1020 
1021  // memory pool used to allocate buffers of primitives
1022  LightMemoryPool m_primDynMemPool;
1023 
1024 };
1025 
1026 
1027 
1028 
1031 // GenericBitmappedDisplayController
1032 
1033 
1034 class GenericBitmappedDisplayController : public BitmappedDisplayController {
1035 
1036 protected:
1037 
1038 
1039  template <typename TPreparePixel, typename TRawSetPixel>
1040  void genericSetPixelAt(PixelDesc const & pixelDesc, Rect & updateRect, TPreparePixel preparePixel, TRawSetPixel rawSetPixel)
1041  {
1042  const int x = pixelDesc.pos.X + paintState().origin.X;
1043  const int y = pixelDesc.pos.Y + paintState().origin.Y;
1044 
1045  const int clipX1 = paintState().absClippingRect.X1;
1046  const int clipY1 = paintState().absClippingRect.Y1;
1047  const int clipX2 = paintState().absClippingRect.X2;
1048  const int clipY2 = paintState().absClippingRect.Y2;
1049 
1050  if (x >= clipX1 && x <= clipX2 && y >= clipY1 && y <= clipY2) {
1051  updateRect = updateRect.merge(Rect(x, y, x, y));
1052  hideSprites(updateRect);
1053  rawSetPixel(x, y, preparePixel(pixelDesc.color));
1054  }
1055  }
1056 
1057 
1058  // coordinates are absolute values (not relative to origin)
1059  // line clipped on current absolute clipping rectangle
1060  template <typename TPreparePixel, typename TRawFillRow, typename TRawInvertRow, typename TRawSetPixel, typename TRawInvertPixel>
1061  void genericAbsDrawLine(int X1, int Y1, int X2, int Y2, RGB888 const & color, TPreparePixel preparePixel, TRawFillRow rawFillRow, TRawInvertRow rawInvertRow, TRawSetPixel rawSetPixel, TRawInvertPixel rawInvertPixel)
1062  {
1063  if (paintState().penWidth > 1) {
1064  absDrawThickLine(X1, Y1, X2, Y2, paintState().penWidth, color);
1065  return;
1066  }
1067  auto pattern = preparePixel(color);
1068  if (Y1 == Y2) {
1069  // horizontal line
1070  if (Y1 < paintState().absClippingRect.Y1 || Y1 > paintState().absClippingRect.Y2)
1071  return;
1072  if (X1 > X2)
1073  tswap(X1, X2);
1074  if (X1 > paintState().absClippingRect.X2 || X2 < paintState().absClippingRect.X1)
1075  return;
1076  X1 = iclamp(X1, paintState().absClippingRect.X1, paintState().absClippingRect.X2);
1077  X2 = iclamp(X2, paintState().absClippingRect.X1, paintState().absClippingRect.X2);
1078  if (paintState().paintOptions.NOT)
1079  rawInvertRow(Y1, X1, X2);
1080  else
1081  rawFillRow(Y1, X1, X2, pattern);
1082  } else if (X1 == X2) {
1083  // vertical line
1084  if (X1 < paintState().absClippingRect.X1 || X1 > paintState().absClippingRect.X2)
1085  return;
1086  if (Y1 > Y2)
1087  tswap(Y1, Y2);
1088  if (Y1 > paintState().absClippingRect.Y2 || Y2 < paintState().absClippingRect.Y1)
1089  return;
1090  Y1 = iclamp(Y1, paintState().absClippingRect.Y1, paintState().absClippingRect.Y2);
1091  Y2 = iclamp(Y2, paintState().absClippingRect.Y1, paintState().absClippingRect.Y2);
1092  if (paintState().paintOptions.NOT) {
1093  for (int y = Y1; y <= Y2; ++y)
1094  rawInvertPixel(X1, y);
1095  } else {
1096  for (int y = Y1; y <= Y2; ++y)
1097  rawSetPixel(X1, y, pattern);
1098  }
1099  } else {
1100  // other cases (Bresenham's algorithm)
1101  // TODO: to optimize
1102  // Unfortunately here we cannot clip exactly using Sutherland-Cohen algorithm (as done before)
1103  // because the starting line (got from clipping algorithm) may not be the same of Bresenham's
1104  // line (think to continuing an existing line).
1105  // Possible solutions:
1106  // - "Yevgeny P. Kuzmin" algorithm:
1107  // https://stackoverflow.com/questions/40884680/how-to-use-bresenhams-line-drawing-algorithm-with-clipping
1108  // https://github.com/ktfh/ClippedLine/blob/master/clip.hpp
1109  // For now Sutherland-Cohen algorithm is only used to check the line is actually visible,
1110  // then test for every point inside the main Bresenham's loop.
1111  if (!clipLine(X1, Y1, X2, Y2, paintState().absClippingRect, true)) // true = do not change line coordinates!
1112  return;
1113  const int dx = abs(X2 - X1);
1114  const int dy = abs(Y2 - Y1);
1115  const int sx = X1 < X2 ? 1 : -1;
1116  const int sy = Y1 < Y2 ? 1 : -1;
1117  int err = (dx > dy ? dx : -dy) / 2;
1118  while (true) {
1119  if (paintState().absClippingRect.contains(X1, Y1)) {
1120  if (paintState().paintOptions.NOT)
1121  rawInvertPixel(X1, Y1);
1122  else
1123  rawSetPixel(X1, Y1, pattern);
1124  }
1125  if (X1 == X2 && Y1 == Y2)
1126  break;
1127  int e2 = err;
1128  if (e2 > -dx) {
1129  err -= dy;
1130  X1 += sx;
1131  }
1132  if (e2 < dy) {
1133  err += dx;
1134  Y1 += sy;
1135  }
1136  }
1137  }
1138  }
1139 
1140 
1141  // McIlroy's algorithm
1142  template <typename TPreparePixel, typename TRawSetPixel>
1143  void genericDrawEllipse(Size const & size, Rect & updateRect, TPreparePixel preparePixel, TRawSetPixel rawSetPixel)
1144  {
1145  auto pattern = preparePixel(getActualPenColor());
1146 
1147  const int clipX1 = paintState().absClippingRect.X1;
1148  const int clipY1 = paintState().absClippingRect.Y1;
1149  const int clipX2 = paintState().absClippingRect.X2;
1150  const int clipY2 = paintState().absClippingRect.Y2;
1151 
1152  const int centerX = paintState().position.X;
1153  const int centerY = paintState().position.Y;
1154 
1155  const int halfWidth = size.width / 2;
1156  const int halfHeight = size.height / 2;
1157 
1158  updateRect = updateRect.merge(Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth, centerY + halfHeight));
1159  hideSprites(updateRect);
1160 
1161  const int a2 = halfWidth * halfWidth;
1162  const int b2 = halfHeight * halfHeight;
1163  const int crit1 = -(a2 / 4 + halfWidth % 2 + b2);
1164  const int crit2 = -(b2 / 4 + halfHeight % 2 + a2);
1165  const int crit3 = -(b2 / 4 + halfHeight % 2);
1166  const int d2xt = 2 * b2;
1167  const int d2yt = 2 * a2;
1168  int x = 0; // travels from 0 up to halfWidth
1169  int y = halfHeight; // travels from halfHeight down to 0
1170  int t = -a2 * y;
1171  int dxt = 2 * b2 * x;
1172  int dyt = -2 * a2 * y;
1173 
1174  while (y >= 0 && x <= halfWidth) {
1175  const int col1 = centerX - x;
1176  const int col2 = centerX + x;
1177  const int row1 = centerY - y;
1178  const int row2 = centerY + y;
1179 
1180  if (col1 >= clipX1 && col1 <= clipX2) {
1181  if (row1 >= clipY1 && row1 <= clipY2)
1182  rawSetPixel(col1, row1, pattern);
1183  if (row2 >= clipY1 && row2 <= clipY2)
1184  rawSetPixel(col1, row2, pattern);
1185  }
1186  if (col2 >= clipX1 && col2 <= clipX2) {
1187  if (row1 >= clipY1 && row1 <= clipY2)
1188  rawSetPixel(col2, row1, pattern);
1189  if (row2 >= clipY1 && row2 <= clipY2)
1190  rawSetPixel(col2, row2, pattern);
1191  }
1192 
1193  if (t + b2 * x <= crit1 || t + a2 * y <= crit3) {
1194  x++;
1195  dxt += d2xt;
1196  t += dxt;
1197  } else if (t - a2 * y > crit2) {
1198  y--;
1199  dyt += d2yt;
1200  t += dyt;
1201  } else {
1202  x++;
1203  dxt += d2xt;
1204  t += dxt;
1205  y--;
1206  dyt += d2yt;
1207  t += dyt;
1208  }
1209  }
1210  }
1211 
1212 
1213  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1214  void genericDrawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1215  {
1216  if (!glyphOptions.bold && !glyphOptions.italic && !glyphOptions.blank && !glyphOptions.underline && !glyphOptions.doubleWidth && glyph.width <= 32)
1217  genericDrawGlyph_light(glyph, glyphOptions, penColor, brushColor, updateRect, preparePixel, rawGetRow, rawSetPixelInRow);
1218  else
1219  genericDrawGlyph_full(glyph, glyphOptions, penColor, brushColor, updateRect, preparePixel, rawGetRow, rawSetPixelInRow);
1220  }
1221 
1222 
1223  // TODO: Italic doesn't work well when clipping rect is specified
1224  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1225  void genericDrawGlyph_full(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1226  {
1227  const int clipX1 = paintState().absClippingRect.X1;
1228  const int clipY1 = paintState().absClippingRect.Y1;
1229  const int clipX2 = paintState().absClippingRect.X2;
1230  const int clipY2 = paintState().absClippingRect.Y2;
1231 
1232  const int origX = paintState().origin.X;
1233  const int origY = paintState().origin.Y;
1234 
1235  const int glyphX = glyph.X + origX;
1236  const int glyphY = glyph.Y + origY;
1237 
1238  if (glyphX > clipX2 || glyphY > clipY2)
1239  return;
1240 
1241  int16_t glyphWidth = glyph.width;
1242  int16_t glyphHeight = glyph.height;
1243  uint8_t const * glyphData = glyph.data;
1244  int16_t glyphWidthByte = (glyphWidth + 7) / 8;
1245  int16_t glyphSize = glyphHeight * glyphWidthByte;
1246 
1247  bool fillBackground = glyphOptions.fillBackground;
1248  bool bold = glyphOptions.bold;
1249  bool italic = glyphOptions.italic;
1250  bool blank = glyphOptions.blank;
1251  bool underline = glyphOptions.underline;
1252  int doubleWidth = glyphOptions.doubleWidth;
1253 
1254  // modify glyph to handle top half and bottom half double height
1255  // doubleWidth = 1 is handled directly inside drawing routine
1256  if (doubleWidth > 1) {
1257  uint8_t * newGlyphData = (uint8_t*) alloca(glyphSize);
1258  // doubling top-half or doubling bottom-half?
1259  int offset = (doubleWidth == 2 ? 0 : (glyphHeight >> 1));
1260  for (int y = 0; y < glyphHeight ; ++y)
1261  for (int x = 0; x < glyphWidthByte; ++x)
1262  newGlyphData[x + y * glyphWidthByte] = glyphData[x + (offset + (y >> 1)) * glyphWidthByte];
1263  glyphData = newGlyphData;
1264  }
1265 
1266  // a very simple and ugly skew (italic) implementation!
1267  int skewAdder = 0, skewH1 = 0, skewH2 = 0;
1268  if (italic) {
1269  skewAdder = 2;
1270  skewH1 = glyphHeight / 3;
1271  skewH2 = skewH1 * 2;
1272  }
1273 
1274  int16_t X1 = 0;
1275  int16_t XCount = glyphWidth;
1276  int16_t destX = glyphX;
1277 
1278  if (destX < clipX1) {
1279  X1 = (clipX1 - destX) / (doubleWidth ? 2 : 1);
1280  destX = clipX1;
1281  }
1282  if (X1 >= glyphWidth)
1283  return;
1284 
1285  if (destX + XCount + skewAdder > clipX2 + 1)
1286  XCount = clipX2 + 1 - destX - skewAdder;
1287  if (X1 + XCount > glyphWidth)
1288  XCount = glyphWidth - X1;
1289 
1290  int16_t Y1 = 0;
1291  int16_t YCount = glyphHeight;
1292  int destY = glyphY;
1293 
1294  if (destY < clipY1) {
1295  Y1 = clipY1 - destY;
1296  destY = clipY1;
1297  }
1298  if (Y1 >= glyphHeight)
1299  return;
1300 
1301  if (destY + YCount > clipY2 + 1)
1302  YCount = clipY2 + 1 - destY;
1303  if (Y1 + YCount > glyphHeight)
1304  YCount = glyphHeight - Y1;
1305 
1306  updateRect = updateRect.merge(Rect(destX, destY, destX + XCount + skewAdder - 1, destY + YCount - 1));
1307  hideSprites(updateRect);
1308 
1309  if (glyphOptions.invert ^ paintState().paintOptions.swapFGBG)
1310  tswap(penColor, brushColor);
1311 
1312  // a very simple and ugly reduce luminosity (faint) implementation!
1313  if (glyphOptions.reduceLuminosity) {
1314  if (penColor.R > 128) penColor.R = 128;
1315  if (penColor.G > 128) penColor.G = 128;
1316  if (penColor.B > 128) penColor.B = 128;
1317  }
1318 
1319  auto penPattern = preparePixel(penColor);
1320  auto brushPattern = preparePixel(brushColor);
1321  auto boldPattern = bold ? preparePixel(RGB888(penColor.R / 2 + 1,
1322  penColor.G / 2 + 1,
1323  penColor.B / 2 + 1))
1324  : preparePixel(RGB888(0, 0, 0));
1325 
1326  for (int y = Y1; y < Y1 + YCount; ++y, ++destY) {
1327 
1328  // true if previous pixel has been set
1329  bool prevSet = false;
1330 
1331  auto dstrow = rawGetRow(destY);
1332  auto srcrow = glyphData + y * glyphWidthByte;
1333 
1334  if (underline && y == glyphHeight - FABGLIB_UNDERLINE_POSITION - 1) {
1335 
1336  for (int x = X1, adestX = destX + skewAdder; x < X1 + XCount && adestX <= clipX2; ++x, ++adestX) {
1337  rawSetPixelInRow(dstrow, adestX, blank ? brushPattern : penPattern);
1338  if (doubleWidth) {
1339  ++adestX;
1340  if (adestX > clipX2)
1341  break;
1342  rawSetPixelInRow(dstrow, adestX, blank ? brushPattern : penPattern);
1343  }
1344  }
1345 
1346  } else {
1347 
1348  for (int x = X1, adestX = destX + skewAdder; x < X1 + XCount && adestX <= clipX2; ++x, ++adestX) {
1349  if ((srcrow[x >> 3] << (x & 7)) & 0x80 && !blank) {
1350  rawSetPixelInRow(dstrow, adestX, penPattern);
1351  prevSet = true;
1352  } else if (bold && prevSet) {
1353  rawSetPixelInRow(dstrow, adestX, boldPattern);
1354  prevSet = false;
1355  } else if (fillBackground) {
1356  rawSetPixelInRow(dstrow, adestX, brushPattern);
1357  prevSet = false;
1358  } else {
1359  prevSet = false;
1360  }
1361  if (doubleWidth) {
1362  ++adestX;
1363  if (adestX > clipX2)
1364  break;
1365  if (fillBackground)
1366  rawSetPixelInRow(dstrow, adestX, prevSet ? penPattern : brushPattern);
1367  else if (prevSet)
1368  rawSetPixelInRow(dstrow, adestX, penPattern);
1369  }
1370  }
1371 
1372  }
1373 
1374  if (italic && (y == skewH1 || y == skewH2))
1375  --skewAdder;
1376 
1377  }
1378  }
1379 
1380 
1381  // assume:
1382  // glyph.width <= 32
1383  // glyphOptions.fillBackground = 0 or 1
1384  // glyphOptions.invert : 0 or 1
1385  // glyphOptions.reduceLuminosity: 0 or 1
1386  // glyphOptions.... others = 0
1387  // paintState().paintOptions.swapFGBG: 0 or 1
1388  template <typename TPreparePixel, typename TRawGetRow, typename TRawSetPixelInRow>
1389  void genericDrawGlyph_light(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1390  {
1391  const int clipX1 = paintState().absClippingRect.X1;
1392  const int clipY1 = paintState().absClippingRect.Y1;
1393  const int clipX2 = paintState().absClippingRect.X2;
1394  const int clipY2 = paintState().absClippingRect.Y2;
1395 
1396  const int origX = paintState().origin.X;
1397  const int origY = paintState().origin.Y;
1398 
1399  const int glyphX = glyph.X + origX;
1400  const int glyphY = glyph.Y + origY;
1401 
1402  if (glyphX > clipX2 || glyphY > clipY2)
1403  return;
1404 
1405  int16_t glyphWidth = glyph.width;
1406  int16_t glyphHeight = glyph.height;
1407  uint8_t const * glyphData = glyph.data;
1408  int16_t glyphWidthByte = (glyphWidth + 7) / 8;
1409 
1410  int16_t X1 = 0;
1411  int16_t XCount = glyphWidth;
1412  int16_t destX = glyphX;
1413 
1414  int16_t Y1 = 0;
1415  int16_t YCount = glyphHeight;
1416  int destY = glyphY;
1417 
1418  if (destX < clipX1) {
1419  X1 = clipX1 - destX;
1420  destX = clipX1;
1421  }
1422  if (X1 >= glyphWidth)
1423  return;
1424 
1425  if (destX + XCount > clipX2 + 1)
1426  XCount = clipX2 + 1 - destX;
1427  if (X1 + XCount > glyphWidth)
1428  XCount = glyphWidth - X1;
1429 
1430  if (destY < clipY1) {
1431  Y1 = clipY1 - destY;
1432  destY = clipY1;
1433  }
1434  if (Y1 >= glyphHeight)
1435  return;
1436 
1437  if (destY + YCount > clipY2 + 1)
1438  YCount = clipY2 + 1 - destY;
1439  if (Y1 + YCount > glyphHeight)
1440  YCount = glyphHeight - Y1;
1441 
1442  updateRect = updateRect.merge(Rect(destX, destY, destX + XCount - 1, destY + YCount - 1));
1443  hideSprites(updateRect);
1444 
1445  if (glyphOptions.invert ^ paintState().paintOptions.swapFGBG)
1446  tswap(penColor, brushColor);
1447 
1448  // a very simple and ugly reduce luminosity (faint) implementation!
1449  if (glyphOptions.reduceLuminosity) {
1450  if (penColor.R > 128) penColor.R = 128;
1451  if (penColor.G > 128) penColor.G = 128;
1452  if (penColor.B > 128) penColor.B = 128;
1453  }
1454 
1455  bool fillBackground = glyphOptions.fillBackground;
1456 
1457  auto penPattern = preparePixel(penColor);
1458  auto brushPattern = preparePixel(brushColor);
1459 
1460  for (int y = Y1; y < Y1 + YCount; ++y, ++destY) {
1461  auto dstrow = rawGetRow(destY);
1462  uint8_t const * srcrow = glyphData + y * glyphWidthByte;
1463 
1464  uint32_t src = (srcrow[0] << 24) | (srcrow[1] << 16) | (srcrow[2] << 8) | (srcrow[3]);
1465  src <<= X1;
1466  if (fillBackground) {
1467  // filled background
1468  for (int x = X1, adestX = destX; x < X1 + XCount; ++x, ++adestX, src <<= 1)
1469  rawSetPixelInRow(dstrow, adestX, src & 0x80000000 ? penPattern : brushPattern);
1470  } else {
1471  // transparent background
1472  for (int x = X1, adestX = destX; x < X1 + XCount; ++x, ++adestX, src <<= 1)
1473  if (src & 0x80000000)
1474  rawSetPixelInRow(dstrow, adestX, penPattern);
1475  }
1476  }
1477  }
1478 
1479 
1480  template <typename TRawInvertRow>
1481  void genericInvertRect(Rect const & rect, Rect & updateRect, TRawInvertRow rawInvertRow)
1482  {
1483  const int origX = paintState().origin.X;
1484  const int origY = paintState().origin.Y;
1485 
1486  const int clipX1 = paintState().absClippingRect.X1;
1487  const int clipY1 = paintState().absClippingRect.Y1;
1488  const int clipX2 = paintState().absClippingRect.X2;
1489  const int clipY2 = paintState().absClippingRect.Y2;
1490 
1491  const int x1 = iclamp(rect.X1 + origX, clipX1, clipX2);
1492  const int y1 = iclamp(rect.Y1 + origY, clipY1, clipY2);
1493  const int x2 = iclamp(rect.X2 + origX, clipX1, clipX2);
1494  const int y2 = iclamp(rect.Y2 + origY, clipY1, clipY2);
1495 
1496  updateRect = updateRect.merge(Rect(x1, y1, x2, y2));
1497  hideSprites(updateRect);
1498 
1499  for (int y = y1; y <= y2; ++y)
1500  rawInvertRow(y, x1, x2);
1501  }
1502 
1503 
1504  template <typename TPreparePixel, typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1505  void genericSwapFGBG(Rect const & rect, Rect & updateRect, TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1506  {
1507  auto penPattern = preparePixel(paintState().penColor);
1508  auto brushPattern = preparePixel(paintState().brushColor);
1509 
1510  int origX = paintState().origin.X;
1511  int origY = paintState().origin.Y;
1512 
1513  const int clipX1 = paintState().absClippingRect.X1;
1514  const int clipY1 = paintState().absClippingRect.Y1;
1515  const int clipX2 = paintState().absClippingRect.X2;
1516  const int clipY2 = paintState().absClippingRect.Y2;
1517 
1518  const int x1 = iclamp(rect.X1 + origX, clipX1, clipX2);
1519  const int y1 = iclamp(rect.Y1 + origY, clipY1, clipY2);
1520  const int x2 = iclamp(rect.X2 + origX, clipX1, clipX2);
1521  const int y2 = iclamp(rect.Y2 + origY, clipY1, clipY2);
1522 
1523  updateRect = updateRect.merge(Rect(x1, y1, x2, y2));
1524  hideSprites(updateRect);
1525 
1526  for (int y = y1; y <= y2; ++y) {
1527  auto row = rawGetRow(y);
1528  for (int x = x1; x <= x2; ++x) {
1529  auto px = rawGetPixelInRow(row, x);
1530  if (px == penPattern)
1531  rawSetPixelInRow(row, x, brushPattern);
1532  else if (px == brushPattern)
1533  rawSetPixelInRow(row, x, penPattern);
1534  }
1535  }
1536  }
1537 
1538 
1539  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1540  void genericCopyRect(Rect const & source, Rect & updateRect, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1541  {
1542  const int clipX1 = paintState().absClippingRect.X1;
1543  const int clipY1 = paintState().absClippingRect.Y1;
1544  const int clipX2 = paintState().absClippingRect.X2;
1545  const int clipY2 = paintState().absClippingRect.Y2;
1546 
1547  int origX = paintState().origin.X;
1548  int origY = paintState().origin.Y;
1549 
1550  int srcX = source.X1 + origX;
1551  int srcY = source.Y1 + origY;
1552  int width = source.X2 - source.X1 + 1;
1553  int height = source.Y2 - source.Y1 + 1;
1554  int destX = paintState().position.X;
1555  int destY = paintState().position.Y;
1556  int deltaX = destX - srcX;
1557  int deltaY = destY - srcY;
1558 
1559  int incX = deltaX < 0 ? 1 : -1;
1560  int incY = deltaY < 0 ? 1 : -1;
1561 
1562  int startX = deltaX < 0 ? destX : destX + width - 1;
1563  int startY = deltaY < 0 ? destY : destY + height - 1;
1564 
1565  updateRect = updateRect.merge(Rect(srcX, srcY, srcX + width - 1, srcY + height - 1));
1566  updateRect = updateRect.merge(Rect(destX, destY, destX + width - 1, destY + height - 1));
1567  hideSprites(updateRect);
1568 
1569  for (int y = startY, i = 0; i < height; y += incY, ++i) {
1570  if (y >= clipY1 && y <= clipY2) {
1571  auto srcRow = rawGetRow(y - deltaY);
1572  auto dstRow = rawGetRow(y);
1573  for (int x = startX, j = 0; j < width; x += incX, ++j) {
1574  if (x >= clipX1 && x <= clipX2)
1575  rawSetPixelInRow(dstRow, x, rawGetPixelInRow(srcRow, x - deltaX));
1576  }
1577  }
1578  }
1579  }
1580 
1581 
1582  template <typename TRawGetRow, typename TRawSetPixelInRow, typename TDataType>
1583  void genericRawDrawBitmap_Native(int destX, int destY, TDataType * data, int width, int X1, int Y1, int XCount, int YCount,
1584  TRawGetRow rawGetRow, TRawSetPixelInRow rawSetPixelInRow)
1585  {
1586  const int yEnd = Y1 + YCount;
1587  const int xEnd = X1 + XCount;
1588  for (int y = Y1; y < yEnd; ++y, ++destY) {
1589  auto dstrow = rawGetRow(destY);
1590  auto src = data + y * width + X1;
1591  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src)
1592  rawSetPixelInRow(dstrow, adestX, *src);
1593  }
1594  }
1595 
1596 
1597 
1598  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1599  void genericRawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1600  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1601  {
1602  const int width = bitmap->width;
1603  const int yEnd = Y1 + YCount;
1604  const int xEnd = X1 + XCount;
1605  auto data = bitmap->data;
1606  const int rowlen = (bitmap->width + 7) / 8;
1607 
1608  if (saveBackground) {
1609 
1610  // save background and draw the bitmap
1611  for (int y = Y1; y < yEnd; ++y, ++destY) {
1612  auto dstrow = rawGetRow(destY);
1613  auto savePx = saveBackground + y * width + X1;
1614  auto src = data + y * rowlen;
1615  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx) {
1616  *savePx = rawGetPixelInRow(dstrow, adestX);
1617  if ((src[x >> 3] << (x & 7)) & 0x80)
1618  rawSetPixelInRow(dstrow, adestX);
1619  }
1620  }
1621 
1622  } else {
1623 
1624  // just draw the bitmap
1625  for (int y = Y1; y < yEnd; ++y, ++destY) {
1626  auto dstrow = rawGetRow(destY);
1627  auto src = data + y * rowlen;
1628  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX) {
1629  if ((src[x >> 3] << (x & 7)) & 0x80)
1630  rawSetPixelInRow(dstrow, adestX);
1631  }
1632  }
1633 
1634  }
1635  }
1636 
1637 
1638  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1639  void genericRawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1640  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1641  {
1642  const int width = bitmap->width;
1643  const int yEnd = Y1 + YCount;
1644  const int xEnd = X1 + XCount;
1645  auto data = bitmap->data;
1646 
1647  if (saveBackground) {
1648 
1649  // save background and draw the bitmap
1650  for (int y = Y1; y < yEnd; ++y, ++destY) {
1651  auto dstrow = rawGetRow(destY);
1652  auto savePx = saveBackground + y * width + X1;
1653  auto src = data + y * width + X1;
1654  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx, ++src) {
1655  *savePx = rawGetPixelInRow(dstrow, adestX);
1656  if (*src & 0xc0) // alpha > 0 ?
1657  rawSetPixelInRow(dstrow, adestX, *src);
1658  }
1659  }
1660 
1661  } else {
1662 
1663  // just draw the bitmap
1664  for (int y = Y1; y < yEnd; ++y, ++destY) {
1665  auto dstrow = rawGetRow(destY);
1666  auto src = data + y * width + X1;
1667  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src) {
1668  if (*src & 0xc0) // alpha > 0 ?
1669  rawSetPixelInRow(dstrow, adestX, *src);
1670  }
1671  }
1672 
1673  }
1674  }
1675 
1676 
1677  template <typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow, typename TBackground>
1678  void genericRawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, TBackground * saveBackground, int X1, int Y1, int XCount, int YCount,
1679  TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1680  {
1681  const int width = bitmap->width;
1682  const int yEnd = Y1 + YCount;
1683  const int xEnd = X1 + XCount;
1684  auto data = (RGBA8888 const *) bitmap->data;
1685 
1686  if (saveBackground) {
1687 
1688  // save background and draw the bitmap
1689  for (int y = Y1; y < yEnd; ++y, ++destY) {
1690  auto dstrow = rawGetRow(destY);
1691  auto savePx = saveBackground + y * width + X1;
1692  auto src = data + y * width + X1;
1693  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++savePx, ++src) {
1694  *savePx = rawGetPixelInRow(dstrow, adestX);
1695  if (src->A)
1696  rawSetPixelInRow(dstrow, adestX, *src);
1697  }
1698  }
1699 
1700  } else {
1701 
1702  // just draw the bitmap
1703  for (int y = Y1; y < yEnd; ++y, ++destY) {
1704  auto dstrow = rawGetRow(destY);
1705  auto src = data + y * width + X1;
1706  for (int x = X1, adestX = destX; x < xEnd; ++x, ++adestX, ++src) {
1707  if (src->A)
1708  rawSetPixelInRow(dstrow, adestX, *src);
1709  }
1710  }
1711 
1712  }
1713  }
1714 
1715 
1716  // Scroll is done copying and filling rows
1717  // scroll < 0 -> scroll UP
1718  // scroll > 0 -> scroll DOWN
1719  template <typename TRawCopyRow, typename TRawFillRow>
1720  void genericVScroll(int scroll, Rect & updateRect,
1721  TRawCopyRow rawCopyRow, TRawFillRow rawFillRow)
1722  {
1723  hideSprites(updateRect);
1724  RGB888 color = getActualBrushColor();
1725  int Y1 = paintState().scrollingRegion.Y1;
1726  int Y2 = paintState().scrollingRegion.Y2;
1727  int X1 = paintState().scrollingRegion.X1;
1728  int X2 = paintState().scrollingRegion.X2;
1729  int height = Y2 - Y1 + 1;
1730 
1731  if (scroll < 0) {
1732 
1733  // scroll UP
1734 
1735  for (int i = 0; i < height + scroll; ++i) {
1736  // copy X1..X2 of (Y1 + i - scroll) to (Y1 + i)
1737  rawCopyRow(X1, X2, (Y1 + i - scroll), (Y1 + i));
1738  }
1739  // fill lower area with brush color
1740  for (int i = height + scroll; i < height; ++i)
1741  rawFillRow(Y1 + i, X1, X2, color);
1742 
1743  } else if (scroll > 0) {
1744 
1745  // scroll DOWN
1746  for (int i = height - scroll - 1; i >= 0; --i) {
1747  // copy X1..X2 of (Y1 + i) to (Y1 + i + scroll)
1748  rawCopyRow(X1, X2, (Y1 + i), (Y1 + i + scroll));
1749  }
1750 
1751  // fill upper area with brush color
1752  for (int i = 0; i < scroll; ++i)
1753  rawFillRow(Y1 + i, X1, X2, color);
1754 
1755  }
1756  }
1757 
1758 
1759  // scroll is done swapping rows and rows pointers
1760  // scroll < 0 -> scroll UP
1761  // scroll > 0 -> scroll DOWN
1762  template <typename TSwapRowsCopying, typename TSwapRowsPointers, typename TRawFillRow>
1763  void genericVScroll(int scroll, Rect & updateRect,
1764  TSwapRowsCopying swapRowsCopying, TSwapRowsPointers swapRowsPointers, TRawFillRow rawFillRow)
1765  {
1766  hideSprites(updateRect);
1767  RGB888 color = getActualBrushColor();
1768  const int Y1 = paintState().scrollingRegion.Y1;
1769  const int Y2 = paintState().scrollingRegion.Y2;
1770  const int X1 = paintState().scrollingRegion.X1;
1771  const int X2 = paintState().scrollingRegion.X2;
1772  const int height = Y2 - Y1 + 1;
1773 
1774  const int viewPortWidth = getViewPortWidth();
1775 
1776  if (scroll < 0) {
1777 
1778  // scroll UP
1779 
1780  for (int i = 0; i < height + scroll; ++i) {
1781 
1782  // these are necessary to maintain invariate out of scrolling regions
1783  if (X1 > 0)
1784  swapRowsCopying(Y1 + i, Y1 + i - scroll, 0, X1 - 1);
1785  if (X2 < viewPortWidth - 1)
1786  swapRowsCopying(Y1 + i, Y1 + i - scroll, X2 + 1, viewPortWidth - 1);
1787 
1788  // swap scan lines
1789  swapRowsPointers(Y1 + i, Y1 + i - scroll);
1790  }
1791 
1792  // fill lower area with brush color
1793  for (int i = height + scroll; i < height; ++i)
1794  rawFillRow(Y1 + i, X1, X2, color);
1795 
1796  } else if (scroll > 0) {
1797 
1798  // scroll DOWN
1799  for (int i = height - scroll - 1; i >= 0; --i) {
1800 
1801  // these are necessary to maintain invariate out of scrolling regions
1802  if (X1 > 0)
1803  swapRowsCopying(Y1 + i, Y1 + i + scroll, 0, X1 - 1);
1804  if (X2 < viewPortWidth - 1)
1805  swapRowsCopying(Y1 + i, Y1 + i + scroll, X2 + 1, viewPortWidth - 1);
1806 
1807  // swap scan lines
1808  swapRowsPointers(Y1 + i, Y1 + i + scroll);
1809  }
1810 
1811  // fill upper area with brush color
1812  for (int i = 0; i < scroll; ++i)
1813  rawFillRow(Y1 + i, X1, X2, color);
1814 
1815  }
1816  }
1817 
1818 
1819 
1820  // Scroll is done copying and filling columns
1821  // scroll < 0 -> scroll LEFT
1822  // scroll > 0 -> scroll RIGHT
1823  template <typename TPreparePixel, typename TRawGetRow, typename TRawGetPixelInRow, typename TRawSetPixelInRow>
1824  void genericHScroll(int scroll, Rect & updateRect,
1825  TPreparePixel preparePixel, TRawGetRow rawGetRow, TRawGetPixelInRow rawGetPixelInRow, TRawSetPixelInRow rawSetPixelInRow)
1826  {
1827  hideSprites(updateRect);
1828  auto pattern = preparePixel(getActualBrushColor());
1829 
1830  int Y1 = paintState().scrollingRegion.Y1;
1831  int Y2 = paintState().scrollingRegion.Y2;
1832  int X1 = paintState().scrollingRegion.X1;
1833  int X2 = paintState().scrollingRegion.X2;
1834 
1835  if (scroll < 0) {
1836  // scroll left
1837  for (int y = Y1; y <= Y2; ++y) {
1838  auto row = rawGetRow(y);
1839  for (int x = X1; x <= X2 + scroll; ++x) {
1840  auto c = rawGetPixelInRow(row, x - scroll);
1841  rawSetPixelInRow(row, x, c);
1842  }
1843  // fill right area with brush color
1844  for (int x = X2 + 1 + scroll; x <= X2; ++x)
1845  rawSetPixelInRow(row, x, pattern);
1846  }
1847  } else if (scroll > 0) {
1848  // scroll right
1849  for (int y = Y1; y <= Y2; ++y) {
1850  auto row = rawGetRow(y);
1851  for (int x = X2 - scroll; x >= X1; --x) {
1852  auto c = rawGetPixelInRow(row, x);
1853  rawSetPixelInRow(row, x + scroll, c);
1854  }
1855  // fill left area with brush color
1856  for (int x = X1; x < X1 + scroll; ++x)
1857  rawSetPixelInRow(row, x, pattern);
1858  }
1859  }
1860  }
1861 
1862 
1863 
1864 
1865 };
1866 
1867 
1868 
1869 } // end of namespace
1870 
1871 
1872 
int16_t X2
Definition: fabutils.h:179
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:180
uint16_t italic
int16_t Y1
Definition: fabutils.h:178
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:177
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:190
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:225
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:208
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.