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