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