14#include "cdc_image/JpegEncoder.h"
32constexpr uint16_t SURFACE_MAX_W = 1024;
33constexpr uint16_t SURFACE_MAX_H = 2048;
34constexpr uint16_t INK = 1;
37 std::unique_ptr<cdc::ui::MonoSurface> surf;
38 void* plugin =
nullptr;
41 uint8_t text_size = 1;
43 bool inverted =
false;
48constexpr uint8_t MAX_SLOTS = 8;
49SurfaceSlot s_slots[MAX_SLOTS];
50uint32_t s_next_handle = 1;
52SurfaceSlot* find_slot(uint32_t handle) {
53 if (handle == 0)
return nullptr;
55 for (
auto& s : s_slots) {
56 if (s.surf && s.handle == handle && s.plugin == plugin)
return &s;
62const GFXfont* apply_text_state(SurfaceSlot& s) {
64 s.surf->setFont(font);
65 s.surf->setTextSize(s.text_size);
66 s.surf->setTextColor(s.inverted ? 0 : INK);
67 s.surf->setTextWrap(
false);
76 if (w == 0 || h == 0 || w > SURFACE_MAX_W || h > SURFACE_MAX_H) {
79 const size_t bytes =
static_cast<size_t>((w + 7) / 8) * h;
86 for (
const auto& s : s_slots) {
87 if (s.surf && s.plugin == plugin) owned++;
91 for (
auto& s : s_slots) {
93 auto surf = std::make_unique<cdc::ui::MonoSurface>(w, h);
95 s.surf = std::move(surf);
97 s.handle = s_next_handle++;
102 return static_cast<int>(s.handle);
108 SurfaceSlot* s = find_slot(surface);
115 SurfaceSlot* s = find_slot(surface);
122 SurfaceSlot* s = find_slot(surface);
125 s->font_id = font_id;
130 SurfaceSlot* s = find_slot(surface);
138 SurfaceSlot* s = find_slot(surface);
140 s->inverted = inverted != 0;
145 SurfaceSlot* s = find_slot(surface);
152 SurfaceSlot* s = find_slot(surface);
155 const GFXfont* font = apply_text_state(*s);
157 s->surf->setCursor(x, y);
163 const char* text, uint8_t align) {
164 SurfaceSlot* s = find_slot(surface);
167 const GFXfont* font = apply_text_state(*s);
171 if (align == 1 || align == 2) {
175 if (align == 1) draw_x = x + (w -
static_cast<int16_t
>(bw)) / 2;
176 else draw_x = x + w -
static_cast<int16_t
>(bw);
178 s->surf->setCursor(draw_x, y);
184 uint16_t* out_w, uint16_t* out_h) {
185 SurfaceSlot* s = find_slot(surface);
188 const GFXfont* font = apply_text_state(*s);
196 SurfaceSlot* s = find_slot(surface);
198 s->surf->drawPixel(x, y, INK);
203 SurfaceSlot* s = find_slot(surface);
205 s->surf->drawLine(x0, y0, x1, y1, INK);
211 SurfaceSlot* s = find_slot(surface);
214 s->surf->drawRect(x, y, w, h, INK);
215 }
else if (s->shade >= 255) {
216 s->surf->fillRect(x, y, w, h, INK);
217 }
else if (s->shade > 0) {
225 SurfaceSlot* s = find_slot(surface);
228 s->surf->drawCircle(x, y, r, INK);
229 }
else if (s->shade >= 255) {
230 s->surf->fillCircle(x, y, r, INK);
231 }
else if (s->shade > 0) {
238 int16_t x1, int16_t y1, int16_t x2, int16_t y2,
240 SurfaceSlot* s = find_slot(surface);
243 s->surf->drawTriangle(x0, y0, x1, y1, x2, y2, INK);
244 }
else if (s->shade >= 255) {
245 s->surf->fillTriangle(x0, y0, x1, y1, x2, y2, INK);
246 }
else if (s->shade > 0) {
253 int16_t w, int16_t h, int16_t r, uint8_t filled) {
254 SurfaceSlot* s = find_slot(surface);
256 if (filled) s->surf->fillRoundRect(x, y, w, h, r, INK);
257 else s->surf->drawRoundRect(x, y, w, h, r, INK);
262 SurfaceSlot* s = find_slot(surface);
264 s->surf->drawFastHLine(x, y, w, INK);
269 SurfaceSlot* s = find_slot(surface);
271 s->surf->drawFastVLine(x, y, h, INK);
276 int16_t w, int16_t h,
const uint8_t* data, uint32_t len) {
277 SurfaceSlot* s = find_slot(surface);
280 const uint32_t needed =
static_cast<uint32_t
>((w + 7) / 8) *
static_cast<uint32_t
>(h);
282 s->surf->drawBitmap(x, y, data, w, h, INK);
287 uint16_t* out_stride_bytes) {
288 SurfaceSlot* s = find_slot(surface);
291 const size_t bytes = s->surf->byteSize();
293 std::memcpy(out, s->surf->buffer(), bytes);
294 *out_stride_bytes = s->surf->strideBytes();
295 return static_cast<int>(bytes);
299 uint8_t* out,
size_t out_size, uint32_t* out_len) {
300 SurfaceSlot* s = find_slot(surface);
304 int rc = cdc::image::encodeMonoJpeg(s->surf->buffer(),
305 static_cast<uint16_t
>(s->surf->width()),
306 static_cast<uint16_t
>(s->surf->height()),
307 s->surf->strideBytes(), quality,
308 out, out_size, &len);
309 *out_len =
static_cast<uint32_t
>(len);
320 SurfaceSlot* s = find_slot(surface);
334 static_cast<int16_t
>(fv.
w),
335 static_cast<int16_t
>(fv.
h), opts, INK, 0);
340 uint16_t frame_h, uint16_t frame_count) {
341 SurfaceSlot* s = find_slot(surface);
345 const uint16_t surfW =
static_cast<uint16_t
>(s->surf->width());
346 const uint16_t surfH =
static_cast<uint16_t
>(s->surf->height());
347 if (frame_w == 0 || frame_h == 0 || frame_count == 0
348 || frame_w > surfW || frame_h > surfH) {
351 const uint16_t cellsPerRow = surfW / frame_w;
352 const uint16_t rowsNeeded =
static_cast<uint16_t
>(
353 (frame_count + cellsPerRow - 1) / cellsPerRow);
354 if (
static_cast<uint32_t
>(rowsNeeded) * frame_h > surfH) {
360 const uint16_t stride =
static_cast<uint16_t
>((frame_w + 7) / 8);
361 const uint32_t sheetBytes =
static_cast<uint32_t
>(stride) * frame_h * frame_count;
362 auto sheet = std::make_unique<uint8_t[]>(sheetBytes);
364 std::memset(sheet.get(), 0, sheetBytes);
366 const uint8_t* src = s->surf->buffer();
367 const uint16_t srcStride = s->surf->strideBytes();
368 for (uint16_t f = 0; f < frame_count; ++f) {
369 const uint16_t cellX =
static_cast<uint16_t
>((f % cellsPerRow) * frame_w);
370 const uint16_t cellY =
static_cast<uint16_t
>((f / cellsPerRow) * frame_h);
371 uint8_t* dstFrame = sheet.get() +
static_cast<uint32_t
>(f) * stride * frame_h;
372 for (uint16_t yy = 0; yy < frame_h; ++yy) {
373 const uint8_t* srcRow = src +
static_cast<uint32_t
>(cellY + yy) * srcStride;
374 uint8_t* dstRow = dstFrame +
static_cast<uint32_t
>(yy) * stride;
375 for (uint16_t xx = 0; xx < frame_w; ++xx) {
376 uint16_t sx =
static_cast<uint16_t
>(cellX + xx);
377 if (srcRow[sx >> 3] & (0x80 >> (sx & 7))) {
378 dstRow[xx >> 3] |=
static_cast<uint8_t
>(0x80 >> (xx & 7));
384 int32_t handle = c->spriteCreate(frame_w, frame_h, frame_count,
385 sheet.get(), sheetBytes);
386 if (handle > 0)
return static_cast<int>(handle);
392 for (
auto& s : s_slots) {
393 if (s.surf && s.plugin == plugin) s = SurfaceSlot{};
Offscreen 1-bpp Adafruit_GFX render target in PSRAM.
Singleton that owns plugin-pushed UI views (lists, confirms, inputs).
cdc::ui::CanvasView * canvasView()
static PluginUiState & instance() noexcept
int host_sprite_create_from_surface(uint32_t surface, uint16_t frame_w, uint16_t frame_h, uint16_t frame_count)
Create a sprite by slicing a surface into a row-major grid of frame_w x frame_h cells.
int host_surface_set_font(uint32_t surface, uint8_t font_id)
Select the font for subsequent text calls (HOST_FONT_* id).
int host_surface_draw_text(uint32_t surface, int16_t x, int16_t y, const char *text)
Draw UTF-8 text with the current font/size at (x, y) baseline-top.
int host_surface_draw_bitmap(uint32_t surface, int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *data, uint32_t len)
Blit a packed 1-bpp bitmap (MSB-first, set bit = black) at (x, y).
int host_surface_vline(uint32_t surface, int16_t x, int16_t y, int16_t h)
Draw a vertical line of height h.
int host_surface_draw_pixel(uint32_t surface, int16_t x, int16_t y)
Draw a pixel (current shade decides ink: shade > 0 draws black).
int host_surface_draw_sprite(uint32_t surface, int16_t x, int16_t y, uint32_t sprite)
Stamp a sprite's current frame onto a surface at (x, y).
int host_surface_measure_text(uint32_t surface, const char *text, uint16_t *out_w, uint16_t *out_h)
Measure UTF-8 text with the surface's current font/size.
int host_surface_destroy(uint32_t surface)
Destroy a surface and free its memory.
int host_surface_draw_line(uint32_t surface, int16_t x0, int16_t y0, int16_t x1, int16_t y1)
Draw a line.
int host_surface_draw_text_aligned(uint32_t surface, int16_t x, int16_t y, int16_t w, const char *text, uint8_t align)
Draw UTF-8 text aligned within a w-wide box starting at x.
int host_surface_draw_rect(uint32_t surface, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t filled)
Draw a rectangle; filled ones use the current shade.
int host_surface_draw_circle(uint32_t surface, int16_t x, int16_t y, int16_t r, uint8_t filled)
Draw a circle centered at (x, y); filled ones use the current shade.
int host_surface_set_text_color(uint32_t surface, uint8_t inverted)
Draw text white-on-black (inverted != 0) or black-on-white.
int host_surface_draw_triangle(uint32_t surface, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t filled)
Draw a triangle; filled ones use the current shade.
#define HOST_SURFACE_MAX_PER_PLUGIN
Maximum surfaces a plugin may hold at once.
int host_surface_export(uint32_t surface, uint8_t *out, size_t out_size, uint16_t *out_stride_bytes)
Copy the surface's packed pixel rows into out.
int host_surface_set_shade(uint32_t surface, uint8_t shade)
Set the fill shade for filled shapes: 0 white .. 255 solid black.
int host_surface_clear(uint32_t surface)
Clear a surface to white.
int host_surface_create(uint16_t w, uint16_t h)
Create a w x h surface, cleared to white.
#define HOST_SURFACE_MAX_BYTES
Maximum packed pixel bytes per surface (stride * height).
int host_surface_set_text_size(uint32_t surface, uint8_t size)
Set the integer text scale (1..4) for subsequent text calls.
int host_surface_hline(uint32_t surface, int16_t x, int16_t y, int16_t w)
Draw a horizontal line of width w.
int host_surface_draw_round_rect(uint32_t surface, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint8_t filled)
Draw a rounded rectangle with corner radius r.
int host_surface_export_jpg(uint32_t surface, uint8_t quality, uint8_t *out, size_t out_size, uint32_t *out_len)
Encode the surface as a grayscale JPEG.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_INVALID_ARG
#define HOST_ERR_NO_MEMORY
#define HOST_ERR_NOT_FOUND
void * plg_get_active_plugin(void)
void * plg_get_active_plugin(void)
void plg_surface_on_unload(void *plugin)
Internal UTF-8 <-> CP437 helpers for the plugin host API boundary.
std::string toDisplay(const char *utf8)
Decode a UTF-8 (with optional HTML entities) string into CP437 bytes.
@ SPRITE_FLAG_ROT_90
Rotate 90 deg clockwise (before flips).
@ SPRITE_FLAG_OPAQUE
Unset data bits paint white.
void drawText(Adafruit_GFX *gfx, const char *text, const GFXfont *font)
Draws CP437-encoded text correctly for the given font: the built-in glcdfont (font == nullptr) is CP4...
void drawBitmapMasked(Adafruit_GFX *gfx, int16_t x, int16_t y, const uint8_t *data, int16_t w, int16_t h, const BlitOpts &opts, uint16_t fg, uint16_t bg)
Blit a packed 1-bpp bitmap with optional mask plane, flipping, 90-degree rotation,...
void fillTriangleDither(Adafruit_GFX *gfx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t shade, uint16_t color)
Dithered filled triangle; see fillRectDither.
void fillCircleDither(Adafruit_GFX *gfx, int16_t cx, int16_t cy, int16_t r, uint8_t shade, uint16_t color)
Dithered filled circle; see fillRectDither.
void measureText(Adafruit_GFX *gfx, const char *text, const GFXfont *font, int16_t x0, int16_t y0, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
Measures CP437 text exactly as drawText would render it with font, so width-based layout (centering,...
void fillRectDither(Adafruit_GFX *gfx, int16_t x, int16_t y, int16_t w, int16_t h, uint8_t shade, uint16_t color)
Fills a rectangle with an ordered-dither fake-grey pattern.
const GFXfont * getGfxFont(FontId id)
Resolves a FontId to its underlying GFX font pointer.
constexpr uint8_t kFontIdCount
Number of entries in the canonical font table.
Replay accessors: geometry, flags and pixel pointers of a sprite.
const uint8_t * data
Current frame pixels.
const uint8_t * mask
Mask plane or null.
Options for drawBitmapMasked. Zero-init = plain transparent blit.
uint8_t scale
Integer upscale 1..4.
const uint8_t * mask
Mask plane (same layout) or null.
bool opaque
Without a mask: unset bits paint bg.