@gjsify/canvas2d 0.1.2 → 0.1.3

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.
@@ -1,22 +1,4 @@
1
- class OurImageData {
2
- constructor(swOrData, sh, maybeHeight) {
3
- this.colorSpace = "srgb";
4
- if (typeof swOrData === "number") {
5
- this.width = swOrData;
6
- this.height = sh;
7
- this.data = new Uint8ClampedArray(this.width * this.height * 4);
8
- } else {
9
- this.data = swOrData;
10
- this.width = sh;
11
- this.height = maybeHeight ?? this.data.length / (4 * this.width);
12
- if (this.data.length !== this.width * this.height * 4) {
13
- throw new RangeError(
14
- `Source data length ${this.data.length} is not a multiple of (4 * width=${this.width})`
15
- );
16
- }
17
- }
18
- }
19
- }
1
+ import { ImageData } from "@gjsify/canvas2d-core";
20
2
  export {
21
- OurImageData
3
+ ImageData as OurImageData
22
4
  };
package/lib/esm/index.js CHANGED
@@ -1,12 +1,7 @@
1
- import { CanvasRenderingContext2D } from "./canvas-rendering-context-2d.js";
2
- import { CanvasGradient } from "./canvas-gradient.js";
3
- import { CanvasPattern } from "./canvas-pattern.js";
4
- import { Path2D } from "./canvas-path.js";
5
- import { OurImageData } from "./image-data.js";
6
- import { parseColor } from "./color.js";
1
+ import { CanvasRenderingContext2D, CanvasGradient, CanvasPattern, Path2D, ImageData, parseColor } from "@gjsify/canvas2d-core";
7
2
  import { Canvas2DWidget } from "./canvas-drawing-area.js";
8
3
  import { HTMLCanvasElement as GjsifyHTMLCanvasElement } from "@gjsify/dom-elements";
9
- import { CanvasRenderingContext2D as CanvasRenderingContext2D2 } from "./canvas-rendering-context-2d.js";
4
+ import { CanvasRenderingContext2D as CanvasRenderingContext2D2, ImageData as ImageData2, Path2D as Path2D2 } from "@gjsify/canvas2d-core";
10
5
  const CONTEXT_KEY = /* @__PURE__ */ Symbol.for("gjsify_canvas2d_context");
11
6
  GjsifyHTMLCanvasElement.registerContextFactory("2d", (canvas, options) => {
12
7
  const existing = canvas[CONTEXT_KEY];
@@ -15,15 +10,13 @@ GjsifyHTMLCanvasElement.registerContextFactory("2d", (canvas, options) => {
15
10
  canvas[CONTEXT_KEY] = ctx;
16
11
  return ctx;
17
12
  });
18
- import { OurImageData as OurImageData2 } from "./image-data.js";
19
- import { Path2D as Path2D2 } from "./canvas-path.js";
20
13
  Object.defineProperty(globalThis, "CanvasRenderingContext2D", {
21
14
  value: CanvasRenderingContext2D2,
22
15
  writable: true,
23
16
  configurable: true
24
17
  });
25
18
  Object.defineProperty(globalThis, "ImageData", {
26
- value: OurImageData2,
19
+ value: ImageData2,
27
20
  writable: true,
28
21
  configurable: true
29
22
  });
@@ -37,7 +30,7 @@ export {
37
30
  CanvasGradient,
38
31
  CanvasPattern,
39
32
  CanvasRenderingContext2D,
40
- OurImageData as ImageData,
33
+ ImageData,
41
34
  Path2D,
42
35
  parseColor
43
36
  };
@@ -3,7 +3,7 @@ import Gdk from 'gi://Gdk?version=4.0';
3
3
  import GLib from 'gi://GLib?version=2.0';
4
4
  import Gtk from 'gi://Gtk?version=4.0';
5
5
  import { HTMLCanvasElement as GjsifyHTMLCanvasElement } from '@gjsify/dom-elements';
6
- import { CanvasRenderingContext2D } from './canvas-rendering-context-2d.js';
6
+ import { CanvasRenderingContext2D } from '@gjsify/canvas2d-core';
7
7
  type Canvas2DReadyCallback = (canvas: globalThis.HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void;
8
8
  /**
9
9
  * A `Gtk.DrawingArea` subclass that handles Canvas 2D bootstrapping:
@@ -29,6 +29,7 @@ export declare const Canvas2DWidget: {
29
29
  _canvas: GjsifyHTMLCanvasElement | null;
30
30
  _ctx: CanvasRenderingContext2D | null;
31
31
  _readyCallbacks: Canvas2DReadyCallback[];
32
+ _resizeCallbacks: ((w: number, h: number) => void)[];
32
33
  _tickCallbackId: number | null;
33
34
  _frameCallback: FrameRequestCallback | null;
34
35
  _timeOrigin: number;
@@ -43,6 +44,12 @@ export declare const Canvas2DWidget: {
43
44
  * If the context is already available, the callback fires synchronously.
44
45
  */
45
46
  onReady(cb: Canvas2DReadyCallback): void;
47
+ /**
48
+ * Register a callback invoked whenever the GTK widget is resized.
49
+ * Canvas dimensions are already updated when the callback fires.
50
+ * Call `queue_draw()` after re-rendering to push the new surface to screen.
51
+ */
52
+ onResize(cb: (width: number, height: number) => void): void;
46
53
  /**
47
54
  * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
48
55
  * Backed by GTK frame clock (vsync-synced, typically ~60 FPS).
@@ -1,11 +1 @@
1
- import Cairo from 'cairo';
2
- /**
3
- * CanvasGradient wrapping a Cairo LinearGradient or RadialGradient.
4
- */
5
- export declare class CanvasGradient {
6
- private _pattern;
7
- constructor(type: 'linear' | 'radial', x0: number, y0: number, x1: number, y1: number, r0?: number, r1?: number);
8
- addColorStop(offset: number, color: string): void;
9
- /** @internal Get the underlying Cairo pattern for rendering. */
10
- _getCairoPattern(): Cairo.LinearGradient | Cairo.RadialGradient;
11
- }
1
+ export { CanvasGradient } from '@gjsify/canvas2d-core';
@@ -1,80 +1 @@
1
- /** A recorded path operation. */
2
- type PathOp = {
3
- type: 'moveTo';
4
- x: number;
5
- y: number;
6
- } | {
7
- type: 'lineTo';
8
- x: number;
9
- y: number;
10
- } | {
11
- type: 'closePath';
12
- } | {
13
- type: 'bezierCurveTo';
14
- cp1x: number;
15
- cp1y: number;
16
- cp2x: number;
17
- cp2y: number;
18
- x: number;
19
- y: number;
20
- } | {
21
- type: 'quadraticCurveTo';
22
- cpx: number;
23
- cpy: number;
24
- x: number;
25
- y: number;
26
- } | {
27
- type: 'arc';
28
- x: number;
29
- y: number;
30
- radius: number;
31
- startAngle: number;
32
- endAngle: number;
33
- ccw: boolean;
34
- } | {
35
- type: 'ellipse';
36
- x: number;
37
- y: number;
38
- rx: number;
39
- ry: number;
40
- rotation: number;
41
- startAngle: number;
42
- endAngle: number;
43
- ccw: boolean;
44
- } | {
45
- type: 'rect';
46
- x: number;
47
- y: number;
48
- w: number;
49
- h: number;
50
- } | {
51
- type: 'roundRect';
52
- x: number;
53
- y: number;
54
- w: number;
55
- h: number;
56
- radii: number | number[];
57
- };
58
- /**
59
- * Path2D records path operations for later replay on a CanvasRenderingContext2D.
60
- */
61
- export declare class Path2D {
62
- /** @internal Recorded operations */
63
- _ops: PathOp[];
64
- constructor(pathOrSvg?: Path2D | string);
65
- addPath(path: Path2D): void;
66
- moveTo(x: number, y: number): void;
67
- lineTo(x: number, y: number): void;
68
- closePath(): void;
69
- bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
70
- quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
71
- arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
72
- ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
73
- rect(x: number, y: number, w: number, h: number): void;
74
- roundRect(x: number, y: number, w: number, h: number, radii?: number | number[]): void;
75
- /**
76
- * @internal Replay all recorded path operations onto a Cairo context.
77
- */
78
- _replayOnCairo(ctx: import('cairo').default.Context): void;
79
- }
80
- export {};
1
+ export { Path2D } from '@gjsify/canvas2d-core';
@@ -1,12 +1 @@
1
- import Cairo from 'cairo';
2
- /**
3
- * CanvasPattern wrapping a Cairo SurfacePattern.
4
- */
5
- export declare class CanvasPattern {
6
- private _pattern;
7
- private constructor();
8
- /** Create a CanvasPattern from a supported image source. Returns null if unsupported. */
9
- static create(image: any, repetition: string | null): CanvasPattern | null;
10
- /** @internal Get the underlying Cairo pattern for rendering. */
11
- _getCairoPattern(): Cairo.SurfacePattern;
12
- }
1
+ export { CanvasPattern } from '@gjsify/canvas2d-core';
@@ -1,155 +1 @@
1
- import Cairo from 'cairo';
2
- import { Path2D } from './canvas-path.js';
3
- /**
4
- * CanvasRenderingContext2D backed by Cairo.ImageSurface.
5
- * Implements the Canvas 2D API for GJS.
6
- */
7
- export declare class CanvasRenderingContext2D {
8
- readonly canvas: any;
9
- private _surface;
10
- private _ctx;
11
- private _state;
12
- private _stateStack;
13
- private _surfaceWidth;
14
- private _surfaceHeight;
15
- constructor(canvas: any, _options?: any);
16
- /** Ensure the surface matches the current canvas dimensions. Recreate if resized. */
17
- private _ensureSurface;
18
- /** Apply the current fill style (color, gradient, or pattern) to the Cairo context. */
19
- private _applyFillStyle;
20
- /** Apply the current stroke style to the Cairo context. */
21
- private _applyStrokeStyle;
22
- /** Apply line properties to the Cairo context. */
23
- private _applyLineStyle;
24
- /** Apply compositing operator. */
25
- private _applyCompositing;
26
- /** Get the Cairo ImageSurface (used by other contexts like drawImage). */
27
- _getSurface(): Cairo.ImageSurface;
28
- /** Check if shadow rendering is needed. */
29
- private _hasShadow;
30
- /**
31
- * Render a shadow for the current path by painting to a temp surface,
32
- * applying a simple box blur approximation, and compositing back.
33
- * This is called before the actual fill/stroke when shadows are active.
34
- */
35
- private _renderShadow;
36
- save(): void;
37
- restore(): void;
38
- translate(x: number, y: number): void;
39
- rotate(angle: number): void;
40
- scale(x: number, y: number): void;
41
- /**
42
- * Multiply the current transformation matrix by the given values.
43
- * Matrix: [a c e]
44
- * [b d f]
45
- * [0 0 1]
46
- */
47
- transform(a: number, b: number, c: number, d: number, e: number, f: number): void;
48
- /**
49
- * Reset the transform to identity, then apply the given matrix.
50
- */
51
- setTransform(matrix?: DOMMatrix2DInit): void;
52
- setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void;
53
- /**
54
- * Return the current transformation matrix as a DOMMatrix-like object.
55
- */
56
- getTransform(): DOMMatrix;
57
- resetTransform(): void;
58
- get fillStyle(): string | CanvasGradient | CanvasPattern;
59
- set fillStyle(value: string | CanvasGradient | CanvasPattern);
60
- get strokeStyle(): string | CanvasGradient | CanvasPattern;
61
- set strokeStyle(value: string | CanvasGradient | CanvasPattern);
62
- get lineWidth(): number;
63
- set lineWidth(value: number);
64
- get lineCap(): CanvasLineCap;
65
- set lineCap(value: CanvasLineCap);
66
- get lineJoin(): CanvasLineJoin;
67
- set lineJoin(value: CanvasLineJoin);
68
- get miterLimit(): number;
69
- set miterLimit(value: number);
70
- get globalAlpha(): number;
71
- set globalAlpha(value: number);
72
- get globalCompositeOperation(): GlobalCompositeOperation;
73
- set globalCompositeOperation(value: GlobalCompositeOperation);
74
- get imageSmoothingEnabled(): boolean;
75
- set imageSmoothingEnabled(value: boolean);
76
- get imageSmoothingQuality(): ImageSmoothingQuality;
77
- set imageSmoothingQuality(value: ImageSmoothingQuality);
78
- setLineDash(segments: number[]): void;
79
- getLineDash(): number[];
80
- get lineDashOffset(): number;
81
- set lineDashOffset(value: number);
82
- get shadowColor(): string;
83
- set shadowColor(value: string);
84
- get shadowBlur(): number;
85
- set shadowBlur(value: number);
86
- get shadowOffsetX(): number;
87
- set shadowOffsetX(value: number);
88
- get shadowOffsetY(): number;
89
- set shadowOffsetY(value: number);
90
- get font(): string;
91
- set font(value: string);
92
- get textAlign(): CanvasTextAlign;
93
- set textAlign(value: CanvasTextAlign);
94
- get textBaseline(): CanvasTextBaseline;
95
- set textBaseline(value: CanvasTextBaseline);
96
- get direction(): CanvasDirection;
97
- set direction(value: CanvasDirection);
98
- beginPath(): void;
99
- moveTo(x: number, y: number): void;
100
- lineTo(x: number, y: number): void;
101
- closePath(): void;
102
- bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
103
- quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
104
- arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
105
- arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;
106
- ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
107
- rect(x: number, y: number, w: number, h: number): void;
108
- roundRect(x: number, y: number, w: number, h: number, radii?: number | number[]): void;
109
- fill(fillRule?: CanvasFillRule): void;
110
- fill(path: Path2D, fillRule?: CanvasFillRule): void;
111
- stroke(): void;
112
- stroke(path: Path2D): void;
113
- fillRect(x: number, y: number, w: number, h: number): void;
114
- strokeRect(x: number, y: number, w: number, h: number): void;
115
- clearRect(x: number, y: number, w: number, h: number): void;
116
- clip(fillRule?: CanvasFillRule): void;
117
- clip(path: Path2D, fillRule?: CanvasFillRule): void;
118
- isPointInPath(x: number, y: number, fillRule?: CanvasFillRule): boolean;
119
- isPointInPath(path: Path2D, x: number, y: number, fillRule?: CanvasFillRule): boolean;
120
- isPointInStroke(x: number, y: number): boolean;
121
- isPointInStroke(path: Path2D, x: number, y: number): boolean;
122
- createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
123
- createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
124
- createPattern(image: any, repetition: string | null): CanvasPattern | null;
125
- createImageData(sw: number, sh: number): ImageData;
126
- createImageData(imagedata: ImageData): ImageData;
127
- getImageData(sx: number, sy: number, sw: number, sh: number): ImageData;
128
- putImageData(imageData: ImageData, dx: number, dy: number, dirtyX?: number, dirtyY?: number, dirtyWidth?: number, dirtyHeight?: number): void;
129
- drawImage(image: any, dx: number, dy: number): void;
130
- drawImage(image: any, dx: number, dy: number, dw: number, dh: number): void;
131
- drawImage(image: any, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
132
- private _getDrawImageSource;
133
- /** Create a PangoCairo layout configured with current font/text settings. */
134
- private _createTextLayout;
135
- /** Parse a CSS font string (e.g. "bold 16px Arial") into a Pango.FontDescription. */
136
- private _parseFontToDescription;
137
- /**
138
- * Compute the x-offset for text alignment relative to the given x coordinate.
139
- */
140
- private _getTextAlignOffset;
141
- /**
142
- * Compute the y-offset for text baseline positioning.
143
- */
144
- private _getTextBaselineOffset;
145
- fillText(text: string, x: number, y: number, _maxWidth?: number): void;
146
- strokeText(text: string, x: number, y: number, _maxWidth?: number): void;
147
- measureText(text: string): TextMetrics;
148
- /**
149
- * Write the canvas surface to a PNG file and return as data URL.
150
- * Used by HTMLCanvasElement.toDataURL() when a '2d' context is active.
151
- */
152
- _toDataURL(type?: string, _quality?: number): string;
153
- /** Release native Cairo resources. Call when the canvas is discarded. */
154
- _dispose(): void;
155
- }
1
+ export { CanvasRenderingContext2D } from '@gjsify/canvas2d-core';
@@ -1,12 +1 @@
1
- /**
2
- * ImageData represents the pixel data of a canvas area.
3
- * Each pixel is 4 bytes: R, G, B, A (0-255 each).
4
- */
5
- export declare class OurImageData {
6
- readonly data: Uint8ClampedArray;
7
- readonly width: number;
8
- readonly height: number;
9
- readonly colorSpace: PredefinedColorSpace;
10
- constructor(sw: number, sh: number);
11
- constructor(data: Uint8ClampedArray, sw: number, sh?: number);
12
- }
1
+ export { ImageData as OurImageData } from '@gjsify/canvas2d-core';
@@ -1,7 +1,2 @@
1
- export { CanvasRenderingContext2D } from './canvas-rendering-context-2d.js';
2
- export { CanvasGradient } from './canvas-gradient.js';
3
- export { CanvasPattern } from './canvas-pattern.js';
4
- export { Path2D } from './canvas-path.js';
5
- export { OurImageData as ImageData } from './image-data.js';
6
- export { parseColor } from './color.js';
1
+ export { CanvasRenderingContext2D, CanvasGradient, CanvasPattern, Path2D, ImageData, parseColor } from '@gjsify/canvas2d-core';
7
2
  export { Canvas2DWidget } from './canvas-drawing-area.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/canvas2d",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Canvas 2D rendering context for GJS, backed by Cairo",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -29,22 +29,23 @@
29
29
  "cairo"
30
30
  ],
31
31
  "dependencies": {
32
- "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.43",
33
- "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.43",
34
- "@girs/gjs": "^4.0.0-beta.43",
35
- "@girs/glib-2.0": "^2.88.0-4.0.0-beta.43",
36
- "@girs/gobject-2.0": "^2.88.0-4.0.0-beta.43",
37
- "@girs/gtk-4.0": "^4.22.1-4.0.0-beta.43",
38
- "@girs/pango-1.0": "^1.57.0-4.0.0-beta.43",
39
- "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.43",
40
- "@gjsify/dom-elements": "^0.1.2",
41
- "@gjsify/event-bridge": "^0.1.2",
42
- "@gjsify/utils": "^0.1.2"
32
+ "@girs/gdk-4.0": "^4.0.0-4.0.0-rc.1",
33
+ "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-rc.1",
34
+ "@girs/gjs": "^4.0.0-rc.1",
35
+ "@girs/glib-2.0": "^2.88.0-4.0.0-rc.1",
36
+ "@girs/gobject-2.0": "^2.88.0-4.0.0-rc.1",
37
+ "@girs/gtk-4.0": "^4.23.0-4.0.0-rc.1",
38
+ "@girs/pango-1.0": "^1.57.1-4.0.0-rc.1",
39
+ "@girs/pangocairo-1.0": "^1.0.0-4.0.0-rc.1",
40
+ "@gjsify/canvas2d-core": "^0.1.3",
41
+ "@gjsify/dom-elements": "^0.1.3",
42
+ "@gjsify/event-bridge": "^0.1.3",
43
+ "@gjsify/utils": "^0.1.3"
43
44
  },
44
45
  "devDependencies": {
45
- "@gjsify/cli": "^0.1.2",
46
- "@gjsify/unit": "^0.1.2",
47
- "@types/node": "^25.5.0",
46
+ "@gjsify/cli": "^0.1.3",
47
+ "@gjsify/unit": "^0.1.3",
48
+ "@types/node": "^25.5.2",
48
49
  "typescript": "^6.0.2"
49
50
  }
50
51
  }
@@ -8,7 +8,7 @@ import GLib from 'gi://GLib?version=2.0';
8
8
  import Gtk from 'gi://Gtk?version=4.0';
9
9
  import { HTMLCanvasElement as GjsifyHTMLCanvasElement } from '@gjsify/dom-elements';
10
10
  import { attachEventControllers } from '@gjsify/event-bridge';
11
- import { CanvasRenderingContext2D } from './canvas-rendering-context-2d.js';
11
+ import { CanvasRenderingContext2D } from '@gjsify/canvas2d-core';
12
12
 
13
13
  type Canvas2DReadyCallback = (canvas: globalThis.HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void;
14
14
 
@@ -37,6 +37,7 @@ export const Canvas2DWidget = GObject.registerClass(
37
37
  _canvas: GjsifyHTMLCanvasElement | null = null;
38
38
  _ctx: CanvasRenderingContext2D | null = null;
39
39
  _readyCallbacks: Canvas2DReadyCallback[] = [];
40
+ _resizeCallbacks: ((w: number, h: number) => void)[] = [];
40
41
  _tickCallbackId: number | null = null;
41
42
  _frameCallback: FrameRequestCallback | null = null;
42
43
  // Time origin in microseconds (GLib monotonic clock).
@@ -66,7 +67,10 @@ export const Canvas2DWidget = GObject.registerClass(
66
67
 
67
68
  /** @internal Draw function called by GTK. Blits the Cairo surface to screen. */
68
69
  _onDraw(_area: Gtk.DrawingArea, cr: any, width: number, height: number): void {
69
- // Lazy init: create canvas + 2D context on first draw
70
+ // Lazy init: create canvas + 2D context on first draw.
71
+ // We do NOT sync dimensions after onReady fires: ready callbacks may set their
72
+ // own canvas dimensions (e.g. a static render at a fixed size). Syncing here
73
+ // would clear the surface they just drew into.
70
74
  if (!this._canvas) {
71
75
  this._canvas = new GjsifyHTMLCanvasElement();
72
76
  this._canvas.width = width;
@@ -79,12 +83,13 @@ export const Canvas2DWidget = GObject.registerClass(
79
83
  }
80
84
  this._readyCallbacks = [];
81
85
  }
82
- }
83
-
84
- // Sync dimensions if widget was resized
85
- if (this._canvas.width !== width || this._canvas.height !== height) {
86
+ } else if (this._canvas.width !== width || this._canvas.height !== height) {
87
+ // Subsequent draw: GTK widget was resized — sync canvas and notify listeners.
86
88
  this._canvas.width = width;
87
89
  this._canvas.height = height;
90
+ for (const cb of this._resizeCallbacks) {
91
+ cb(width, height);
92
+ }
88
93
  }
89
94
 
90
95
  // Blit the Canvas 2D's Cairo.ImageSurface onto the DrawingArea
@@ -117,6 +122,15 @@ export const Canvas2DWidget = GObject.registerClass(
117
122
  this._readyCallbacks.push(cb);
118
123
  }
119
124
 
125
+ /**
126
+ * Register a callback invoked whenever the GTK widget is resized.
127
+ * Canvas dimensions are already updated when the callback fires.
128
+ * Call `queue_draw()` after re-rendering to push the new surface to screen.
129
+ */
130
+ onResize(cb: (width: number, height: number) => void): void {
131
+ this._resizeCallbacks.push(cb);
132
+ }
133
+
120
134
  /**
121
135
  * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
122
136
  * Backed by GTK frame clock (vsync-synced, typically ~60 FPS).
@@ -1,36 +1,2 @@
1
- // CanvasGradient implementation backed by Cairo gradient patterns
2
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient
3
-
4
- import Cairo from 'cairo';
5
- import { parseColor } from './color.js';
6
-
7
- /**
8
- * CanvasGradient wrapping a Cairo LinearGradient or RadialGradient.
9
- */
10
- export class CanvasGradient {
11
- private _pattern: Cairo.LinearGradient | Cairo.RadialGradient;
12
-
13
- constructor(
14
- type: 'linear' | 'radial',
15
- x0: number, y0: number,
16
- x1: number, y1: number,
17
- r0?: number, r1?: number,
18
- ) {
19
- if (type === 'radial') {
20
- this._pattern = new Cairo.RadialGradient(x0, y0, r0!, x1, y1, r1!);
21
- } else {
22
- this._pattern = new Cairo.LinearGradient(x0, y0, x1, y1);
23
- }
24
- }
25
-
26
- addColorStop(offset: number, color: string): void {
27
- const parsed = parseColor(color);
28
- if (!parsed) return;
29
- this._pattern.addColorStopRGBA(offset, parsed.r, parsed.g, parsed.b, parsed.a);
30
- }
31
-
32
- /** @internal Get the underlying Cairo pattern for rendering. */
33
- _getCairoPattern(): Cairo.LinearGradient | Cairo.RadialGradient {
34
- return this._pattern;
35
- }
36
- }
1
+ // Moved to @gjsify/canvas2d-core re-exported here for backward compatibility.
2
+ export { CanvasGradient } from '@gjsify/canvas2d-core';
@@ -1,131 +1,2 @@
1
- // Path2D implementation for Canvas 2D context
2
- // Reference: https://developer.mozilla.org/en-US/docs/Web/API/Path2D
3
- // Records path operations and replays them on a Cairo context.
4
-
5
- import { quadraticToCubic, cairoRoundRect } from './cairo-utils.js';
6
-
7
- /** A recorded path operation. */
8
- type PathOp =
9
- | { type: 'moveTo'; x: number; y: number }
10
- | { type: 'lineTo'; x: number; y: number }
11
- | { type: 'closePath' }
12
- | { type: 'bezierCurveTo'; cp1x: number; cp1y: number; cp2x: number; cp2y: number; x: number; y: number }
13
- | { type: 'quadraticCurveTo'; cpx: number; cpy: number; x: number; y: number }
14
- | { type: 'arc'; x: number; y: number; radius: number; startAngle: number; endAngle: number; ccw: boolean }
15
- | { type: 'ellipse'; x: number; y: number; rx: number; ry: number; rotation: number; startAngle: number; endAngle: number; ccw: boolean }
16
- | { type: 'rect'; x: number; y: number; w: number; h: number }
17
- | { type: 'roundRect'; x: number; y: number; w: number; h: number; radii: number | number[] };
18
-
19
- /**
20
- * Path2D records path operations for later replay on a CanvasRenderingContext2D.
21
- */
22
- export class Path2D {
23
- /** @internal Recorded operations */
24
- _ops: PathOp[] = [];
25
-
26
- constructor(pathOrSvg?: Path2D | string) {
27
- if (pathOrSvg instanceof Path2D) {
28
- this._ops = [...pathOrSvg._ops];
29
- }
30
- // SVG path string parsing is not implemented (complex, rarely needed)
31
- }
32
-
33
- addPath(path: Path2D): void {
34
- this._ops.push(...path._ops);
35
- }
36
-
37
- moveTo(x: number, y: number): void {
38
- this._ops.push({ type: 'moveTo', x, y });
39
- }
40
-
41
- lineTo(x: number, y: number): void {
42
- this._ops.push({ type: 'lineTo', x, y });
43
- }
44
-
45
- closePath(): void {
46
- this._ops.push({ type: 'closePath' });
47
- }
48
-
49
- bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void {
50
- this._ops.push({ type: 'bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y });
51
- }
52
-
53
- quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void {
54
- this._ops.push({ type: 'quadraticCurveTo', cpx, cpy, x, y });
55
- }
56
-
57
- arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise = false): void {
58
- this._ops.push({ type: 'arc', x, y, radius, startAngle, endAngle, ccw: counterclockwise });
59
- }
60
-
61
- ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise = false): void {
62
- if (radiusX < 0 || radiusY < 0) throw new RangeError('The radii provided are negative');
63
- this._ops.push({ type: 'ellipse', x, y, rx: radiusX, ry: radiusY, rotation, startAngle, endAngle, ccw: counterclockwise });
64
- }
65
-
66
- rect(x: number, y: number, w: number, h: number): void {
67
- this._ops.push({ type: 'rect', x, y, w, h });
68
- }
69
-
70
- roundRect(x: number, y: number, w: number, h: number, radii: number | number[] = 0): void {
71
- this._ops.push({ type: 'roundRect', x, y, w, h, radii });
72
- }
73
-
74
- /**
75
- * @internal Replay all recorded path operations onto a Cairo context.
76
- */
77
- _replayOnCairo(ctx: import('cairo').default.Context): void {
78
- let lastX = 0, lastY = 0;
79
-
80
- for (const op of this._ops) {
81
- switch (op.type) {
82
- case 'moveTo':
83
- ctx.moveTo(op.x, op.y);
84
- lastX = op.x; lastY = op.y;
85
- break;
86
- case 'lineTo':
87
- ctx.lineTo(op.x, op.y);
88
- lastX = op.x; lastY = op.y;
89
- break;
90
- case 'closePath':
91
- ctx.closePath();
92
- break;
93
- case 'bezierCurveTo':
94
- ctx.curveTo(op.cp1x, op.cp1y, op.cp2x, op.cp2y, op.x, op.y);
95
- lastX = op.x; lastY = op.y;
96
- break;
97
- case 'quadraticCurveTo': {
98
- const { cp1x, cp1y, cp2x, cp2y } = quadraticToCubic(lastX, lastY, op.cpx, op.cpy, op.x, op.y);
99
- ctx.curveTo(cp1x, cp1y, cp2x, cp2y, op.x, op.y);
100
- lastX = op.x; lastY = op.y;
101
- break;
102
- }
103
- case 'arc':
104
- if (op.ccw) {
105
- ctx.arcNegative(op.x, op.y, op.radius, op.startAngle, op.endAngle);
106
- } else {
107
- ctx.arc(op.x, op.y, op.radius, op.startAngle, op.endAngle);
108
- }
109
- break;
110
- case 'ellipse':
111
- ctx.save();
112
- ctx.translate(op.x, op.y);
113
- ctx.rotate(op.rotation);
114
- ctx.scale(op.rx, op.ry);
115
- if (op.ccw) {
116
- ctx.arcNegative(0, 0, 1, op.startAngle, op.endAngle);
117
- } else {
118
- ctx.arc(0, 0, 1, op.startAngle, op.endAngle);
119
- }
120
- ctx.restore();
121
- break;
122
- case 'rect':
123
- ctx.rectangle(op.x, op.y, op.w, op.h);
124
- break;
125
- case 'roundRect':
126
- cairoRoundRect(ctx, op.x, op.y, op.w, op.h, op.radii);
127
- break;
128
- }
129
- }
130
- }
131
- }
1
+ // Moved to @gjsify/canvas2d-core — re-exported here for backward compatibility.
2
+ export { Path2D } from '@gjsify/canvas2d-core';