@typecad/ui 1.0.0-alpha.6 → 1.0.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/engine-index.js +2 -2
- package/dist/preview/host-ui-runtime.d.ts +1 -0
- package/dist/preview/host-ui-runtime.js +56 -11
- package/dist/ui-engine/font-assets.d.ts +8 -0
- package/dist/ui-engine/font-assets.js +13 -0
- package/dist/ui-engine/layout-engine.js +19 -6
- package/dist/ui-engine/model.d.ts +1 -1
- package/dist/ui-engine/model.js +10 -4
- package/dist/ui-engine/runtime-header/antialiasing.js +52 -32
- package/dist/ui-engine/runtime-header/blend-bodies.js +4 -4
- package/dist/ui-engine/runtime-header/canvas-helpers.js +42 -69
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +22 -10
- package/dist/ui-engine/runtime-header/color-mono-refresh.js +10 -10
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.d.ts +1 -0
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +563 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +26 -10
- package/dist/ui-engine/runtime-header/display-shim.js +8 -8
- package/dist/ui-engine/runtime-header/forward-decls.js +52 -10
- package/dist/ui-engine/runtime-header/image-drawing.js +154 -84
- package/dist/ui-engine/runtime-header/init-press-input.js +20 -5
- package/dist/ui-engine/runtime-header/keyboard.js +62 -18
- package/dist/ui-engine/runtime-header/node-decoration.js +8 -9
- package/dist/ui-engine/runtime-header/node-draw-body.d.ts +1 -0
- package/dist/ui-engine/runtime-header/node-draw-body.js +1402 -0
- package/dist/ui-engine/runtime-header/paint-order-coords.js +48 -18
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +38 -17
- package/dist/ui-engine/runtime-header/scroll-physics.js +15 -18
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +23 -13
- package/dist/ui-engine/runtime-header/structs.js +12 -10
- package/dist/ui-engine/runtime-header/text-rendering.js +132 -71
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +29 -10
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +336 -749
- package/dist/ui-engine/runtime-header/tick/flush-phase.js +7 -3
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +19 -3
- package/dist/ui-engine/runtime-header/tick/transitions-phase.js +31 -24
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +46 -41
- package/dist/ui-engine/runtime-header/types-defines.js +23 -3
- package/dist/ui-engine/runtime-header.d.ts +10 -1
- package/dist/ui-engine/runtime-header.js +7 -1
- package/dist/ui-engine/transpile-ui.js +2 -2
- package/dist/ui-engine/ua-stylesheet.js +46 -3
- package/dist/ui-engine/ui-lowering.d.ts +1 -3
- package/dist/ui-engine/ui-lowering.js +18 -24
- package/dist/ui-engine/ui-registry.js +1 -6
- package/package.json +2 -2
- package/src/engine-index.ts +2 -2
- package/src/preview/host-ui-runtime.ts +56 -12
- package/src/ui-engine/font-assets.ts +13 -0
- package/src/ui-engine/layout-engine.ts +19 -6
- package/src/ui-engine/model.ts +10 -5
- package/src/ui-engine/runtime-header/antialiasing.ts +52 -32
- package/src/ui-engine/runtime-header/blend-bodies.ts +4 -4
- package/src/ui-engine/runtime-header/canvas-helpers.ts +42 -69
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +22 -10
- package/src/ui-engine/runtime-header/color-mono-refresh.ts +10 -10
- package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +566 -0
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +26 -10
- package/src/ui-engine/runtime-header/display-shim.ts +8 -8
- package/src/ui-engine/runtime-header/forward-decls.ts +52 -10
- package/src/ui-engine/runtime-header/image-drawing.ts +154 -84
- package/src/ui-engine/runtime-header/init-press-input.ts +20 -5
- package/src/ui-engine/runtime-header/keyboard.ts +62 -18
- package/src/ui-engine/runtime-header/node-decoration.ts +8 -9
- package/src/ui-engine/runtime-header/node-draw-body.ts +1402 -0
- package/src/ui-engine/runtime-header/paint-order-coords.ts +48 -18
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +38 -17
- package/src/ui-engine/runtime-header/scroll-physics.ts +15 -18
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +23 -13
- package/src/ui-engine/runtime-header/structs.ts +12 -10
- package/src/ui-engine/runtime-header/text-rendering.ts +132 -71
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +29 -10
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +336 -749
- package/src/ui-engine/runtime-header/tick/flush-phase.ts +7 -3
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +19 -3
- package/src/ui-engine/runtime-header/tick/transitions-phase.ts +31 -24
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +46 -41
- package/src/ui-engine/runtime-header/types-defines.ts +23 -3
- package/src/ui-engine/runtime-header.ts +17 -1
- package/src/ui-engine/transpile-ui.ts +2 -2
- package/src/ui-engine/ua-stylesheet.ts +46 -3
- package/src/ui-engine/ui-lowering.ts +19 -26
- package/src/ui-engine/ui-registry.ts +1 -7
- package/dist/ui-engine/scroll-memory-diagnostics.d.ts +0 -16
- package/dist/ui-engine/scroll-memory-diagnostics.js +0 -58
- package/src/ui-engine/scroll-memory-diagnostics.ts +0 -79
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Slice of the C++ runtime header. Defines the CuttlefishGFX class — the
|
|
3
|
+
// shared geometry/canvas/text base used by native display adapters that
|
|
4
|
+
// instantiate CuttlefishGFX by value. NOT emitted on macro-alias paths
|
|
5
|
+
// (Adafruit `CuttlefishCanvas16==GFXcanvas16`, SDL
|
|
6
|
+
// `CuttlefishCanvas16==SdlGfxCanvas`), which own the canvas type.
|
|
7
|
+
//
|
|
8
|
+
// ──── BSD-3-Clause attribution ────────────────────────────────────────────
|
|
9
|
+
// The geometry algorithm implementations emitted by this slice (drawLine,
|
|
10
|
+
// drawCircle, drawCircleHelper, fillCircleHelper, drawRoundRect,
|
|
11
|
+
// fillRoundRect, drawTriangle, fillTriangle, drawChar, and the glyph loop
|
|
12
|
+
// in write()) are ported from Adafruit's Adafruit_GFX library:
|
|
13
|
+
//
|
|
14
|
+
// Adafruit_GFX.cpp / Adafruit_GFX.h
|
|
15
|
+
// Copyright (c) 2013 Adafruit Industries. All rights reserved.
|
|
16
|
+
// Licensed under BSD-3-Clause.
|
|
17
|
+
// Upstream: https://github.com/adafruit/Adafruit-GFX-Library
|
|
18
|
+
//
|
|
19
|
+
// Redistribution and use in source and binary forms, with or without
|
|
20
|
+
// modification, are permitted provided that the following conditions are met:
|
|
21
|
+
//
|
|
22
|
+
// - Redistributions of source code must retain the above copyright notice,
|
|
23
|
+
// this list of conditions and the following disclaimer.
|
|
24
|
+
// - Redistributions in binary form must reproduce the above copyright notice,
|
|
25
|
+
// this list of conditions and the following disclaimer in the documentation
|
|
26
|
+
// and/or other materials provided with the distribution.
|
|
27
|
+
//
|
|
28
|
+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
29
|
+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
30
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
31
|
+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
32
|
+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
33
|
+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
34
|
+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
35
|
+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
36
|
+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
37
|
+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
38
|
+
// POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
+
// ──── End BSD-3-Clause attribution ────────────────────────────────────────
|
|
40
|
+
//
|
|
41
|
+
// Pixel output of the ported algorithms is byte-for-byte identical to
|
|
42
|
+
// Adafruit_GFX because the runtime's text layout and the AGENTS.md
|
|
43
|
+
// scroll/canvas invariants depend on it.
|
|
44
|
+
//
|
|
45
|
+
// AGENTS.md rendering guardrails: canvas ops reuse persistent buffers,
|
|
46
|
+
// use memmove-shift repair strips for scroll (handled in scroll slices that
|
|
47
|
+
// consume getBuffer()), never allocate per-frame.
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
import { renderGlcdfontArray } from "@typecad/cuttlefish/api/shared";
|
|
50
|
+
export function emitCuttlefishGfx(active) {
|
|
51
|
+
if (!active)
|
|
52
|
+
return "";
|
|
53
|
+
// The font array (shared with the SDL adapter).
|
|
54
|
+
const fontArray = renderGlcdfontArray("cuttlefish_glcdfont", "unsigned char");
|
|
55
|
+
return `
|
|
56
|
+
// ── CuttlefishGFX (native display GFX base) ────────────────────────────────
|
|
57
|
+
// Emitted only for native adapters that instantiate CuttlefishGFX by value;
|
|
58
|
+
// suppressed on macro-alias paths (Adafruit CuttlefishCanvas16==GFXcanvas16,
|
|
59
|
+
// SDL CuttlefishCanvas16==SdlGfxCanvas). Gated in ui-emitter.ts.
|
|
60
|
+
#ifndef CUTTLEFISH_GFX_DEFINED
|
|
61
|
+
#define CUTTLEFISH_GFX_DEFINED
|
|
62
|
+
|
|
63
|
+
#include <stdint.h>
|
|
64
|
+
#include <cstdlib>
|
|
65
|
+
#include <cstring>
|
|
66
|
+
#include <new> // std::nothrow for canvas buffer allocation
|
|
67
|
+
|
|
68
|
+
// ── Panel-ops interface ────────────────────────────────────────────────────
|
|
69
|
+
// Each native adapter fills a CuttlefishPanelOps with function pointers that
|
|
70
|
+
// drive its specific bus + panel. CuttlefishGFX calls through these; it never
|
|
71
|
+
// touches hardware directly. Pointers marked "may be nullptr" must be null-
|
|
72
|
+
// checked by callers.
|
|
73
|
+
struct CuttlefishPanelOps {
|
|
74
|
+
void (*startWrite)(void* ctx); // assert CS, take bus lock (may be nullptr)
|
|
75
|
+
void (*endWrite)(void* ctx); // release CS / bus lock (may be nullptr)
|
|
76
|
+
void (*setAddrWindow)(void* ctx, int16_t x, int16_t y, int16_t w, int16_t h);
|
|
77
|
+
void (*writePixels)(void* ctx, const uint16_t* px, uint32_t n);
|
|
78
|
+
void (*writePixel)(void* ctx, int16_t x, int16_t y, uint16_t c); // direct mode
|
|
79
|
+
void (*fillRect)(void* ctx, int16_t x, int16_t y, int16_t w, int16_t h, uint16_t c);
|
|
80
|
+
int16_t (*width)(void* ctx);
|
|
81
|
+
int16_t (*height)(void* ctx);
|
|
82
|
+
// For SSD1309/SSD1680 only — flush a backing-store buffer to the panel.
|
|
83
|
+
// May be nullptr on direct-mode panels.
|
|
84
|
+
void (*flush)(void* ctx, int16_t x, int16_t y, int16_t w, int16_t h);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ── Color snap (mono displays) ─────────────────────────────────────────────
|
|
88
|
+
// Same logic as the Adafruit SSD1309 adapter: any nonzero 565 → white.
|
|
89
|
+
static inline uint16_t ssd_mono(uint16_t c) { return c ? 0xFFFFu : 0x0000u; }
|
|
90
|
+
|
|
91
|
+
// ── 5x7 font (Adafruit glcdfont, BSD-3-Clause — see file header) ──────────
|
|
92
|
+
// 256 glyphs × 5 bytes per glyph. Indexed as cuttlefish_glcdfont[ch * 5 + col].
|
|
93
|
+
${fontArray}
|
|
94
|
+
|
|
95
|
+
// ── CuttlefishGFX base class ───────────────────────────────────────────────
|
|
96
|
+
class CuttlefishGFX {
|
|
97
|
+
public:
|
|
98
|
+
CuttlefishGFX(const CuttlefishPanelOps* ops, void* ctx)
|
|
99
|
+
: ops_(ops), ctx_(ctx),
|
|
100
|
+
cursor_x_(0), cursor_y_(0),
|
|
101
|
+
textcolor_(0xFFFFu), textbgcolor_(0x0000u),
|
|
102
|
+
textsize_(1), wrap_(true) {}
|
|
103
|
+
virtual ~CuttlefishGFX() {}
|
|
104
|
+
|
|
105
|
+
// Geometry — match Adafruit_GFX signatures exactly.
|
|
106
|
+
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
107
|
+
virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
|
|
108
|
+
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
|
109
|
+
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
|
|
110
|
+
virtual void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
|
|
111
|
+
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
|
|
112
|
+
virtual void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
|
|
113
|
+
virtual void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color);
|
|
114
|
+
virtual void drawCircle(int16_t x, int16_t y, int16_t r, uint16_t color);
|
|
115
|
+
virtual void fillCircle(int16_t x, int16_t y, int16_t r, uint16_t color);
|
|
116
|
+
virtual void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
|
|
117
|
+
virtual void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
|
|
118
|
+
|
|
119
|
+
// Bitmaps — match Adafruit_GFX signatures.
|
|
120
|
+
virtual void drawRGBBitmap(int16_t x, int16_t y, const uint16_t* bitmap, int16_t w, int16_t h);
|
|
121
|
+
|
|
122
|
+
// Text
|
|
123
|
+
virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size);
|
|
124
|
+
virtual void write(uint8_t c);
|
|
125
|
+
void print(const char* s) { while (*s) write(static_cast<uint8_t>(*s++)); }
|
|
126
|
+
void setCursor(int16_t x, int16_t y) { cursor_x_ = x; cursor_y_ = y; }
|
|
127
|
+
void setTextColor(uint16_t c) { textcolor_ = c; }
|
|
128
|
+
void setTextColor(uint16_t c, uint16_t bg) { textcolor_ = c; textbgcolor_ = bg; }
|
|
129
|
+
void setTextSize(uint8_t s) { textsize_ = (s > 0) ? s : 1; }
|
|
130
|
+
void setTextWrap(bool w) { wrap_ = w; }
|
|
131
|
+
|
|
132
|
+
// virtual so CuttlefishCanvas16/Mono overrides route through their stored
|
|
133
|
+
// canvas dimensions instead of trying to deref the null ops_.
|
|
134
|
+
virtual int16_t width() const { return (ops_ && ops_->width) ? ops_->width(ctx_) : 0; }
|
|
135
|
+
virtual int16_t height() const { return (ops_ && ops_->height) ? ops_->height(ctx_) : 0; }
|
|
136
|
+
int16_t getCursorX() const { return cursor_x_; }
|
|
137
|
+
int16_t getCursorY() const { return cursor_y_; }
|
|
138
|
+
|
|
139
|
+
// Adafruit circle helpers — public because drawRoundRect/fillRoundRect call them.
|
|
140
|
+
void drawCircleHelper(int16_t x, int16_t y, int16_t r, uint8_t cornername, uint16_t color);
|
|
141
|
+
void fillCircleHelper(int16_t x, int16_t y, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
|
|
142
|
+
|
|
143
|
+
protected:
|
|
144
|
+
const CuttlefishPanelOps* ops_;
|
|
145
|
+
void* ctx_;
|
|
146
|
+
int16_t cursor_x_, cursor_y_;
|
|
147
|
+
uint16_t textcolor_, textbgcolor_;
|
|
148
|
+
uint8_t textsize_;
|
|
149
|
+
bool wrap_;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// ── RGB565 canvas (offscreen; for AA text + scroll compositing) ─────────────
|
|
153
|
+
// Mirrors Adafruit_GFXcanvas16. Allocates w*h*2 bytes; consumer must free via
|
|
154
|
+
// display_deleteCanvas (the runtime reuses persistent canvas slots — see
|
|
155
|
+
// AGENTS.md: __ui_container_canvas, __ui_repair_canvas, __ui_list_canvas,
|
|
156
|
+
// __ui_node_canvas).
|
|
157
|
+
class CuttlefishCanvas16 final : public CuttlefishGFX {
|
|
158
|
+
public:
|
|
159
|
+
// Noncopyable (owns a heap buffer; a value-copy would double-free).
|
|
160
|
+
CuttlefishCanvas16(const CuttlefishCanvas16&) = delete;
|
|
161
|
+
CuttlefishCanvas16& operator=(const CuttlefishCanvas16&) = delete;
|
|
162
|
+
// Owns its buffer: mallocs w*h*2 bytes and frees it in the dtor.
|
|
163
|
+
CuttlefishCanvas16(int16_t w, int16_t h);
|
|
164
|
+
// Adopts an externally-allocated buffer (e.g. PSRAM via display_createCanvasPsram).
|
|
165
|
+
// If takeOwnership is true, the dtor frees the buffer with free() (works for both
|
|
166
|
+
// SRAM malloc and the ESP unified heap, which includes PSRAM). If false, the
|
|
167
|
+
// caller owns the buffer's lifetime.
|
|
168
|
+
CuttlefishCanvas16(int16_t w, int16_t h, uint16_t* externalBuffer, uint8_t takeOwnership = 0);
|
|
169
|
+
virtual ~CuttlefishCanvas16();
|
|
170
|
+
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
171
|
+
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
|
|
172
|
+
// Override width()/height() — the base implementation reads ops_->width,
|
|
173
|
+
// which is null for canvases (constructed with CuttlefishGFX(nullptr, nullptr)).
|
|
174
|
+
// Without these overrides, any code path that calls width()/height() on a
|
|
175
|
+
// canvas target (text wrapping, scroll clipping, ui_display_target_bounds)
|
|
176
|
+
// dereferences null.
|
|
177
|
+
virtual int16_t width() const { return canvas_w_; }
|
|
178
|
+
virtual int16_t height() const { return canvas_h_; }
|
|
179
|
+
uint16_t* getBuffer() const { return buffer_; }
|
|
180
|
+
uint16_t getPixel(int16_t x, int16_t y) const;
|
|
181
|
+
void fillScreen(uint16_t color) { fillRect(0, 0, canvas_w_, canvas_h_, color); }
|
|
182
|
+
int16_t canvasWidth() const { return canvas_w_; }
|
|
183
|
+
int16_t canvasHeight() const { return canvas_h_; }
|
|
184
|
+
private:
|
|
185
|
+
uint16_t* buffer_;
|
|
186
|
+
int16_t canvas_w_, canvas_h_;
|
|
187
|
+
uint8_t owns_buffer_; // 1 = dtor frees buffer_ (malloc'd), 0 = externally owned
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// ── 1-bit mono canvas (SSD1309/SSD1680 backing stores) ──────────────────────
|
|
191
|
+
// Mirrors Adafruit_GFXcanvasMono. (w+7)/8 bytes per row. The buffer is the
|
|
192
|
+
// panel backing store on SSD1309 — flush via display_partial_refresh.
|
|
193
|
+
class CuttlefishCanvasMono final : public CuttlefishGFX {
|
|
194
|
+
public:
|
|
195
|
+
CuttlefishCanvasMono(const CuttlefishCanvasMono&) = delete;
|
|
196
|
+
CuttlefishCanvasMono& operator=(const CuttlefishCanvasMono&) = delete;
|
|
197
|
+
CuttlefishCanvasMono(int16_t w, int16_t h);
|
|
198
|
+
virtual ~CuttlefishCanvasMono();
|
|
199
|
+
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
200
|
+
virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
|
|
201
|
+
// Override width()/height() — see CuttlefishCanvas16 for rationale.
|
|
202
|
+
virtual int16_t width() const { return canvas_w_; }
|
|
203
|
+
virtual int16_t height() const { return canvas_h_; }
|
|
204
|
+
uint8_t* getBuffer() const { return buffer_; }
|
|
205
|
+
uint16_t getPixel(int16_t x, int16_t y) const;
|
|
206
|
+
void fillScreen(uint16_t color) { fillRect(0, 0, canvas_w_, canvas_h_, color); }
|
|
207
|
+
int16_t canvasWidth() const { return canvas_w_; }
|
|
208
|
+
int16_t canvasHeight() const { return canvas_h_; }
|
|
209
|
+
private:
|
|
210
|
+
uint8_t* buffer_; // (w+7)/8 bytes per row × h rows
|
|
211
|
+
int16_t canvas_w_, canvas_h_;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
215
|
+
// Implementations. Ported verbatim from Adafruit_GFX.cpp.
|
|
216
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
void CuttlefishGFX::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
219
|
+
// Dispatch through ops_->writePixel when present (live panel); otherwise
|
|
220
|
+
// rely on subclasses (CuttlefishCanvas16/Mono) overriding drawPixel to
|
|
221
|
+
// write into their buffers. The null check on ops_ is required because
|
|
222
|
+
// canvases construct their base with CuttlefishGFX(nullptr, nullptr).
|
|
223
|
+
if (ops_ && ops_->writePixel) ops_->writePixel(ctx_, x, y, color);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
void CuttlefishGFX::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
|
|
227
|
+
// Dispatch through the virtual fillRect so canvases (which override it)
|
|
228
|
+
// route into their buffer. Do NOT call the panel op directly here —
|
|
229
|
+
// canvases construct their base with a null panel-ops struct.
|
|
230
|
+
fillRect(x, y, 1, h, color);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void CuttlefishGFX::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
|
|
234
|
+
fillRect(x, y, w, 1, color);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
void CuttlefishGFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
238
|
+
drawFastHLine(x, y, w, color);
|
|
239
|
+
drawFastHLine(x, y + h - 1, w, color);
|
|
240
|
+
drawFastVLine(x, y, h, color);
|
|
241
|
+
drawFastVLine(x + w - 1, y, h, color);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void CuttlefishGFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
245
|
+
// Live panel fast-path: bypass the per-pixel virtual call and ask the
|
|
246
|
+
// panel to fill directly. Canvases override this method to write into
|
|
247
|
+
// their buffer (so they never reach this base path).
|
|
248
|
+
if (ops_ && ops_->fillRect) ops_->fillRect(ctx_, x, y, w, h, color);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Bresenham line — Adafruit_GFX.cpp drawLine, unchanged.
|
|
252
|
+
void CuttlefishGFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
|
|
253
|
+
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
|
|
254
|
+
if (steep) {
|
|
255
|
+
int16_t t = x0; x0 = y0; y0 = t;
|
|
256
|
+
t = x1; x1 = y1; y1 = t;
|
|
257
|
+
}
|
|
258
|
+
if (x0 > x1) {
|
|
259
|
+
int16_t t = x0; x0 = x1; x1 = t;
|
|
260
|
+
t = y0; y0 = y1; y1 = t;
|
|
261
|
+
}
|
|
262
|
+
int16_t dx = x1 - x0, dy = abs(y1 - y0);
|
|
263
|
+
int16_t err = dx / 2;
|
|
264
|
+
int16_t ystep = (y0 < y1) ? 1 : -1;
|
|
265
|
+
for (; x0 <= x1; x0++) {
|
|
266
|
+
if (steep) drawPixel(y0, x0, color);
|
|
267
|
+
else drawPixel(x0, y0, color);
|
|
268
|
+
err -= dy;
|
|
269
|
+
if (err < 0) { y0 += ystep; err += dx; }
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Midpoint circle — Adafruit_GFX.cpp drawCircle, unchanged.
|
|
274
|
+
void CuttlefishGFX::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
|
|
275
|
+
if (r <= 0) { drawPixel(x0, y0, color); return; }
|
|
276
|
+
int16_t f = 1 - r, ddF_x = 1, ddF_y = -2 * r;
|
|
277
|
+
int16_t x = 0, y = r;
|
|
278
|
+
drawPixel(x0, y0 + r, color);
|
|
279
|
+
drawPixel(x0, y0 - r, color);
|
|
280
|
+
drawPixel(x0 + r, y0, color);
|
|
281
|
+
drawPixel(x0 - r, y0, color);
|
|
282
|
+
while (x < y) {
|
|
283
|
+
if (f >= 0) { y--; ddF_y += 2; f += ddF_y; }
|
|
284
|
+
x++; ddF_x += 2; f += ddF_x;
|
|
285
|
+
drawPixel(x0 + x, y0 + y, color);
|
|
286
|
+
drawPixel(x0 - x, y0 + y, color);
|
|
287
|
+
drawPixel(x0 + x, y0 - y, color);
|
|
288
|
+
drawPixel(x0 - x, y0 - y, color);
|
|
289
|
+
drawPixel(x0 + y, y0 + x, color);
|
|
290
|
+
drawPixel(x0 - y, y0 + x, color);
|
|
291
|
+
drawPixel(x0 + y, y0 - x, color);
|
|
292
|
+
drawPixel(x0 - y, y0 - x, color);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
void CuttlefishGFX::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) {
|
|
297
|
+
if (r <= 0) return;
|
|
298
|
+
int16_t f = 1 - r, ddF_x = 1, ddF_y = -2 * r;
|
|
299
|
+
int16_t x = 0, y = r;
|
|
300
|
+
while (x < y) {
|
|
301
|
+
if (f >= 0) { y--; ddF_y += 2; f += ddF_y; }
|
|
302
|
+
x++; ddF_x += 2; f += ddF_x;
|
|
303
|
+
if (cornername & 0x4) {
|
|
304
|
+
drawPixel(x0 + x, y0 + y, color);
|
|
305
|
+
drawPixel(x0 + y, y0 + x, color);
|
|
306
|
+
}
|
|
307
|
+
if (cornername & 0x2) {
|
|
308
|
+
drawPixel(x0 + x, y0 - y, color);
|
|
309
|
+
drawPixel(x0 + y, y0 - x, color);
|
|
310
|
+
}
|
|
311
|
+
if (cornername & 0x8) {
|
|
312
|
+
drawPixel(x0 - y, y0 + x, color);
|
|
313
|
+
drawPixel(x0 - x, y0 + y, color);
|
|
314
|
+
}
|
|
315
|
+
if (cornername & 0x1) {
|
|
316
|
+
drawPixel(x0 - y, y0 - x, color);
|
|
317
|
+
drawPixel(x0 - x, y0 - y, color);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
void CuttlefishGFX::fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
|
|
323
|
+
if (r <= 0) { drawPixel(x0, y0, color); return; }
|
|
324
|
+
drawFastVLine(x0, y0 - r, 2 * r + 1, color);
|
|
325
|
+
fillCircleHelper(x0, y0, r, 3, 0, color);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
void CuttlefishGFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) {
|
|
329
|
+
if (r <= 0) return;
|
|
330
|
+
int16_t f = 1 - r, ddF_x = 1, ddF_y = -2 * r;
|
|
331
|
+
int16_t x = 0, y = r;
|
|
332
|
+
while (x < y) {
|
|
333
|
+
if (f >= 0) { y--; ddF_y += 2; f += ddF_y; }
|
|
334
|
+
x++; ddF_x += 2; f += ddF_x;
|
|
335
|
+
if (cornername & 0x1) {
|
|
336
|
+
drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
|
|
337
|
+
drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
|
|
338
|
+
}
|
|
339
|
+
if (cornername & 0x2) {
|
|
340
|
+
drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
|
|
341
|
+
drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
void CuttlefishGFX::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
|
|
347
|
+
drawFastHLine(x + r, y, w - 2 * r, color); // Top
|
|
348
|
+
drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
|
|
349
|
+
drawFastVLine(x, y + r, h - 2 * r, color); // Left
|
|
350
|
+
drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
|
|
351
|
+
drawCircleHelper(x + r, y + r, r, 1, color);
|
|
352
|
+
drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
|
|
353
|
+
drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
|
|
354
|
+
drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
void CuttlefishGFX::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
|
|
358
|
+
fillRect(x + r, y, w - 2 * r, h, color);
|
|
359
|
+
fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
|
|
360
|
+
fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
void CuttlefishGFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {
|
|
364
|
+
drawLine(x0, y0, x1, y1, color);
|
|
365
|
+
drawLine(x1, y1, x2, y2, color);
|
|
366
|
+
drawLine(x2, y2, x0, y0, color);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
void CuttlefishGFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {
|
|
370
|
+
int16_t a, b, y, last;
|
|
371
|
+
// Sort coordinates by Y order ascending.
|
|
372
|
+
if (y0 > y1) { int16_t t = y0; y0 = y1; y1 = t; t = x0; x0 = x1; x1 = t; }
|
|
373
|
+
if (y1 > y2) { int16_t t = y1; y1 = y2; y2 = t; t = x1; x1 = x2; x2 = t; }
|
|
374
|
+
if (y0 > y1) { int16_t t = y0; y0 = y1; y1 = t; t = x0; x0 = x1; x1 = t; }
|
|
375
|
+
if (y0 == y2) { drawFastHLine(x0, y0, x1 - x0, color); return; }
|
|
376
|
+
int16_t dx01 = x1 - x0, dy01 = y1 - y0;
|
|
377
|
+
int16_t dx02 = x2 - x0, dy02 = y2 - y0;
|
|
378
|
+
int16_t dx12 = x2 - x1, dy12 = y2 - y1;
|
|
379
|
+
int32_t sa = 0, sb = 0;
|
|
380
|
+
last = (y1 == y2) ? y1 : y1 - 1;
|
|
381
|
+
for (y = y0; y <= last; y++) {
|
|
382
|
+
a = x0 + sa / dy01;
|
|
383
|
+
b = x0 + sb / dy02;
|
|
384
|
+
sa += dx01;
|
|
385
|
+
sb += dx02;
|
|
386
|
+
if (a > b) { int16_t t = a; a = b; b = t; }
|
|
387
|
+
drawFastHLine(a, y, b - a + 1, color);
|
|
388
|
+
}
|
|
389
|
+
sa = static_cast<int32_t>(dx12) * (last - y1);
|
|
390
|
+
sb = static_cast<int32_t>(dx02) * (last - y0);
|
|
391
|
+
for (; y <= y2; y++) {
|
|
392
|
+
a = x1 + sa / dy12;
|
|
393
|
+
b = x0 + sb / dy02;
|
|
394
|
+
sa += dx12;
|
|
395
|
+
sb += dx02;
|
|
396
|
+
if (a > b) { int16_t t = a; a = b; b = t; }
|
|
397
|
+
drawFastHLine(a, y, b - a + 1, color);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// RGB565 bitmap — Adafruit_GFX.cpp drawRGBBitmap, adapted to call writePixel
|
|
402
|
+
// through the panel ops (no memory-backed variant; the Adafruit mem-version
|
|
403
|
+
// is unused by the cuttlefish runtime).
|
|
404
|
+
void CuttlefishGFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t* bitmap, int16_t w, int16_t h) {
|
|
405
|
+
if (!bitmap || w <= 0 || h <= 0) return;
|
|
406
|
+
for (int16_t j = 0; j < h; j++) {
|
|
407
|
+
for (int16_t i = 0; i < w; i++) {
|
|
408
|
+
drawPixel(x + i, y + j, bitmap[static_cast<int32_t>(j) * w + i]);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Glyph rendering — Adafruit_GFX.cpp drawChar, unchanged algorithm.
|
|
414
|
+
void CuttlefishGFX::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size) {
|
|
415
|
+
if ((x >= width()) || // Clip right
|
|
416
|
+
(y >= height()) || // Clip bottom
|
|
417
|
+
((x + 5 * size - 1) < 0) || // Clip left
|
|
418
|
+
((y + 8 * size - 1) < 0)) // Clip top
|
|
419
|
+
return;
|
|
420
|
+
if (c >= 176) c++; // Adafruit '±' fixup
|
|
421
|
+
for (int8_t i = 0; i < 5; i++) {
|
|
422
|
+
uint8_t line = cuttlefish_glcdfont[static_cast<size_t>(c) * 5 + i];
|
|
423
|
+
for (int8_t j = 0; j < 8; j++, line >>= 1) {
|
|
424
|
+
if (line & 1) {
|
|
425
|
+
if (size == 1) drawPixel(x + i, y + j, color);
|
|
426
|
+
else fillRect(x + i * size, y + j * size, size, size, color);
|
|
427
|
+
} else if (bg != color) {
|
|
428
|
+
if (size == 1) drawPixel(x + i, y + j, bg);
|
|
429
|
+
else fillRect(x + i * size, y + j * size, size, size, bg);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (bg != color) {
|
|
434
|
+
if (size == 1) drawFastVLine(x + 5, y, 8, bg);
|
|
435
|
+
else fillRect(x + 5 * size, y, size, 8 * size, bg);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
void CuttlefishGFX::write(uint8_t c) {
|
|
440
|
+
if (c == '\\n') {
|
|
441
|
+
cursor_x_ = 0;
|
|
442
|
+
cursor_y_ += textsize_ * 8;
|
|
443
|
+
} else if (c != '\\r') {
|
|
444
|
+
int16_t w = 6 * textsize_;
|
|
445
|
+
if (wrap_ && ((cursor_x_ + w) > width())) {
|
|
446
|
+
cursor_x_ = 0;
|
|
447
|
+
cursor_y_ += textsize_ * 8;
|
|
448
|
+
}
|
|
449
|
+
drawChar(cursor_x_, cursor_y_, c, textcolor_, textbgcolor_, textsize_);
|
|
450
|
+
cursor_x_ += w;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ── Canvas implementations ─────────────────────────────────────────────────
|
|
455
|
+
|
|
456
|
+
CuttlefishCanvas16::CuttlefishCanvas16(int16_t w, int16_t h)
|
|
457
|
+
: CuttlefishGFX(nullptr, nullptr),
|
|
458
|
+
buffer_(nullptr), canvas_w_(w), canvas_h_(h), owns_buffer_(1) {
|
|
459
|
+
if ((w > 0) && (h > 0)) {
|
|
460
|
+
size_t bytes = static_cast<size_t>(w) * static_cast<size_t>(h) * sizeof(uint16_t);
|
|
461
|
+
// malloc (not new): on targets with CONFIG_REQUIRES_FULL_LIBCPP but without
|
|
462
|
+
// CONFIG_CPP_EXCEPTIONS, operator new throws std::bad_alloc on OOM and the
|
|
463
|
+
// nothrow wrapper's internal catch can't unwind → std::terminate → abort.
|
|
464
|
+
// malloc returns NULL on failure with no exception path, which is exactly
|
|
465
|
+
// what the canvas-failure self-heal logic expects.
|
|
466
|
+
buffer_ = static_cast<uint16_t*>(malloc(bytes));
|
|
467
|
+
if (buffer_) memset(buffer_, 0, bytes);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
CuttlefishCanvas16::CuttlefishCanvas16(int16_t w, int16_t h, uint16_t* externalBuffer, uint8_t takeOwnership)
|
|
472
|
+
: CuttlefishGFX(nullptr, nullptr),
|
|
473
|
+
buffer_(externalBuffer), canvas_w_(w), canvas_h_(h), owns_buffer_(takeOwnership) {
|
|
474
|
+
// Zero the buffer so the canvas starts clean whether or not we own it.
|
|
475
|
+
if (externalBuffer && (w > 0) && (h > 0)) {
|
|
476
|
+
memset(externalBuffer, 0, static_cast<size_t>(w) * static_cast<size_t>(h) * sizeof(uint16_t));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
CuttlefishCanvas16::~CuttlefishCanvas16() {
|
|
481
|
+
if (buffer_ && owns_buffer_) free(buffer_);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
void CuttlefishCanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
485
|
+
if (!buffer_) return;
|
|
486
|
+
if ((x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return;
|
|
487
|
+
buffer_[static_cast<size_t>(y) * static_cast<size_t>(canvas_w_) + static_cast<size_t>(x)] = color;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
void CuttlefishCanvas16::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
491
|
+
if (!buffer_) return;
|
|
492
|
+
int16_t x1 = (x < 0) ? 0 : x;
|
|
493
|
+
int16_t y1 = (y < 0) ? 0 : y;
|
|
494
|
+
int16_t x2 = x + w; if (x2 > canvas_w_) x2 = canvas_w_;
|
|
495
|
+
int16_t y2 = y + h; if (y2 > canvas_h_) y2 = canvas_h_;
|
|
496
|
+
// Cull before the inner loop (AGENTS.md: clip before expensive work).
|
|
497
|
+
if (x2 <= x1 || y2 <= y1) return;
|
|
498
|
+
for (int16_t j = y1; j < y2; j++) {
|
|
499
|
+
uint16_t* row = buffer_ + static_cast<size_t>(j) * static_cast<size_t>(canvas_w_);
|
|
500
|
+
for (int16_t i = x1; i < x2; i++) row[i] = color;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
uint16_t CuttlefishCanvas16::getPixel(int16_t x, int16_t y) const {
|
|
505
|
+
if (!buffer_ || (x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return 0;
|
|
506
|
+
return buffer_[static_cast<size_t>(y) * static_cast<size_t>(canvas_w_) + static_cast<size_t>(x)];
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
CuttlefishCanvasMono::CuttlefishCanvasMono(int16_t w, int16_t h)
|
|
510
|
+
: CuttlefishGFX(nullptr, nullptr),
|
|
511
|
+
buffer_(nullptr), canvas_w_(w), canvas_h_(h) {
|
|
512
|
+
if ((w > 0) && (h > 0)) {
|
|
513
|
+
size_t row_bytes = (static_cast<size_t>(w) + 7u) / 8u;
|
|
514
|
+
size_t bytes = row_bytes * static_cast<size_t>(h);
|
|
515
|
+
buffer_ = static_cast<uint8_t*>(malloc(bytes));
|
|
516
|
+
if (buffer_) memset(buffer_, 0, bytes);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
CuttlefishCanvasMono::~CuttlefishCanvasMono() {
|
|
521
|
+
if (buffer_) free(buffer_);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
void CuttlefishCanvasMono::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
525
|
+
if (!buffer_) return;
|
|
526
|
+
if ((x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return;
|
|
527
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
528
|
+
uint8_t* row = buffer_ + static_cast<size_t>(y) * row_bytes;
|
|
529
|
+
uint8_t mask = 0x80u >> (x & 7);
|
|
530
|
+
if (color) row[x >> 3] |= mask;
|
|
531
|
+
else row[x >> 3] &= ~mask;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
void CuttlefishCanvasMono::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
|
|
535
|
+
if (!buffer_) return;
|
|
536
|
+
int16_t x1 = (x < 0) ? 0 : x;
|
|
537
|
+
int16_t y1 = (y < 0) ? 0 : y;
|
|
538
|
+
int16_t x2 = x + w; if (x2 > canvas_w_) x2 = canvas_w_;
|
|
539
|
+
int16_t y2 = y + h; if (y2 > canvas_h_) y2 = canvas_h_;
|
|
540
|
+
if (x2 <= x1 || y2 <= y1) return;
|
|
541
|
+
uint8_t fill = color ? 0xFFu : 0x00u;
|
|
542
|
+
for (int16_t j = y1; j < y2; j++) {
|
|
543
|
+
for (int16_t i = x1; i < x2; i++) {
|
|
544
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
545
|
+
uint8_t* row = buffer_ + static_cast<size_t>(j) * row_bytes;
|
|
546
|
+
uint8_t mask = 0x80u >> (i & 7);
|
|
547
|
+
if (color) row[i >> 3] |= mask;
|
|
548
|
+
else row[i >> 3] &= ~mask;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
(void)fill;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
uint16_t CuttlefishCanvasMono::getPixel(int16_t x, int16_t y) const {
|
|
555
|
+
if (!buffer_ || (x < 0) || (y < 0) || (x >= canvas_w_) || (y >= canvas_h_)) return 0;
|
|
556
|
+
size_t row_bytes = (static_cast<size_t>(canvas_w_) + 7u) / 8u;
|
|
557
|
+
const uint8_t* row = buffer_ + static_cast<size_t>(y) * row_bytes;
|
|
558
|
+
return (row[x >> 3] & (0x80u >> (x & 7))) ? 0xFFFFu : 0x0000u;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
#endif // CUTTLEFISH_GFX_DEFINED
|
|
562
|
+
`;
|
|
563
|
+
}
|
|
@@ -64,7 +64,10 @@ static inline void ui_mark_subtree_dirty_local(uint16_t scrollNode) {
|
|
|
64
64
|
|
|
65
65
|
// Mode C strip-only: direct-partial scroll when no viewport canvas fits and the
|
|
66
66
|
// scroll delta is small. Fills only the exposed strip on the display, then marks
|
|
67
|
-
//
|
|
67
|
+
// only the descendants that intersect that strip dirty for direct draw — mirroring
|
|
68
|
+
// the Mode B canvas repair path. Never clears the whole viewport, and never marks
|
|
69
|
+
// the whole subtree dirty (AGENTS.md: keep scroll drag invalidation small — a
|
|
70
|
+
// 1-pixel drag must not repaint every visible child).
|
|
68
71
|
static inline void ui_scroll_direct_prepare(uint16_t s, int16_t* outVX, int16_t* outVY) {
|
|
69
72
|
int16_t vw = __ui_nodes[s].box.w;
|
|
70
73
|
int16_t vh = __ui_nodes[s].box.h;
|
|
@@ -73,18 +76,31 @@ static inline void ui_scroll_direct_prepare(uint16_t s, int16_t* outVX, int16_t*
|
|
|
73
76
|
UI_COLOR_T scrollBg = __ui_nodes[s].hasBg ? __ui_nodes[s].bg : __ui_nodes[s].clearColor;
|
|
74
77
|
int16_t deltaY = __ui_nodes[s].scrollY - __ui_nodes[s].lastPaintedScrollY;
|
|
75
78
|
int16_t absDelta = deltaY < 0 ? -deltaY : deltaY;
|
|
76
|
-
int16_t stripY = deltaY > 0 ?
|
|
77
|
-
ui_display_fill_rect(vox,
|
|
79
|
+
int16_t stripY = deltaY > 0 ? static_cast<int16_t>(vh - absDelta) : 0;
|
|
80
|
+
ui_display_fill_rect(vox, static_cast<int16_t>(voy + stripY), vw, absDelta, scrollBg);
|
|
81
|
+
// Only descendants whose paint rect intersects the exposed strip need
|
|
82
|
+
// repainting; the rest keep their last-painted pixels (the strip background
|
|
83
|
+
// fill already covered the vacated band). Clear stale dirty flags on the
|
|
84
|
+
// others so a flag set by a prior frame (or a sibling promotion) doesn't
|
|
85
|
+
// trigger a needless repaint.
|
|
86
|
+
UIRect exposed = { vox, static_cast<int16_t>(voy + stripY), vw, absDelta };
|
|
78
87
|
for (uint16_t c = s + 1; c < __ui_nodes[s].subtreeEnd; c++) {
|
|
79
88
|
if (!ui_is_effectively_visible(c) || __ui_nodes[c].screenId != __ui_active_screen) {
|
|
80
89
|
__ui_nodes[c].dirty = 0;
|
|
81
90
|
continue;
|
|
82
91
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
UIRect cr;
|
|
93
|
+
ui_node_current_paint_rect(c, &cr);
|
|
94
|
+
if (cr.w > 0 && cr.h > 0 &&
|
|
95
|
+
ui_rects_intersect(cr.x, cr.y, cr.w, cr.h, exposed.x, exposed.y, exposed.w, exposed.h)) {
|
|
96
|
+
__ui_nodes[c].dirty = 1;
|
|
97
|
+
if (__ui_nodes[c].kind == NODE_PROGRESS || __ui_nodes[c].kind == NODE_RANGE) {
|
|
98
|
+
__ui_nodes[c].lastTextWidth = -1;
|
|
99
|
+
}
|
|
100
|
+
__ui_nodes[c].lastTextHeight = 0;
|
|
101
|
+
} else {
|
|
102
|
+
__ui_nodes[c].dirty = 0;
|
|
86
103
|
}
|
|
87
|
-
__ui_nodes[c].lastTextHeight = 0;
|
|
88
104
|
}
|
|
89
105
|
__ui_nodes[s].dirty = 0;
|
|
90
106
|
if (outVX) *outVX = vox;
|
|
@@ -96,13 +112,13 @@ static inline int16_t ui_overflow_scroll_compositor(uint16_t nodeIdx) {
|
|
|
96
112
|
if (nodeIdx >= __ui_node_count) return -1;
|
|
97
113
|
if (__ui_nodes[nodeIdx].scrollable && !__ui_nodes[nodeIdx].virtualized &&
|
|
98
114
|
__ui_nodes[nodeIdx].contentHeight > __ui_nodes[nodeIdx].box.h) {
|
|
99
|
-
return (
|
|
115
|
+
return static_cast<int16_t>(nodeIdx);
|
|
100
116
|
}
|
|
101
117
|
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
102
118
|
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
103
119
|
if (__ui_nodes[p].scrollable && !__ui_nodes[p].virtualized &&
|
|
104
120
|
__ui_nodes[p].contentHeight > __ui_nodes[p].box.h) {
|
|
105
|
-
return (
|
|
121
|
+
return static_cast<int16_t>(p);
|
|
106
122
|
}
|
|
107
123
|
p = __ui_nodes[p].parent;
|
|
108
124
|
}
|
|
@@ -151,7 +167,7 @@ static inline void ui_mark_scroll_view_dirty(uint16_t scrollNode) {
|
|
|
151
167
|
static inline int16_t ui_scroll_ancestor_for_node(uint16_t nodeIdx) {
|
|
152
168
|
uint16_t p = __ui_nodes[nodeIdx].parent;
|
|
153
169
|
while (p != UI_NO_PARENT && p < __ui_node_count) {
|
|
154
|
-
if (__ui_nodes[p].scrollable) return (
|
|
170
|
+
if (__ui_nodes[p].scrollable) return static_cast<int16_t>(p);
|
|
155
171
|
p = __ui_nodes[p].parent;
|
|
156
172
|
}
|
|
157
173
|
return -1;
|
|
@@ -26,10 +26,10 @@ static inline int16_t ui_clamp_i16(int16_t value, int16_t lo, int16_t hi) {
|
|
|
26
26
|
return value;
|
|
27
27
|
}
|
|
28
28
|
static inline void ui_display_target_bounds(int16_t* left, int16_t* top, int16_t* right, int16_t* bottom) {
|
|
29
|
-
if (left) *left =
|
|
30
|
-
if (top) *top =
|
|
31
|
-
if (right) *right =
|
|
32
|
-
if (bottom) *bottom =
|
|
29
|
+
if (left) *left = static_cast<int16_t>(-__ui_draw_off_x);
|
|
30
|
+
if (top) *top = static_cast<int16_t>(-__ui_draw_off_y);
|
|
31
|
+
if (right) *right = static_cast<int16_t>(ui_display_target_width() - __ui_draw_off_x);
|
|
32
|
+
if (bottom) *bottom = static_cast<int16_t>(ui_display_target_height() - __ui_draw_off_y);
|
|
33
33
|
}
|
|
34
34
|
static inline uint8_t ui_clip_rect_to_display_target(int16_t* x, int16_t* y, int16_t* w, int16_t* h) {
|
|
35
35
|
if (!x || !y || !w || !h || *w <= 0 || *h <= 0) return 0;
|
|
@@ -37,15 +37,15 @@ static inline uint8_t ui_clip_rect_to_display_target(int16_t* x, int16_t* y, int
|
|
|
37
37
|
ui_display_target_bounds(&left, &top, &right, &bottom);
|
|
38
38
|
int16_t x0 = *x > left ? *x : left;
|
|
39
39
|
int16_t y0 = *y > top ? *y : top;
|
|
40
|
-
int16_t x1 =
|
|
41
|
-
int16_t y1 =
|
|
40
|
+
int16_t x1 = static_cast<int16_t>(*x + *w);
|
|
41
|
+
int16_t y1 = static_cast<int16_t>(*y + *h);
|
|
42
42
|
if (x1 > right) x1 = right;
|
|
43
43
|
if (y1 > bottom) y1 = bottom;
|
|
44
44
|
if (x0 >= x1 || y0 >= y1) return 0;
|
|
45
45
|
*x = x0;
|
|
46
46
|
*y = y0;
|
|
47
|
-
*w =
|
|
48
|
-
*h =
|
|
47
|
+
*w = static_cast<int16_t>(x1 - x0);
|
|
48
|
+
*h = static_cast<int16_t>(y1 - y0);
|
|
49
49
|
return 1;
|
|
50
50
|
}
|
|
51
51
|
static inline void ui_display_draw_pixel(int16_t x, int16_t y, UI_COLOR_T color) {
|