GFXcanvas: Allow subclass to take charge of buffer allocations (#465)

* GFXcanvas: Allow subclass to take charge of buffer allocations

In Adafruit_dvhstx, the low level driver takes charge of the framebuffer
allocations. Consequently, any allocation by the GFXcanvas class would be
an unused waste of memory.

Introduce a new constructor parameter `bool allocate_buffer=true`. The
default preserves the old behavior of allocating the buffer in the
constructor and freeing it in the destructor, while passing in `false`
skips buffer allocation & freeing.

In this case, the subclass must allocate the buffer before the first
drawing operation (such as in its constructor or begin method), and
free it in its own destructor or end method.

The subclass can also freely change the buffer pointer at runtime, for
instance in a swap method if the underlying framebuffer is double-buffered.

* experimentally run a different clang-format

This runs version 19.1.7: uvx clang-format -i *.cpp *.h

the changes look similar to what was shown during CI

* undo one edit that ci doesn't like
This commit is contained in:
Jeff Epler
2025-02-19 10:15:01 -05:00
committed by GitHub
parent 1124b157d5
commit f80daa51c9
4 changed files with 89 additions and 57 deletions
+42 -16
View File
@@ -1758,12 +1758,21 @@ const uint8_t PROGMEM GFXcanvas1::GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF,
@brief Instatiate a GFX 1-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
@param allocate_buffer If true, a buffer is allocated with malloc. If
false, the subclass must initialize the buffer before any drawing operation,
and free it in the destructor. If false (the default), the buffer is
allocated and freed by the library.
*/
/**************************************************************************/
GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = ((w + 7) / 8) * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h, bool allocate_buffer)
: Adafruit_GFX(w, h), buffer_owned(allocate_buffer) {
if (allocate_buffer) {
uint32_t bytes = ((w + 7) / 8) * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
} else {
buffer = nullptr;
}
}
@@ -1773,7 +1782,7 @@ GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
*/
/**************************************************************************/
GFXcanvas1::~GFXcanvas1(void) {
if (buffer)
if (buffer && buffer_owned)
free(buffer);
}
@@ -2111,13 +2120,21 @@ void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
@brief Instatiate a GFX 8-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
@param allocate_buffer If true, a buffer is allocated with malloc. If
false, the subclass must initialize the buffer before any drawing operation,
and free it in the destructor. If false (the default), the buffer is
allocated and freed by the library.
*/
/**************************************************************************/
GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = w * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h, bool allocate_buffer)
: Adafruit_GFX(w, h), buffer_owned(allocate_buffer) {
if (allocate_buffer) {
uint32_t bytes = w * h;
if ((buffer = (uint8_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
} else
buffer = nullptr;
}
/**************************************************************************/
@@ -2126,7 +2143,7 @@ GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
*/
/**************************************************************************/
GFXcanvas8::~GFXcanvas8(void) {
if (buffer)
if (buffer && buffer_owned)
free(buffer);
}
@@ -2379,12 +2396,21 @@ void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w,
@brief Instatiate a GFX 16-bit canvas context for graphics
@param w Display width, in pixels
@param h Display height, in pixels
@param allocate_buffer If true, a buffer is allocated with malloc. If
false, the subclass must initialize the buffer before any drawing operation,
and free it in the destructor. If false (the default), the buffer is
allocated and freed by the library.
*/
/**************************************************************************/
GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
uint32_t bytes = w * h * 2;
if ((buffer = (uint16_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h, bool allocate_buffer)
: Adafruit_GFX(w, h), buffer_owned(allocate_buffer) {
if (allocate_buffer) {
uint32_t bytes = w * h * 2;
if ((buffer = (uint16_t *)malloc(bytes))) {
memset(buffer, 0, bytes);
}
} else {
buffer = nullptr;
}
}
@@ -2394,7 +2420,7 @@ GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) {
*/
/**************************************************************************/
GFXcanvas16::~GFXcanvas16(void) {
if (buffer)
if (buffer && buffer_owned)
free(buffer);
}
+12 -6
View File
@@ -309,7 +309,7 @@ private:
/// A GFX 1-bit canvas context for graphics
class GFXcanvas1 : public Adafruit_GFX {
public:
GFXcanvas1(uint16_t w, uint16_t h);
GFXcanvas1(uint16_t w, uint16_t h, bool allocate_buffer = true);
~GFXcanvas1(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
@@ -328,7 +328,9 @@ protected:
bool getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
///< nothing
private:
#ifdef __AVR__
@@ -340,7 +342,7 @@ private:
/// A GFX 8-bit canvas context for graphics
class GFXcanvas8 : public Adafruit_GFX {
public:
GFXcanvas8(uint16_t w, uint16_t h);
GFXcanvas8(uint16_t w, uint16_t h, bool allocate_buffer = true);
~GFXcanvas8(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
@@ -359,13 +361,15 @@ protected:
uint8_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
uint8_t *buffer; ///< Raster data: no longer private, allow subclass access
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
///< nothing
};
/// A GFX 16-bit canvas context for graphics
class GFXcanvas16 : public Adafruit_GFX {
public:
GFXcanvas16(uint16_t w, uint16_t h);
GFXcanvas16(uint16_t w, uint16_t h, bool allocate_buffer = true);
~GFXcanvas16(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t color);
@@ -385,7 +389,9 @@ protected:
uint16_t getRawPixel(int16_t x, int16_t y) const;
void drawFastRawVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastRawHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint16_t *buffer; ///< Raster data: no longer private, allow subclass access
uint16_t *buffer; ///< Raster data: no longer private, allow subclass access
bool buffer_owned; ///< If true, destructor will free buffer, else it will do
///< nothing
};
#endif // _ADAFRUIT_GFX_H
+23 -23
View File
@@ -879,7 +879,7 @@ void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) {
DMA_ADDRESS_INCREMENT_STEP_SIZE_1;
descriptor[d].DSTADDR.reg = (uint32_t)tft8.writePort;
}
#endif // __SAMD51
#endif // __SAMD51
} // end parallel-specific DMA setup
lastFillColor = 0x0000;
@@ -887,13 +887,13 @@ void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) {
dma.setCallback(dma_callback);
return; // Success!
// else clean up any partial allocation...
} // end descriptor memalign()
} // end descriptor memalign()
free(pixelBuf[0]);
pixelBuf[0] = pixelBuf[1] = NULL;
} // end pixelBuf malloc()
// Don't currently have a descriptor delete function in
// ZeroDMA lib, but if we did, it would be called here.
} // end addDescriptor()
} // end pixelBuf malloc()
// Don't currently have a descriptor delete function in
// ZeroDMA lib, but if we did, it would be called here.
} // end addDescriptor()
dma.free(); // Deallocate DMA channel
}
#endif // end USE_SPI_DMA
@@ -1373,11 +1373,11 @@ void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) {
dma.trigger();
while (dma_busy)
; // Wait for completion
// Unfortunately blocking is necessary. An earlier version returned
// immediately and checked dma_busy on startWrite() instead, but it
// turns out to be MUCH slower on many graphics operations (as when
// drawing lines, pixel-by-pixel), perhaps because it's a volatile
// type and doesn't cache. Working on this.
// Unfortunately blocking is necessary. An earlier version returned
// immediately and checked dma_busy on startWrite() instead, but it
// turns out to be MUCH slower on many graphics operations (as when
// drawing lines, pixel-by-pixel), perhaps because it's a volatile
// type and doesn't cache. Working on this.
#if defined(__SAMD51__) || defined(ARDUINO_SAMD_ZERO)
if (connection == TFT_HARD_SPI) {
// SAMD51: SPI DMA seems to leave the SPI peripheral in a freaky
@@ -2089,9 +2089,9 @@ uint16_t Adafruit_SPITFT::readcommand16(uint16_t addr) {
result = *(volatile uint16_t *)tft8.readPort; // 16-bit read
*(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state
#else // !HAS_PORT_SET_CLR
*(volatile uint16_t *)tft8.portDir = 0x0000; // Input state
result = *(volatile uint16_t *)tft8.readPort; // 16-bit read
*(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state
*(volatile uint16_t *)tft8.portDir = 0x0000; // Input state
result = *(volatile uint16_t *)tft8.readPort; // 16-bit read
*(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state
#endif // end !HAS_PORT_SET_CLR
TFT_RD_HIGH(); // Read line HIGH
endWrite();
@@ -2246,17 +2246,17 @@ uint8_t Adafruit_SPITFT::spiRead(void) {
w = *tft8.readPort; // Read value from port
*tft8.portDir = 0xFF; // Restore port to output
#else // !__AVR__
if (!tft8.wide) { // 8-bit TFT connection
if (!tft8.wide) { // 8-bit TFT connection
#if defined(HAS_PORT_SET_CLR)
*tft8.dirClr = 0xFF; // Set port to input state
w = *tft8.readPort; // Read value from port
*tft8.dirSet = 0xFF; // Restore port to output
*tft8.dirClr = 0xFF; // Set port to input state
w = *tft8.readPort; // Read value from port
*tft8.dirSet = 0xFF; // Restore port to output
#else // !HAS_PORT_SET_CLR
*tft8.portDir = 0x00; // Set port to input state
w = *tft8.readPort; // Read value from port
*tft8.portDir = 0xFF; // Restore port to output
*tft8.portDir = 0x00; // Set port to input state
w = *tft8.readPort; // Read value from port
*tft8.portDir = 0xFF; // Restore port to output
#endif // end HAS_PORT_SET_CLR
} else { // 16-bit TFT connection
} else { // 16-bit TFT connection
#if defined(HAS_PORT_SET_CLR)
*(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state
w = *(volatile uint16_t *)tft8.readPort; // 16-bit read
@@ -2267,7 +2267,7 @@ uint8_t Adafruit_SPITFT::spiRead(void) {
*(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state
#endif // end !HAS_PORT_SET_CLR
}
TFT_RD_HIGH(); // Read line HIGH
TFT_RD_HIGH(); // Read line HIGH
#endif // end !__AVR__
#else // !USE_FAST_PINIO
w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO
+12 -12
View File
@@ -32,8 +32,8 @@
typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
#define USE_FAST_PINIO ///< Use direct PORT register access
#elif defined(ARDUINO_STM32_FEATHER) // WICED
typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED
typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
#elif defined(__arm__)
#if defined(ARDUINO_ARCH_SAMD)
// Adafruit M0, M4
@@ -79,8 +79,8 @@ typedef volatile ADAGFX_PORT_t *PORTreg_t; ///< PORT register type
defined(ADAFRUIT_CIRCUITPLAYGROUND_M0)
#define USE_SPI_DMA ///< Auto DMA
#else
//#define USE_SPI_DMA ///< If set,
// use DMA if available
// #define USE_SPI_DMA ///< If set,
// use DMA if available
#endif
// Another "oops" name -- this now also handles parallel DMA.
// If DMA is enabled, Arduino sketch MUST #include <Adafruit_ZeroDMA.h>
@@ -397,8 +397,8 @@ protected:
PORTreg_t dcPortSet; ///< PORT register for data/command SET
PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR
#else // !HAS_PORT_SET_CLR
PORTreg_t csPort; ///< PORT register for chip select
PORTreg_t dcPort; ///< PORT register for data/command
PORTreg_t csPort; ///< PORT register for chip select
PORTreg_t dcPort; ///< PORT register for data/command
#endif // end HAS_PORT_SET_CLR
#endif // end USE_FAST_PINIO
#if defined(__cplusplus) && (__cplusplus >= 201100)
@@ -448,8 +448,8 @@ protected:
volatile uint32_t *writePort; ///< PORT register for DATA WRITE
volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ
#else
volatile uint8_t *writePort; ///< PORT register for DATA WRITE
volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ
volatile uint8_t *writePort; ///< PORT register for DATA WRITE
volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ
#endif
#if defined(HAS_PORT_SET_CLR)
// Port direction register pointers are always 8-bit regardless of
@@ -508,10 +508,10 @@ protected:
ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command
#endif // end !KINETISK
#else // !HAS_PORT_SET_CLR
ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR)
ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND)
ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR)
ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND)
ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR)
ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND)
ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR)
ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND)
#endif // end HAS_PORT_SET_CLR
#endif // end USE_FAST_PINIO
uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc.