Skip to content

Commit 7ecb1e8

Browse files
authored
Support for 24-bit RGB888 simulator and fonts (#1112)
1 parent a2476b5 commit 7ecb1e8

File tree

10 files changed

+227
-5
lines changed

10 files changed

+227
-5
lines changed

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ sim16-build:
6565
sim16: sim16-build
6666
./lib/Simulator/build16/simulator
6767

68+
.PHONY: sim24-build
69+
sim24-build:
70+
mkdir -p lib/Simulator/build24
71+
cd lib/Simulator/build24 && cmake $(CMAKE_FLAGS) -DDISPLAY_TYPE=DISPLAY_SIM_24BIT -DDISPLAY_WIDTH=600 -DDISPLAY_HEIGHT=600 $(CMAKE_SIM_DISPLAY_OPTIONS) .. && $(MAKE)
72+
73+
.PHONY: sim-run24
74+
sim24: sim24-build
75+
./lib/Simulator/build24/simulator
76+
6877
.PHONY: lldb
6978
lldb: sim-build
7079
lldb lib/Simulator/build/simulator

lib/Simulator/Common/CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC
7272
)
7373

7474
# Conditionally link freetype if we're using 8-bit or 16-bit display
75-
if(DISPLAY_TYPE STREQUAL "DISPLAY_SIM_16BIT" OR DISPLAY_TYPE STREQUAL "DISPLAY_SIM_8BIT")
75+
if(DISPLAY_TYPE STREQUAL "DISPLAY_SIM_24BIT" OR DISPLAY_TYPE STREQUAL "DISPLAY_SIM_16BIT" OR DISPLAY_TYPE STREQUAL "DISPLAY_SIM_8BIT")
7676
# Set FreeType options before adding the subdirectory
7777
set(FT_DISABLE_PNG ON CACHE BOOL "Disable PNG support in FreeType" FORCE)
7878
set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "Disable HarfBuzz support in FreeType" FORCE)
@@ -91,7 +91,11 @@ else()
9191
endif()
9292

9393
# Set compile definitions based on the selected DISPLAY_TYPE
94-
if(DISPLAY_TYPE STREQUAL "DISPLAY_SIM_16BIT")
94+
if(DISPLAY_TYPE STREQUAL "DISPLAY_SIM_24BIT")
95+
target_compile_definitions(${PROJECT_NAME} PUBLIC
96+
CONFIG_DISPLAY_SIM_24BIT=1
97+
)
98+
elseif(DISPLAY_TYPE STREQUAL "DISPLAY_SIM_16BIT")
9599
target_compile_definitions(${PROJECT_NAME} PUBLIC
96100
CONFIG_DISPLAY_SIM_16BIT=1
97101
)

lib/Simulator/native.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
extern PreferencesStore prefs;
3636
extern void UIInnerLoop(RemoteCommand command);
3737

38-
#if CONFIG_DISPLAY_SIM_16BIT
38+
#if CONFIG_DISPLAY_SIM_24BIT
39+
#include "Displays/DisplaySim24Bit.hpp"
40+
extern DisplaySim24Bit myDisplay;
41+
#elif CONFIG_DISPLAY_SIM_16BIT
3942
#include "Displays/DisplaySim16Bit.hpp"
4043
extern DisplaySim16Bit myDisplay;
4144
#elif CONFIG_DISPLAY_SIM_8BIT
@@ -100,6 +103,18 @@ void SimDrawScreen() {
100103
uint8_t g = ((pixel >> 5) & 0x3F) << 2; // 6 bits green
101104
uint8_t b = (pixel & 0x1F) << 3; // 5 bits blue
102105

106+
SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
107+
SDL_RenderDrawPoint(renderer, x, y);
108+
#elif CONFIG_DISPLAY_SIM_24BIT
109+
uint8_t *buffer = myDisplay.buffer().get();
110+
// Calculate the starting index for this pixel (3 bytes per pixel)
111+
size_t idx = (x + y * myDisplay.gfxClass()->width()) * 3;
112+
113+
// Read BGR values directly from the buffer (GFXcanvas24 stores as BGR)
114+
uint8_t b = buffer[idx]; // Blue is first byte
115+
uint8_t g = buffer[idx + 1]; // Green is second byte
116+
uint8_t r = buffer[idx + 2]; // Red is third byte
117+
103118
SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
104119
SDL_RenderDrawPoint(renderer, x, y);
105120
#else

src/Displays/DisplaySim24Bit.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#if CONFIG_DISPLAY_SIM_24BIT
2+
3+
#include "DisplaySim24Bit.hpp"
4+
5+
#include "Misc/Dispatch.hpp"
6+
#include "Misc/Platform.hpp"
7+
#include "config.h"
8+
9+
auto DisplaySim24Bit::setup() -> bool {
10+
DisplaySystem::setup();
11+
12+
if (renderQueue_ == nullptr) {
13+
renderQueue_ = xQueueCreate(1, sizeof(uint8_t));
14+
}
15+
xQueueReset(renderQueue_);
16+
17+
return true;
18+
}
19+
20+
void DisplaySim24Bit::updateDisplay(DisplaySystem::UpdateType updateType, bool clearFrameBuffer,
21+
int vcomNudge, DisplaySystem::UpdateType altUpdateType,
22+
ScreenSide screen, SolRect altRect) {
23+
auto queue = DispatchGetUiQueue(false);
24+
if (queue == nullptr || !queue->isInQueue()) {
25+
log_w("updateDisplay called from outside the UI queue.");
26+
}
27+
28+
// Double buffer so we can start writing the next frame buffer while this one is getting drawn
29+
memcpy(buffer_.get(), gfxCanvas_.getBuffer(), gfxCanvas_.width() * gfxCanvas_.height() * 3);
30+
31+
if (!skipSignalling_) {
32+
uint8_t in = 1;
33+
xQueueSend(renderQueue_, &in, portMAX_DELAY);
34+
}
35+
36+
// Clear buffer afterwards if argument not set to false.
37+
if (clearFrameBuffer) {
38+
fillScreen(bg);
39+
}
40+
41+
fullRefreshNext_ = false;
42+
}
43+
44+
#endif

src/Displays/DisplaySim24Bit.hpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#pragma once
2+
3+
#include <freertos/FreeRTOS.h>
4+
#include <freertos/queue.h>
5+
6+
#include "DisplaySystem.hpp"
7+
#include "sindarin-debug.h"
8+
9+
class DisplaySim24Bit : public DisplaySystem {
10+
public:
11+
DisplaySim24Bit(uint16_t w, uint16_t h) noexcept : gfxCanvas_(w, h), width_(w), height_(h) {
12+
gfxClass_ = &gfxCanvas_;
13+
buffer_ = std::make_unique<uint8_t[]>(w * h * 3); // 3 bytes per pixel for 24-bit color
14+
};
15+
16+
DisplaySim24Bit(DisplaySim24Bit const &) = delete;
17+
18+
~DisplaySim24Bit() override {
19+
if (renderQueue_ != nullptr) {
20+
vQueueDelete(renderQueue_);
21+
}
22+
};
23+
24+
[[nodiscard]] auto defaultTextInsets() const -> DisplayInset override {
25+
return {static_cast<uint16_t>(30 * nativeWidth() / 256),
26+
static_cast<uint16_t>(30 * nativeHeight() / 256),
27+
static_cast<uint16_t>(20 * nativeWidth() / 256),
28+
static_cast<uint16_t>(20 * nativeHeight() / 256)};
29+
};
30+
[[nodiscard]] auto minTextInsets() const -> DisplayInset override { return {5, 5, 5, 5}; };
31+
[[nodiscard]] auto minTextBoxWidth() const -> int16_t override { return 125; };
32+
[[nodiscard]] auto minTextBoxHeight() const -> int16_t override { return 45; };
33+
34+
auto setup() -> bool override;
35+
36+
void updateDisplay(DisplaySystem::UpdateType updateType = UNSPECIFIED,
37+
bool clearFrameBuffer = true, int vcomNudge = 0,
38+
DisplaySystem::UpdateType altUpdateType = UNSPECIFIED,
39+
ScreenSide screen = ScreenSide::Both,
40+
SolRect altRect = {{0, 0}, {0, 0}}) override;
41+
42+
// Simulator-specific methodds
43+
auto renderQueue() -> QueueHandle_t override { return renderQueue_; };
44+
45+
auto buffer() -> std::unique_ptr<uint8_t[]> & override { return buffer_; };
46+
47+
auto getSkipSignalling() -> bool override { return skipSignalling_; };
48+
49+
void setSkipSignalling(bool skip) override { skipSignalling_ = skip; };
50+
51+
protected:
52+
[[nodiscard]] auto nativeWidth() const -> uint16_t override { return width_; }
53+
[[nodiscard]] auto nativeHeight() const -> uint16_t override { return height_; }
54+
55+
[[nodiscard]] auto getBuffer(void) -> uint8_t * override {
56+
return reinterpret_cast<uint8_t *>(gfxCanvas_.getBuffer());
57+
}
58+
59+
GFXcanvas24 gfxCanvas_;
60+
uint16_t width_;
61+
uint16_t height_;
62+
std::unique_ptr<uint8_t[]> buffer_;
63+
bool skipSignalling_ = false;
64+
bool fullRefreshNext_ = false;
65+
QueueHandle_t renderQueue_ = nullptr;
66+
};

src/UI/Fonts/FontDefs.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct Pos {
2121
};
2222

2323
typedef uint8_t *MemoryPtr;
24-
enum class PixelResolution : uint8_t { ONE_BIT, EIGHT_BITS, SIXTEEN_BITS };
24+
enum class PixelResolution : uint8_t { ONE_BIT, EIGHT_BITS, SIXTEEN_BITS, TWENTYFOUR_BITS };
2525

2626
// For 8-bit screen:
2727
//
@@ -38,6 +38,9 @@ enum class PixelResolution : uint8_t { ONE_BIT, EIGHT_BITS, SIXTEEN_BITS };
3838
#if CONFIG_DISPLAY_SIM_16BIT
3939
const constexpr PixelResolution DEFAULT_DISPLAY_PIXEL_RESOLUTION = PixelResolution::SIXTEEN_BITS;
4040
const constexpr PixelResolution DEFAULT_FONT_PIXEL_RESOLUTION = PixelResolution::SIXTEEN_BITS;
41+
#elif CONFIG_DISPLAY_SIM_24BIT
42+
const constexpr PixelResolution DEFAULT_DISPLAY_PIXEL_RESOLUTION = PixelResolution::TWENTYFOUR_BITS;
43+
const constexpr PixelResolution DEFAULT_FONT_PIXEL_RESOLUTION = PixelResolution::TWENTYFOUR_BITS;
4144
#elif CONFIG_DISPLAY_SIM_8BIT
4245
const constexpr PixelResolution DEFAULT_DISPLAY_PIXEL_RESOLUTION = PixelResolution::EIGHT_BITS;
4346
const constexpr PixelResolution DEFAULT_FONT_PIXEL_RESOLUTION = PixelResolution::EIGHT_BITS;
@@ -58,6 +61,7 @@ const constexpr PixelResolution DEFAULT_FONT_PIXEL_RESOLUTION = PixelResolution:
5861
const constexpr bool PIXEL_RESOLUTION_OK =
5962
((DEFAULT_DISPLAY_PIXEL_RESOLUTION == PixelResolution::SIXTEEN_BITS) ||
6063
(DEFAULT_DISPLAY_PIXEL_RESOLUTION == PixelResolution::EIGHT_BITS) ||
64+
(DEFAULT_DISPLAY_PIXEL_RESOLUTION == PixelResolution::TWENTYFOUR_BITS) ||
6165
(DEFAULT_FONT_PIXEL_RESOLUTION == PixelResolution::ONE_BIT));
6266

6367
#if CONFIG_FONT_IBMF

src/UI/Fonts/Fonts.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ TTFNotoSansLight mainFont;
102102
Font fontFace0 = Font(mainFont, 8);
103103
Font fontFace1 = Font(mainFont, 10);
104104
Font fontFace2 = Font(mainFont, 12);
105+
#elif CONFIG_DISPLAY_SIM_24BIT
106+
Font fontFace0 = Font(mainFont, 8);
107+
Font fontFace1 = Font(mainFont, 10);
108+
Font fontFace2 = Font(mainFont, 12);
105109
#elif CONFIG_DISPLAY_SIM_8BIT
106110
Font fontFace0 = Font(mainFont, 8);
107111
Font fontFace1 = Font(mainFont, 10);

src/UI/Fonts/TTFDriver/TTFFont.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ auto Font::ligKern(const GlyphCode glyphCode1, GlyphCode *glyphCode2, FIX16 *ker
7676
* versa)
7777
*
7878
* For 1-bit font resolution:
79+
* - With 24-bit display: Converts to RGB format (0 or 0xFF, 0xFF, 0xFF)
7980
* - With 16-bit display: Converts to RGB565 format (0 or 0xFFFF)
8081
* - With 8-bit display: Converts to grayscale (0 or 0xFF)
8182
* - With 1-bit display: Performs bit-by-bit copy with proper masking
8283
*
8384
* For 8-bit font resolution:
85+
* - With 24-bit display: Converts grayscale to RGB format
8486
* - With 16-bit display: Converts grayscale to RGB565 format
8587
* - With 8-bit display: Direct grayscale copy
8688
*
@@ -127,6 +129,48 @@ void Font::copyBitmap(Bitmap &to, const Bitmap &from, Pos atPos, bool inverted)
127129
}
128130
}
129131
}
132+
} else if (displayPixelResolution_ == PixelResolution::TWENTYFOUR_BITS) {
133+
uint8_t data;
134+
auto fromPtr = from.pixels;
135+
auto toPtr = to.pixels + static_cast<size_t>((atPos.y * to.pitch) * 3);
136+
137+
for (uint16_t fromRow = 0; fromRow < from.dim.height;
138+
fromRow++, toPtr += to.pitch * 3, fromPtr += from.pitch) {
139+
int toIdx = atPos.x * 3;
140+
int fromIdx = 0;
141+
uint8_t fromMask = 0;
142+
if (inverted) {
143+
for (uint16_t i = 0; i < from.dim.width; i++) {
144+
if (fromMask == 0) {
145+
fromMask = 0x80;
146+
data = fromPtr[fromIdx++];
147+
}
148+
if (data & fromMask) {
149+
// Set RGB to white (0xFF, 0xFF, 0xFF)
150+
toPtr[toIdx] = 0xFF; // R
151+
toPtr[toIdx + 1] = 0xFF; // G
152+
toPtr[toIdx + 2] = 0xFF; // B
153+
}
154+
fromMask >>= 1;
155+
toIdx += 3;
156+
}
157+
} else {
158+
for (uint16_t i = 0; i < from.dim.width; i++) {
159+
if (fromMask == 0) {
160+
fromMask = 0x80;
161+
data = fromPtr[fromIdx++];
162+
}
163+
if (data & fromMask) {
164+
// Set RGB to black (0, 0, 0)
165+
toPtr[toIdx] = 0; // R
166+
toPtr[toIdx + 1] = 0; // G
167+
toPtr[toIdx + 2] = 0; // B
168+
}
169+
fromMask >>= 1;
170+
toIdx += 3;
171+
}
172+
}
173+
}
130174
} else if (displayPixelResolution_ == PixelResolution::EIGHT_BITS) {
131175
uint8_t data;
132176
auto fromPtr = from.pixels;
@@ -234,6 +278,35 @@ void Font::copyBitmap(Bitmap &to, const Bitmap &from, Pos atPos, bool inverted)
234278
fromPtr += from.pitch;
235279
toPtr += to.pitch;
236280
}
281+
} else if (displayPixelResolution_ == PixelResolution::TWENTYFOUR_BITS) {
282+
auto rowCount = from.dim.height;
283+
auto fromPtr = from.pixels;
284+
auto toPtr = &to.pixels[(to.pitch * atPos.y + atPos.x) * 3];
285+
while (rowCount-- > 0) {
286+
if (inverted) {
287+
for (uint16_t i = 0; i < from.dim.width; i++) {
288+
if (fromPtr[i] != 0) {
289+
// Convert grayscale to RGB
290+
uint8_t val = fromPtr[i];
291+
toPtr[i * 3] = val; // R
292+
toPtr[i * 3 + 1] = val; // G
293+
toPtr[i * 3 + 2] = val; // B
294+
}
295+
}
296+
} else {
297+
for (uint16_t i = 0; i < from.dim.width; i++) {
298+
if (fromPtr[i] != 0) {
299+
// Convert inverted grayscale to RGB
300+
uint8_t val = 255 - fromPtr[i];
301+
toPtr[i * 3] = val; // R
302+
toPtr[i * 3 + 1] = val; // G
303+
toPtr[i * 3 + 2] = val; // B
304+
}
305+
}
306+
}
307+
fromPtr += from.pitch;
308+
toPtr += to.pitch * 3;
309+
}
237310
} else if (displayPixelResolution_ == PixelResolution::EIGHT_BITS) {
238311
auto rowCount = from.dim.height;
239312
auto fromPtr = from.pixels;

src/main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
#if CONFIG_DISPLAY_EINKET013TT1
4747
#include "Displays/DisplayEinkET013TT1.hpp"
4848
DisplayEinkET013TT1 myDisplay;
49+
#elif CONFIG_DISPLAY_SIM_24BIT
50+
#include "Displays/DisplaySim24Bit.hpp"
51+
DisplaySim24Bit myDisplay(CONFIG_DISPLAY_WIDTH, CONFIG_DISPLAY_HEIGHT);
4952
#elif CONFIG_DISPLAY_SIM_16BIT
5053
#include "Displays/DisplaySim16Bit.hpp"
5154
DisplaySim16Bit myDisplay(CONFIG_DISPLAY_WIDTH, CONFIG_DISPLAY_HEIGHT);

0 commit comments

Comments
 (0)