@geastack/core 0.1.37 → 0.1.39-uart.1

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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include "canvas.h"
4
4
  #include "display.h"
5
+ #include "image.h"
5
6
 
6
7
  #include <algorithm>
7
8
  #include <array>
@@ -284,6 +285,13 @@ inline void addCommandBounds(const Command &command, Rect *rects, int *count, in
284
285
  addRect(rects, count, capacity, commandBounds(command, width, height), width, height);
285
286
  }
286
287
 
288
+ // An app-registered buffer (native surface, ImageData565, video frame) is
289
+ // rewritten in place, so equal pointers say nothing about equal pixels.
290
+ inline bool imagePixelsStable(const Command &command)
291
+ {
292
+ return !gea::framework::graphics::ImageStore::instance().pixelsMayChange(command.pixels);
293
+ }
294
+
287
295
  inline bool commandsEqual(const Command &a, const Command &b)
288
296
  {
289
297
  if (a.type != b.type || a.alpha != b.alpha) return false;
@@ -321,15 +329,15 @@ inline bool commandsEqual(const Command &a, const Command &b)
321
329
  sizeof(gea::framework::graphics::TriangleEntry)) == 0;
322
330
  }
323
331
  case Type::DrawImage:
324
- return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
332
+ return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
325
333
  a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight && a.x == b.x && a.y == b.y;
326
334
  case Type::DrawImageScaled:
327
335
  case Type::DrawImageRotated90CW:
328
- return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
336
+ return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
329
337
  a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight &&
330
338
  a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h;
331
339
  case Type::DrawImageTiledX:
332
- return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels &&
340
+ return a.pixels == b.pixels && a.alphaPixels == b.alphaPixels && imagePixelsStable(a) &&
333
341
  a.srcWidth == b.srcWidth && a.srcHeight == b.srcHeight &&
334
342
  a.x == b.x && a.y == b.y && a.w == b.w;
335
343
  case Type::FillText:
@@ -574,8 +582,17 @@ inline bool frameHasOpaqueBase(const Frame &frame, int width, int height)
574
582
  const Command &first = frame.commands.front();
575
583
  using Type = gea::platform::display::DisplayPresentCommandType;
576
584
  if (first.type == Type::Clear) return true;
577
- if (first.type != Type::FillRectRgb565 || first.alpha != 255) return false;
578
- const Rect bounds = rectFromBox(first.x, first.y, first.w, first.h);
585
+ if (first.alpha != 255) return false;
586
+ Rect bounds;
587
+ if (first.type == Type::FillRectRgb565) {
588
+ bounds = rectFromBox(first.x, first.y, first.w, first.h);
589
+ } else if (first.type == Type::DrawImage && first.alphaPixels == nullptr) {
590
+ // A full-panel opaque image (putImageData of a whole-screen buffer)
591
+ // paints every pixel, the same as a full-panel fill.
592
+ bounds = rectFromBox(first.x, first.y, first.srcWidth, first.srcHeight);
593
+ } else {
594
+ return false;
595
+ }
579
596
  return bounds.x0 <= 0 && bounds.y0 <= 0 && bounds.x1 >= width - 1 && bounds.y1 >= height - 1;
580
597
  }
581
598
 
package/include/image.h CHANGED
@@ -61,6 +61,9 @@ struct ImageSlot {
61
61
  pixel::native_t *pixels = nullptr;
62
62
  std::uint8_t *alpha = nullptr;
63
63
  bool ownsPixels = false;
64
+ // Registered by the app (registerBuffer): the app writes these pixels in
65
+ // place between frames, so the same pointer does not mean the same image.
66
+ bool mutablePixels = false;
64
67
 
65
68
  void *gifDecoder = nullptr;
66
69
  std::uint8_t *gifData = nullptr;
@@ -98,6 +101,11 @@ public:
98
101
  // camera capture, which hands over a freshly-allocated RGBA8888 still.
99
102
  int registerBuffer(pixel::native_t *pixels, int width, int height, int preferredSlot = -1, bool takeOwnership = false);
100
103
 
104
+ // True when `pixels` point into a registerBuffer() image, whose content the
105
+ // app rewrites in place. The present diff never treats such an image as
106
+ // unchanged from the previous frame.
107
+ bool pixelsMayChange(const pixel::native_t *pixels) const;
108
+
101
109
  int width(int id) const;
102
110
  int height(int id) const;
103
111
  int frameCount(int id) const;
@@ -0,0 +1,58 @@
1
+ #pragma once
2
+
3
+ #include <cstddef>
4
+ #include <cstdint>
5
+
6
+ // Target-owned implementation. Unsupported platforms report unavailable.
7
+ extern "C" {
8
+ bool gea_uart_open(int port, int tx, int rx, int baud, int rxBytes, int txBytes)
9
+ __attribute__((weak));
10
+ bool gea_uart_is_open(int port) __attribute__((weak));
11
+ void gea_uart_close(int port) __attribute__((weak));
12
+ int gea_uart_read(int port, void *bytes, std::size_t capacity)
13
+ __attribute__((weak));
14
+ int gea_uart_write(int port, const void *bytes, std::size_t length)
15
+ __attribute__((weak));
16
+ int gea_uart_available(int port) __attribute__((weak));
17
+ int gea_uart_writable(int port) __attribute__((weak));
18
+ }
19
+
20
+ namespace gea::platform::uart {
21
+ struct Config {
22
+ int tx;
23
+ int rx;
24
+ int baudRate;
25
+ int rxBufferSize = 4096;
26
+ int txBufferSize = 4096;
27
+ };
28
+
29
+ class Uart {
30
+ public:
31
+ static bool open(int port, const Config &config) {
32
+ return gea_uart_open &&
33
+ gea_uart_open(port, config.tx, config.rx, config.baudRate,
34
+ config.rxBufferSize, config.txBufferSize);
35
+ }
36
+ static bool isOpen(int port) {
37
+ return gea_uart_is_open && gea_uart_is_open(port);
38
+ }
39
+ static void close(int port) {
40
+ if (gea_uart_close)
41
+ gea_uart_close(port);
42
+ }
43
+ // Nonblocking. -1 means unavailable/closed/error; 0 means no progress.
44
+ static int read(int port, void *bytes, std::size_t capacity) {
45
+ return gea_uart_read ? gea_uart_read(port, bytes, capacity) : -1;
46
+ }
47
+ // Enqueues the complete buffer, or returns 0 if it cannot fit yet.
48
+ static int write(int port, const void *bytes, std::size_t length) {
49
+ return gea_uart_write ? gea_uart_write(port, bytes, length) : -1;
50
+ }
51
+ static int available(int port) {
52
+ return gea_uart_available ? gea_uart_available(port) : -1;
53
+ }
54
+ static int writable(int port) {
55
+ return gea_uart_writable ? gea_uart_writable(port) : -1;
56
+ }
57
+ };
58
+ } // namespace gea::platform::uart
package/index.d.ts CHANGED
@@ -653,6 +653,13 @@ export interface ImageProps extends DataAttributes {
653
653
  onFrame?: (frame: number) => void
654
654
  }
655
655
 
656
+ export interface ImageData565 {
657
+ readonly width: number
658
+ readonly height: number
659
+ /** Row-major RGB565 pixels, `width * height` long. */
660
+ readonly data16: Uint16Array
661
+ }
662
+
656
663
  export interface GeaEmbeddedImage {
657
664
  readonly width: number
658
665
  readonly height: number
@@ -1008,6 +1015,24 @@ export interface CanvasRenderingContext2D {
1008
1015
  drawImageCircle(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
1009
1016
  drawImageRotated90CW(image: GeaEmbeddedImage, dx: number, dy: number, width: number, height: number): void
1010
1017
  drawImageTiledX(image: GeaEmbeddedImage, dx: number, dy: number, width: number): void
1018
+ /**
1019
+ * An RGB565 pixel buffer the app writes directly:
1020
+ * `img.data16[i] = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3)`.
1021
+ * Allocate once, write every frame, then draw with `putImageData`.
1022
+ */
1023
+ createImageData565(width: number, height: number): ImageData565
1024
+ /** Draw an `ImageData565` with its top-left corner at (dx, dy): one blit. */
1025
+ putImageData(image: ImageData565, dx: number, dy: number): void
1026
+ /** Draw only the dirty rectangle of an `ImageData565`, at (dx + dirtyX, dy + dirtyY). */
1027
+ putImageData(
1028
+ image: ImageData565,
1029
+ dx: number,
1030
+ dy: number,
1031
+ dirtyX: number,
1032
+ dirtyY: number,
1033
+ dirtyWidth: number,
1034
+ dirtyHeight: number,
1035
+ ): void
1011
1036
  flush(): void
1012
1037
  beginBatch(): void
1013
1038
  endBatch(): void
@@ -1208,6 +1233,33 @@ export interface GpioController {
1208
1233
 
1209
1234
  export declare const Gpio: GpioController
1210
1235
 
1236
+ export interface UartOptions {
1237
+ tx: number
1238
+ rx: number
1239
+ baudRate: number
1240
+ /** Receive/transmit queue bytes. Defaults to 4096 each. */
1241
+ rxBufferSize?: number
1242
+ txBufferSize?: number
1243
+ }
1244
+
1245
+ /** Hardware UART, 8 data bits, no parity, one stop bit (8N1). */
1246
+ export interface UartController {
1247
+ /** Claims a free controller and pins. False on conflict/invalid configuration. */
1248
+ open(port: number, options: UartOptions): boolean
1249
+ isOpen(port: number): boolean
1250
+ close(port: number): void
1251
+ /** Nonblocking read into the supplied view; returns bytes read, or -1 on error. */
1252
+ read(port: number, buffer: Uint8Array): number
1253
+ /** Nonblocking whole-buffer enqueue; 0 means backpressure, -1 means error. */
1254
+ write(port: number, buffer: Uint8Array): number
1255
+ /** Queued readable bytes, or -1 when unavailable. */
1256
+ available(port: number): number
1257
+ /** Writable queue capacity, or -1 when unavailable. */
1258
+ writable(port: number): number
1259
+ }
1260
+
1261
+ export declare const Uart: UartController
1262
+
1211
1263
  export interface LedController {
1212
1264
  // A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
1213
1265
  // modern ESP32 modules.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geastack/core",
3
- "version": "0.1.37",
3
+ "version": "0.1.39-uart.1",
4
4
  "description": "GeaStack app API, reactivity headers, runtime sources, and type declarations.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,8 +65,8 @@
65
65
  "@babel/types": "^7.23.0",
66
66
  "@geajs/core": "1.5.1",
67
67
  "@geajs/vite-plugin": "1.5.0",
68
- "@geastack/compiler": "^1.0.22",
69
- "@geastack/geatsc-plugin-gea": "^0.1.19",
68
+ "@geastack/compiler": "^1.0.25",
69
+ "@geastack/geatsc-plugin-gea": "0.1.21-uart.1",
70
70
  "opentype.js": "^2.0.0",
71
71
  "vite": "^8.0.0"
72
72
  }
package/runtime/host.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  // fails the representation coverage guard for the whole `Geolocation` literal
7
7
  // below. lib.dom's GeolocationPositionError also carries PERMISSION_DENIED /
8
8
  // POSITION_UNAVAILABLE / TIMEOUT, which our host object never sets.
9
+ import type { UartOptions } from '../index'
9
10
  import type {
10
11
  CanvasRenderingContext2D,
11
12
  GeolocationCoordinates,
@@ -361,6 +362,15 @@ declare const __gea_Gpio: {
361
362
  write(pin: number, level: boolean): boolean
362
363
  read(pin: number): boolean
363
364
  }
365
+ declare const __gea_Uart: {
366
+ open(port: number, tx: number, rx: number, baud: number, rxBytes: number, txBytes: number): boolean
367
+ isOpen(port: number): boolean
368
+ close(port: number): void
369
+ read(port: number, buffer: Uint8Array): number
370
+ write(port: number, buffer: Uint8Array): number
371
+ available(port: number): number
372
+ writable(port: number): number
373
+ }
364
374
  declare const __gea_Led: {
365
375
  // A WS2812/NeoPixel strand on one pin — what "the onboard LED" is on most
366
376
  // modern ESP32 modules. set() is the one-call default (attach if needed,
@@ -995,6 +1005,31 @@ export const Gpio = {
995
1005
  return __gea_Gpio.read(pin)
996
1006
  }
997
1007
  }
1008
+ export const Uart = {
1009
+ open(port: number, options: UartOptions): boolean {
1010
+ if (typeof __gea_Uart === 'undefined') return false
1011
+ return __gea_Uart.open(port, options.tx, options.rx, options.baudRate,
1012
+ options.rxBufferSize ?? 4096, options.txBufferSize ?? 4096)
1013
+ },
1014
+ isOpen(port: number): boolean {
1015
+ return typeof __gea_Uart !== 'undefined' && __gea_Uart.isOpen(port)
1016
+ },
1017
+ close(port: number): void {
1018
+ if (typeof __gea_Uart !== 'undefined') __gea_Uart.close(port)
1019
+ },
1020
+ read(port: number, buffer: Uint8Array): number {
1021
+ return typeof __gea_Uart !== 'undefined' ? __gea_Uart.read(port, buffer) : -1
1022
+ },
1023
+ write(port: number, buffer: Uint8Array): number {
1024
+ return typeof __gea_Uart !== 'undefined' ? __gea_Uart.write(port, buffer) : -1
1025
+ },
1026
+ available(port: number): number {
1027
+ return typeof __gea_Uart !== 'undefined' ? __gea_Uart.available(port) : -1
1028
+ },
1029
+ writable(port: number): number {
1030
+ return typeof __gea_Uart !== 'undefined' ? __gea_Uart.writable(port) : -1
1031
+ },
1032
+ }
998
1033
  export const Led = {
999
1034
  set(pin: number, r: number, g: number, b: number): boolean {
1000
1035
  return __gea_Led.set(pin, r, g, b)
@@ -57,6 +57,8 @@ export type {
57
57
  * gets today.
58
58
  */
59
59
  export type {
60
+ UartOptions,
61
+ UartController,
60
62
  AccelerometerController,
61
63
  AccelerometerReading,
62
64
  AppsController,
@@ -119,6 +121,7 @@ export type {
119
121
  HttpModule,
120
122
  HttpRequestHandler,
121
123
  HttpServer,
124
+ ImageData565,
122
125
  ImageProps,
123
126
  ImageSource,
124
127
  IncomingMessage,