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