Skip to content

Commit b8343a8

Browse files
committed
#95 palette based images on TFT_eSPI
1 parent 5b361fe commit b8343a8

File tree

7 files changed

+161
-41
lines changed

7 files changed

+161
-41
lines changed

examples/esp/esp32Amplifier/app_icondata.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
#define TCMENU_ESPAMPLIFIER_ICONDATA_H
33

44
#include <Arduino.h>
5+
// To use palette and size constants we need to use tcgfx types
6+
#include <graphics/DrawingPrimitives.h>
7+
8+
using namespace tcgfx;
59

610
#define APPICONS_WIDTH 40
711
#define APPICONS_HEIGHT 40
@@ -25,6 +29,46 @@ static unsigned char statusIcon40Bits[] PROGMEM = {
2529
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2630
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
2731

32+
// PALETTE_4BPP width=31, height=40, size=640
33+
// auto size = Coord(31, 40);
34+
const color_t statusBitmap_palette0[] PROGMEM { RGB(48,48,218), RGB(26,107,176), RGB(14,55,120), RGB(62,70,89), RGB(158,151,110), RGB(32,34,101), RGB(217,190,50), RGB(161,85,76), RGB(229,164,27), RGB(10,41,124), RGB(135,37,108), RGB(250,91,21), RGB(133,191,216), RGB(38,184,248), RGB(233,30,101), RGB(50,204,103) };
35+
const uint8_t statusBitmap0[] PROGMEM = {
36+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
37+
0x00,0x00,0x01,0x22,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x44,
38+
0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x34,0x66,0x63,0x20,0x00,0x00,
39+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x46,0x47,0x86,0x59,0x00,0x00,0x00,0x00,0x00,0x00,
40+
0x00,0x00,0x00,0x00,0x00,0x03,0x66,0x55,0x78,0xa1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x11,0x11,
41+
0x11,0x13,0x68,0x33,0x88,0x72,0x11,0x11,0x11,0x19,0x00,0x00,0x09,0x55,0x55,0x55,0x55,0x54,0x68,0x88,
42+
0x8b,0x75,0x55,0x55,0x55,0x99,0x50,0x00,0x09,0xcc,0x35,0x11,0x11,0x56,0x88,0x88,0xbb,0xb3,0x21,0x11,
43+
0x35,0xc7,0x50,0x00,0x09,0xcc,0x5c,0xcc,0xcc,0x57,0x77,0x77,0x77,0x75,0xcc,0xcc,0xc1,0x3b,0x90,0x00,
44+
0x09,0xcc,0x5c,0xdd,0xdd,0x12,0x22,0x22,0x22,0x22,0xdd,0xdd,0xd1,0x5b,0x90,0x00,0x09,0xcc,0x2c,0xdd,
45+
0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xd1,0x5b,0x90,0x00,0x09,0xcc,0x5c,0xdd,0x11,0x11,0x11,0x11,
46+
0x11,0xdd,0x11,0x1d,0xd1,0x3b,0x90,0x00,0x09,0xcc,0x2c,0xd3,0x33,0x33,0x33,0x33,0x33,0x25,0x3a,0x51,
47+
0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xd3,0x88,0x88,0x88,0x88,0x84,0x5a,0xee,0xa2,0xd1,0x3e,0x90,0x00,
48+
0x09,0xcc,0x2c,0xd1,0x22,0x22,0x22,0x22,0x22,0x12,0x22,0x21,0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xdd,
49+
0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0x11,0xdd,0xd1,0x5e,0x90,0x00,0x09,0xcc,0x2c,0xdd,0xd1,0x11,0xdd,0xdd,
50+
0xdd,0xdd,0xdd,0xdd,0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xdd,0x13,0x33,0x2d,0xdd,0xd1,0x22,0x1d,0xdd,
51+
0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xd1,0x36,0x66,0x32,0xdd,0xd2,0x46,0x2d,0xdd,0xd1,0x3e,0x90,0x00,
52+
0x09,0xcc,0x2c,0xd1,0x46,0xff,0xf3,0x1d,0x13,0x66,0xf1,0xdd,0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xd9,
53+
0x64,0xff,0xff,0x51,0x24,0x6f,0xf3,0x1d,0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0xd5,0x4f,0xff,0xff,0x45,
54+
0x34,0xff,0xff,0x5d,0xd1,0x3e,0x90,0x00,0x09,0xcc,0x2c,0x2f,0xff,0xff,0xff,0xf2,0xff,0xff,0xff,0x11,
55+
0xd1,0x5e,0x90,0x00,0x09,0xcc,0x2c,0x1d,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xd1,0xd1,0x5e,0x90,0x00,
56+
0x09,0xcc,0x2c,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0x11,0xdd,0xd1,0x5e,0x90,0x00,0x09,0xcc,0x9c,0xdd,
57+
0xdd,0xdd,0xdd,0xdd,0xdd,0xd2,0xcc,0x51,0xd1,0x5e,0x90,0x00,0x09,0xcc,0x9c,0xd1,0x22,0x22,0x22,0x22,
58+
0x22,0x11,0xcc,0x11,0xd1,0x3e,0x90,0x00,0x09,0x1c,0x2c,0xd3,0x66,0x66,0x66,0x66,0x64,0x21,0xdd,0x1d,
59+
0xd1,0x3e,0x90,0x00,0x09,0x11,0x2c,0xd3,0x77,0x77,0x77,0x77,0x77,0x22,0x11,0x11,0xd1,0x3e,0x90,0x00,
60+
0x09,0x1d,0x2c,0xda,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xa2,0xd1,0x3e,0x90,0x00,0x09,0x1d,0x2d,0xda,
61+
0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xe2,0xd1,0x3e,0x90,0x00,0x09,0x1d,0x5d,0xd1,0x99,0x99,0x99,0x99,
62+
0x99,0x99,0x99,0x91,0xd2,0x3e,0x90,0x00,0x09,0x1d,0x21,0x11,0x19,0x29,0x99,0x99,0x99,0x99,0x99,0x91,
63+
0x15,0xae,0x90,0x00,0x09,0x21,0x25,0x22,0x59,0x99,0x99,0x99,0x99,0x99,0x99,0x95,0x55,0xaa,0x50,0x00,
64+
0x09,0x55,0x55,0x55,0x59,0x99,0x99,0x55,0x59,0x99,0x95,0x55,0x55,0x55,0x90,0x00,0x00,0x00,0x00,0x09,
65+
0x25,0x55,0x55,0x05,0x59,0x99,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x55,0x55,0x00,
66+
0x55,0x55,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x22,0x22,0x00,0x99,0x99,0x99,0x00,
67+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
68+
69+
};
70+
71+
2872
static unsigned char settingsIcon40Bits[] = {
2973
0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xf8, 0x80, 0x1f, 0x00, 0x00, 0xfc,
3074
0xc1, 0x3f, 0x00, 0x00, 0xfe, 0xc1, 0x3f, 0x00, 0x00, 0xfe, 0xc3, 0x3f,

examples/esp/esp32Amplifier/esp32Amplifier.emf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@
685685
"menuDefinitions": []
686686
},
687687
"packageNamespace": "",
688-
"appIsModular": false
688+
"appIsModular": false,
689+
"listOfEmbeddedForms": []
689690
},
690691
"stringLists": []
691692
}

examples/esp/esp32Amplifier/esp32Amplifier.ino

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "app_icondata.h"
2424
#include "TestingDialogController.h"
2525
#include <extras/DrawableTouchCalibrator.h>
26+
#include <graphics/TcThemeBuilder.h>
2627

2728
#define MENU_USING_CALIBRATION_MGR true
2829

@@ -81,29 +82,41 @@ void setup() {
8182
#else
8283
touchScreen.calibrateMinMaxValues(0.250F, 0.890F, 0.09F, 0.88F);
8384
#endif // TC_TFT_ESPI_NEEDS_TOUCH
84-
// first we get the graphics factory
85-
auto & factory = renderer.getGraphicsPropertiesFactory();
86-
87-
// now we add the icons that we want to use with certain menu items
85+
/**
86+
* Here we use the theme builder to modify the drawing of certain menu items, we want three of the main menu
87+
* items to render using icons, we therefore request a "menuItemOverride" from the theme and then we simply
88+
* say the item will render as an image.
89+
*/
90+
TcThemeBuilder themeBuilder(renderer);
8891
const Coord iconSize(APPICONS_WIDTH, APPICONS_HEIGHT);
89-
factory.addImageToCache(DrawableIcon(menuSettings.getId(), iconSize, DrawableIcon::ICON_XBITMAP, settingsIcon40Bits));
90-
factory.addImageToCache(DrawableIcon(menuStatus.getId(), iconSize, DrawableIcon::ICON_XBITMAP, statusIcon40Bits));
91-
factory.addImageToCache(DrawableIcon(menuMute.getId(), iconSize, DrawableIcon::ICON_XBITMAP, muteOffIcon40Bits, muteOnIcon40Bits));
92-
93-
// and now we define that row 3 of the main menu will have three columns, drawn as icons
94-
factory.addGridPosition(&menuSettings, GridPosition(GridPosition::DRAW_AS_ICON_ONLY,
95-
GridPosition::JUSTIFY_CENTER_NO_VALUE, 3, 1, 4, 49));
96-
factory.addGridPosition(&menuStatus, GridPosition(GridPosition::DRAW_AS_ICON_ONLY,
97-
GridPosition::JUSTIFY_CENTER_NO_VALUE, 3, 2, 4, 49));
98-
factory.addGridPosition(&menuMute, GridPosition(GridPosition::DRAW_AS_ICON_ONLY,
99-
GridPosition::JUSTIFY_CENTER_NO_VALUE, 3, 3, 4, 49));
100-
101-
// here is how we completely redefine the drawing of a specific item, you can also define for submenu or default
92+
themeBuilder.menuItemOverride(menuSettings)
93+
.withImage4bpp(Coord(31, 40), statusBitmap_palette0, statusBitmap0)
94+
.onRow(3).multiCol(1, 3)
95+
.apply();
96+
//themeBuilder.menuItemOverride(menuSettings)
97+
// .withImageXbmp(iconSize, settingsIcon40Bits)
98+
// .onRow(3).multiCol(1, 3)
99+
// .apply();
100+
themeBuilder.menuItemOverride(menuStatus)
101+
.withImageXbmp(iconSize, statusIcon40Bits)
102+
.onRow(3).multiCol(2, 3)
103+
.apply();
104+
themeBuilder.menuItemOverride(menuMute)
105+
.withImageXbmp(iconSize, muteOffIcon40Bits, muteOnIcon40Bits)
106+
.onRow(3).multiCol(3, 3)
107+
.apply();
108+
109+
/**
110+
* here is how we override drawing for items only when a submenu is active, you can also define at the item level.
111+
*/
102112
color_t specialPalette[] { RGB(255, 255, 255), RGB(255, 0, 0), RGB(0, 0, 0), RGB(0, 0, 255) };
103-
factory.setDrawingPropertiesAllInSub(ItemDisplayProperties::COMPTYPE_TITLE, menuStatus.getId(), specialPalette,
104-
MenuPadding(4), nullptr, 4, 10, 36,
105-
GridPosition::JUSTIFY_CENTER_WITH_VALUE , MenuBorder(2));
106-
tcgfx::ConfigurableItemDisplayPropertiesFactory::refreshCache();
113+
themeBuilder.submenuPropertiesTitleOverride(menuStatus)
114+
.withPalette(specialPalette)
115+
.withPadding(MenuPadding(4))
116+
.withBorder(MenuBorder(2))
117+
.apply();
118+
119+
themeBuilder.apply();
107120

108121
setTitlePressedCallback([](int) {
109122
BaseDialog* dlg = MenuRenderer::getInstance()->getDialog();

examples/esp/esp32Amplifier/esp32Amplifier_menu.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ void setupMenu() {
105105
menuConnectivityAuthenticator.setLocalOnly(true);
106106
menuConnectivityIoTMonitor.setLocalOnly(true);
107107

108-
// Code generated by plugins.
108+
// Code generated by plugins and new operators.
109109
tft.begin();
110110
tft.setRotation(1);
111111
renderer.setUpdatesPerSecond(10);

examples/esp/esp32Amplifier/tcMenuTfteSpi.cpp

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ void TfteSpiDrawable::internalDrawText(const Coord &where, const void *font, int
4242
void TfteSpiDrawable::drawBitmap(const Coord &where, const DrawableIcon *icon, bool selected) {
4343
if(icon->getIconType() == DrawableIcon::ICON_XBITMAP) {
4444
tft->drawXBitmap(where.x, where.y, icon->getIcon(selected), icon->getDimensions().x, icon->getDimensions().y, drawColor, backgroundColor);
45-
}
46-
else if(icon->getIconType() == DrawableIcon::ICON_MONO) {
45+
} else if(icon->getIconType() == DrawableIcon::ICON_MONO) {
4746
tft->drawBitmap(where.x, where.y, icon->getIcon(selected), icon->getDimensions().x, icon->getDimensions().y, drawColor, backgroundColor);
48-
}
49-
else if(icon->getIconType() == DrawableIcon::ICON_NATIVE) {
47+
} else if(icon->getIconType() == DrawableIcon::ICON_NATIVE) {
5048
tft->pushImage(where.x, where.y, icon->getDimensions().x, icon->getDimensions().y, (const uint16_t*)icon->getIcon(selected));
49+
} else if(icon->getPalette() != nullptr) {
50+
auto bpp = icon->getIconType() == tcgfx::DrawableIcon::ICON_PALLETE_2BPP ? 2 : 4;
51+
drawBitmapNbpp(where, icon->getIcon(selected), icon->getDimensions(), bpp, icon->getPalette());
5152
}
5253
}
5354

@@ -167,6 +168,77 @@ void TftSpriteAndConfig::transaction(bool isStarting, bool redrawNeeded) {
167168
}
168169
}
169170

171+
void TftSpriteAndConfig::drawBitmapNbpp(const Coord& where, const uint8_t* data, const Coord& size, int bpp, const uint16_t* palette) {
172+
auto yTot = int16_t(where.y + size.y);
173+
auto xTot = int16_t(where.x + size.x);
174+
int bitsInByte = bpp == 2 ? 4 : 2;
175+
uint8_t downShift = bpp == 2 ? 6 : 4;
176+
177+
uint8_t byteIteration = bitsInByte;
178+
uint8_t current;
179+
for(int16_t y = where.y; y<yTot; y++) {
180+
for(int16_t x = where.x; x<xTot; x++) {
181+
if(byteIteration == bitsInByte) {
182+
current = pgm_read_byte(data);
183+
data += 1;
184+
byteIteration = 0;
185+
}
186+
uint8_t idx = current >> downShift;
187+
current = current << bitsInByte;
188+
byteIteration++;
189+
sprite.drawPixel(x, y, idx);
190+
}
191+
byteIteration = bitsInByte; // always need a new byte in this case
192+
}
193+
}
194+
195+
#ifndef COOKIE_CUT_MEMBUFFER_SIZE
196+
#define COOKIE_CUT_MEMBUFFER_SIZE 32
197+
#endif
198+
uint16_t memBuffer[COOKIE_CUT_MEMBUFFER_SIZE];
199+
200+
void TfteSpiDrawable::drawBitmapNbpp(const Coord& where, const uint8_t* data, const Coord& size, int bpp, const color_t* palette) {
201+
auto yTot = int16_t(where.y + size.y);
202+
auto xTot = int16_t(where.x + size.x);
203+
int bitsInByte = bpp == 2 ? 4 : 2;
204+
uint8_t downShift = bpp == 2 ? 6 : 4;
205+
206+
uint16_t next = 0;
207+
uint8_t byteIteration = bitsInByte;
208+
uint8_t current;
209+
210+
tft->startWrite();
211+
212+
for(int16_t y = where.y; y<yTot; y++) {
213+
tft->setAddrWindow(where.x, y, size.x, 1);
214+
for(int16_t x = where.x; x<xTot; x++) {
215+
if(byteIteration == bitsInByte) {
216+
current = pgm_read_byte(data);
217+
data += 1;
218+
byteIteration = 0;
219+
}
220+
uint8_t idx = current >> downShift;
221+
current = current << bpp;
222+
byteIteration++;
223+
224+
memBuffer[next] = palette[idx];
225+
next = next + 1;
226+
if(next == COOKIE_CUT_MEMBUFFER_SIZE) {
227+
tft->pushColors(memBuffer, next);
228+
next = 0;
229+
}
230+
}
231+
if(next != 0) {
232+
tft->pushColors(memBuffer, next);
233+
next = 0;
234+
}
235+
byteIteration = bitsInByte; // always need a new byte in this case
236+
}
237+
238+
tft->endWrite();
239+
}
240+
241+
170242
#if TC_TFT_ESPI_NEEDS_TOUCH == true
171243

172244
TouchState TftSpiTouchInterrogator::internalProcessTouch(float *ptrX, float *ptrY, const TouchOrientationSettings& rotation, const iotouch::CalibrationHandler& calib) {

examples/esp/esp32Amplifier/tcMenuTfteSpi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class TfteSpiDrawable : public DeviceDrawable {
4141

4242
void drawXBitmap(const Coord &where, const Coord &size, const uint8_t *data) override;
4343

44+
virtual void drawBitmapNbpp(const Coord& where, const uint8_t* data, const Coord& size, int bpp, const color_t* palette);
45+
4446
void drawBox(const Coord &where, const Coord &size, bool filled) override;
4547

4648
void drawCircle(const Coord &where, int radius, bool filled) override;
@@ -75,6 +77,8 @@ class TftSpriteAndConfig : public TfteSpiDrawable {
7577
DeviceDrawable *getSubDeviceFor(const Coord &where, const Coord& size, const color_t *palette, int paletteSize) override { return nullptr; }
7678
void transaction(bool isStarting, bool redrawNeeded) override;
7779
color_t getUnderlyingColor(color_t col) override;
80+
void drawBitmapNbpp(const Coord& where, const uint8_t* data, const Coord& size, int bpp, const color_t* palette) override;
81+
TFT_eSprite& getTftSprite() { return sprite; }
7882
};
7983

8084
#define TC_TFT_ESPI_NEEDS_TOUCH false

src/graphics/TcThemeBuilder.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
namespace tcgfx {
2020
class TcThemeBuilder;
21-
typedef void (*RowBuilderCallback)(TcThemeBuilder& themeBuilder);
2221

2322
/**
2423
* Represents a set of theme properties that can be applied at any level, if at the item level, this class can also
@@ -304,8 +303,6 @@ namespace tcgfx {
304303
*/
305304
explicit TcThemeBuilder(GraphicsDeviceRenderer& renderer) : renderer(renderer), factory(renderer.getGraphicsPropertiesFactory()),
306305
propertiesBuilder(this) {
307-
auto *drawable = renderer.getDeviceDrawable();
308-
renderer.setDisplayDimensions(drawable->getDisplayDimensions().x, drawable->getDisplayDimensions().y);
309306
}
310307

311308
/**
@@ -493,17 +490,6 @@ namespace tcgfx {
493490
*/
494491
TcThemeBuilder& withPalette(const color_t* cols);
495492

496-
/**
497-
* With this option you conifugre the row and column count once and then within the provided closure you
498-
* only need to provide the column for each item override. Instead of this you can also use the
499-
* `onRow(row).onCol(col, colCount)` way of configuring instead. Either option is equal in end state.
500-
* @param row the row on which the items will appear
501-
* @param colCount the number of columns on that row
502-
* @param callback a callback that will call `menuItemOverride` colCount times for each column
503-
* @return reference to itself for chaining
504-
*/
505-
TcThemeBuilder& defineRowWithCols(int row, int colCount, RowBuilderCallback callback);
506-
507493
/**
508494
* Set up the core rendering settings, that is the way the title should be drawn, and also if analog sliders
509495
* should be used to represent analog items.

0 commit comments

Comments
 (0)