@gjsify/canvas2d 0.3.21 → 0.4.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.
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/cairo-utils.js +1 -1
- package/lib/esm/canvas-state.js +1 -1
- package/lib/esm/canvas2d-bridge.js +1 -1
- package/lib/esm/color.js +1 -1
- package/lib/esm/index.js +1 -1
- package/package.json +53 -49
- package/src/cairo-utils.ts +0 -243
- package/src/canvas-gradient.ts +0 -2
- package/src/canvas-path.ts +0 -2
- package/src/canvas-pattern.ts +0 -2
- package/src/canvas-rendering-context-2d.ts +0 -2
- package/src/canvas-state.ts +0 -77
- package/src/canvas2d-bridge.ts +0 -196
- package/src/color.ts +0 -125
- package/src/image-data.ts +0 -3
- package/src/index.spec.ts +0 -888
- package/src/index.ts +0 -42
- package/src/test.mts +0 -6
- package/tmp/.tsbuildinfo +0 -1
- package/tsconfig.json +0 -47
package/src/canvas2d-bridge.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
// Canvas2DBridge — GTK container for HTMLCanvasElement (2D) backed by Cairo.
|
|
2
|
-
// Provides a Gtk.DrawingArea subclass that handles Canvas 2D bootstrapping.
|
|
3
|
-
// Pattern follows packages/dom/iframe/src/iframe-bridge.ts (IFrameBridge)
|
|
4
|
-
|
|
5
|
-
import GObject from 'gi://GObject';
|
|
6
|
-
import Gdk from 'gi://Gdk?version=4.0';
|
|
7
|
-
import GLib from 'gi://GLib?version=2.0';
|
|
8
|
-
import Gtk from 'gi://Gtk?version=4.0';
|
|
9
|
-
import { HTMLCanvasElement as GjsifyHTMLCanvasElement } from '@gjsify/dom-elements';
|
|
10
|
-
import { attachEventControllers } from '@gjsify/event-bridge';
|
|
11
|
-
import { CanvasRenderingContext2D } from '@gjsify/canvas2d-core';
|
|
12
|
-
import { Event } from '@gjsify/dom-events';
|
|
13
|
-
|
|
14
|
-
type Canvas2DReadyCallback = (canvas: globalThis.HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A `Gtk.DrawingArea` subclass that handles Canvas 2D bootstrapping:
|
|
18
|
-
* - Creates an `HTMLCanvasElement` + `CanvasRenderingContext2D` on first draw
|
|
19
|
-
* - Blits the Canvas 2D Cairo.ImageSurface onto the DrawingArea each frame
|
|
20
|
-
* - Fires `onReady()` callbacks with (canvas, ctx) once the context is available
|
|
21
|
-
* - Provides `requestAnimationFrame()` backed by GTK frame clock (vsync)
|
|
22
|
-
* - `installGlobals()` sets `globalThis.requestAnimationFrame` and `globalThis.performance`
|
|
23
|
-
*
|
|
24
|
-
* Usage:
|
|
25
|
-
* ```ts
|
|
26
|
-
* const widget = new Canvas2DBridge();
|
|
27
|
-
* widget.installGlobals(); // sets globalThis.requestAnimationFrame
|
|
28
|
-
* widget.onReady((canvas, ctx) => {
|
|
29
|
-
* ctx.fillStyle = 'red';
|
|
30
|
-
* ctx.fillRect(0, 0, 100, 100);
|
|
31
|
-
* });
|
|
32
|
-
* window.set_child(widget);
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
// Gtk.DrawingArea inherits a 'resize' signal with signature
|
|
36
|
-
// (widget: Drawable, width: int, height: int) from its ancestors (Gtk.Widget).
|
|
37
|
-
// WebGLBridge uses Gtk.GLArea which has the same-shaped signal. We do
|
|
38
|
-
// NOT register a custom signal — we piggyback on the inherited one so
|
|
39
|
-
// consumers can use a single pattern on both widgets:
|
|
40
|
-
// widget.connect('resize', (w, width, height) => { ... })
|
|
41
|
-
// In addition, _onDraw fires onResize(cb) callbacks and dispatches a DOM
|
|
42
|
-
// 'resize' event on the canvas for browser-style listeners.
|
|
43
|
-
|
|
44
|
-
export const Canvas2DBridge = GObject.registerClass(
|
|
45
|
-
{ GTypeName: 'GjsifyCanvas2DBridge' },
|
|
46
|
-
class Canvas2DBridge extends Gtk.DrawingArea {
|
|
47
|
-
_canvas: GjsifyHTMLCanvasElement | null = null;
|
|
48
|
-
_ctx: CanvasRenderingContext2D | null = null;
|
|
49
|
-
_readyCallbacks: Canvas2DReadyCallback[] = [];
|
|
50
|
-
_resizeCallbacks: ((w: number, h: number) => void)[] = [];
|
|
51
|
-
_tickCallbackId: number | null = null;
|
|
52
|
-
_frameCallback: FrameRequestCallback | null = null;
|
|
53
|
-
// Time origin in microseconds (GLib monotonic clock).
|
|
54
|
-
// Both requestAnimationFrame timestamps and performance.now() are
|
|
55
|
-
// relative to this origin, matching the browser DOMHighResTimeStamp spec.
|
|
56
|
-
_timeOrigin: number = GLib.get_monotonic_time();
|
|
57
|
-
|
|
58
|
-
constructor(params?: Partial<Gtk.DrawingArea.ConstructorProps>) {
|
|
59
|
-
super(params);
|
|
60
|
-
this.set_draw_func(this._onDraw.bind(this));
|
|
61
|
-
|
|
62
|
-
// Bridge GTK events → DOM events on the canvas element
|
|
63
|
-
// captureKeys: true prevents GTK focus traversal consuming arrow/Enter keys
|
|
64
|
-
attachEventControllers(this, () => this._canvas, { captureKeys: true });
|
|
65
|
-
|
|
66
|
-
this.connect('unrealize', () => {
|
|
67
|
-
if (this._tickCallbackId !== null) {
|
|
68
|
-
this.remove_tick_callback(this._tickCallbackId);
|
|
69
|
-
this._tickCallbackId = null;
|
|
70
|
-
}
|
|
71
|
-
if (this._ctx) {
|
|
72
|
-
this._ctx._dispose();
|
|
73
|
-
}
|
|
74
|
-
this._canvas = null;
|
|
75
|
-
this._ctx = null;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** @internal Draw function called by GTK. Blits the Cairo surface to screen. */
|
|
80
|
-
_onDraw(_area: Gtk.DrawingArea, cr: any, width: number, height: number): void {
|
|
81
|
-
// Lazy init: create canvas + 2D context on first draw.
|
|
82
|
-
// We do NOT sync dimensions after onReady fires: ready callbacks may set their
|
|
83
|
-
// own canvas dimensions (e.g. a static render at a fixed size). Syncing here
|
|
84
|
-
// would clear the surface they just drew into.
|
|
85
|
-
if (!this._canvas) {
|
|
86
|
-
this._canvas = new GjsifyHTMLCanvasElement();
|
|
87
|
-
this._canvas.width = width;
|
|
88
|
-
this._canvas.height = height;
|
|
89
|
-
// Import side-effect registers the '2d' factory, so getContext('2d') works
|
|
90
|
-
this._ctx = this._canvas.getContext('2d') as unknown as CanvasRenderingContext2D;
|
|
91
|
-
if (this._ctx) {
|
|
92
|
-
for (const cb of this._readyCallbacks) {
|
|
93
|
-
cb(this._canvas as unknown as globalThis.HTMLCanvasElement, this._ctx);
|
|
94
|
-
}
|
|
95
|
-
this._readyCallbacks = [];
|
|
96
|
-
}
|
|
97
|
-
} else if (this._canvas.width !== width || this._canvas.height !== height) {
|
|
98
|
-
// Subsequent draw: GTK widget was resized — sync canvas and notify listeners.
|
|
99
|
-
// NOTE: Gtk.DrawingArea itself emits the 'resize' GObject signal
|
|
100
|
-
// (inherited) when the allocation changes, so consumers can use
|
|
101
|
-
// widget.connect('resize', ...) without us emitting it here.
|
|
102
|
-
// We additionally dispatch a DOM 'resize' event on the canvas
|
|
103
|
-
// and fire onResize() callbacks for cross-widget API parity
|
|
104
|
-
// with WebGLBridge.
|
|
105
|
-
this._canvas.width = width;
|
|
106
|
-
this._canvas.height = height;
|
|
107
|
-
this._canvas.dispatchEvent(new Event('resize'));
|
|
108
|
-
for (const cb of this._resizeCallbacks) {
|
|
109
|
-
cb(width, height);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Blit the Canvas 2D's Cairo.ImageSurface onto the DrawingArea
|
|
114
|
-
if (this._ctx) {
|
|
115
|
-
const surface = this._ctx._getSurface();
|
|
116
|
-
cr.setSourceSurface(surface, 0, 0);
|
|
117
|
-
cr.paint();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** The HTMLCanvasElement backing this widget. Available after the first draw. */
|
|
122
|
-
get canvas(): globalThis.HTMLCanvasElement | null {
|
|
123
|
-
return this._canvas as unknown as globalThis.HTMLCanvasElement | null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Get the 2D rendering context. Available after the first draw. */
|
|
127
|
-
getContext(_id: '2d'): CanvasRenderingContext2D | null {
|
|
128
|
-
return this._ctx;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Register a callback to be invoked once the Canvas 2D context is ready.
|
|
133
|
-
* If the context is already available, the callback fires synchronously.
|
|
134
|
-
*/
|
|
135
|
-
onReady(cb: Canvas2DReadyCallback): void {
|
|
136
|
-
if (this._canvas && this._ctx) {
|
|
137
|
-
cb(this._canvas as unknown as globalThis.HTMLCanvasElement, this._ctx);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
this._readyCallbacks.push(cb);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Register a callback invoked whenever the GTK widget is resized.
|
|
145
|
-
* Canvas dimensions are already updated when the callback fires.
|
|
146
|
-
* Call `queue_draw()` after re-rendering to push the new surface to screen.
|
|
147
|
-
*/
|
|
148
|
-
onResize(cb: (width: number, height: number) => void): void {
|
|
149
|
-
this._resizeCallbacks.push(cb);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
|
|
154
|
-
* Backed by GTK frame clock (vsync-synced, typically ~60 FPS).
|
|
155
|
-
* Returns 0 (handle — cancel not yet implemented).
|
|
156
|
-
*/
|
|
157
|
-
requestAnimationFrame(cb: FrameRequestCallback): number {
|
|
158
|
-
this._frameCallback = cb;
|
|
159
|
-
if (this._tickCallbackId === null) {
|
|
160
|
-
this._tickCallbackId = this.add_tick_callback((_widget: Gtk.Widget, frameClock: Gdk.FrameClock) => {
|
|
161
|
-
this._tickCallbackId = null;
|
|
162
|
-
// DOMHighResTimeStamp: ms since time origin, matching performance.now()
|
|
163
|
-
const time = (frameClock.get_frame_time() - this._timeOrigin) / 1000;
|
|
164
|
-
this._frameCallback?.(time);
|
|
165
|
-
this.queue_draw();
|
|
166
|
-
return GLib.SOURCE_REMOVE;
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
// Ensure GTK schedules a new frame so the tick callback fires.
|
|
170
|
-
// Without this, requestAnimationFrame called during the paint phase
|
|
171
|
-
// (e.g. from onReady) may not trigger the frame clock to tick again.
|
|
172
|
-
this.queue_draw();
|
|
173
|
-
return 0;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Sets browser globals (`requestAnimationFrame`, `performance`) so that
|
|
178
|
-
* browser-targeted code works unchanged on GJS.
|
|
179
|
-
*/
|
|
180
|
-
installGlobals(): void {
|
|
181
|
-
(globalThis as any).requestAnimationFrame = (cb: FrameRequestCallback) =>
|
|
182
|
-
this.requestAnimationFrame(cb);
|
|
183
|
-
// Install performance.now() on the same time origin as rAF timestamps.
|
|
184
|
-
// Always override to ensure consistency — native GJS performance.now()
|
|
185
|
-
// may use a different time origin than the frame clock.
|
|
186
|
-
const timeOrigin = this._timeOrigin;
|
|
187
|
-
(globalThis as any).performance = {
|
|
188
|
-
now: () => (GLib.get_monotonic_time() - timeOrigin) / 1000,
|
|
189
|
-
timeOrigin: Date.now(),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// Export the instance type so callers can type-annotate their Canvas2DBridge variables
|
|
196
|
-
export type Canvas2DBridge = InstanceType<typeof Canvas2DBridge>;
|
package/src/color.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
// CSS color parser for Canvas 2D context
|
|
2
|
-
// Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
|
|
3
|
-
|
|
4
|
-
export interface RGBA {
|
|
5
|
-
r: number; // 0-1
|
|
6
|
-
g: number; // 0-1
|
|
7
|
-
b: number; // 0-1
|
|
8
|
-
a: number; // 0-1
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// CSS named colors (all 148 standard colors)
|
|
12
|
-
const NAMED_COLORS: Record<string, string> = {
|
|
13
|
-
aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4',
|
|
14
|
-
azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000',
|
|
15
|
-
blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a',
|
|
16
|
-
burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e',
|
|
17
|
-
coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c',
|
|
18
|
-
cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b',
|
|
19
|
-
darkgray: '#a9a9a9', darkgreen: '#006400', darkgrey: '#a9a9a9', darkkhaki: '#bdb76b',
|
|
20
|
-
darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc',
|
|
21
|
-
darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b',
|
|
22
|
-
darkslategray: '#2f4f4f', darkslategrey: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3',
|
|
23
|
-
deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dimgrey: '#696969',
|
|
24
|
-
dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22',
|
|
25
|
-
fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700',
|
|
26
|
-
goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f',
|
|
27
|
-
grey: '#808080', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c',
|
|
28
|
-
indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa',
|
|
29
|
-
lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6',
|
|
30
|
-
lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3',
|
|
31
|
-
lightgreen: '#90ee90', lightgrey: '#d3d3d3', lightpink: '#ffb6c1', lightsalmon: '#ffa07a',
|
|
32
|
-
lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightslategrey: '#778899',
|
|
33
|
-
lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32',
|
|
34
|
-
linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa',
|
|
35
|
-
mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db', mediumseagreen: '#3cb371',
|
|
36
|
-
mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc',
|
|
37
|
-
mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1',
|
|
38
|
-
moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6',
|
|
39
|
-
olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500',
|
|
40
|
-
orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee',
|
|
41
|
-
palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f',
|
|
42
|
-
pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080',
|
|
43
|
-
rebeccapurple: '#663399', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1',
|
|
44
|
-
saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57',
|
|
45
|
-
seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb',
|
|
46
|
-
slateblue: '#6a5acd', slategray: '#708090', slategrey: '#708090', snow: '#fffafa',
|
|
47
|
-
springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080',
|
|
48
|
-
thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee',
|
|
49
|
-
wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00',
|
|
50
|
-
yellowgreen: '#9acd32',
|
|
51
|
-
transparent: '#00000000',
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Parse a CSS color string into RGBA components (0-1 range).
|
|
56
|
-
* Supports: #rgb, #rrggbb, #rgba, #rrggbbaa, rgb(), rgba(), named colors, 'transparent'.
|
|
57
|
-
*/
|
|
58
|
-
export function parseColor(color: string): RGBA | null {
|
|
59
|
-
if (!color || typeof color !== 'string') return null;
|
|
60
|
-
|
|
61
|
-
const trimmed = color.trim().toLowerCase();
|
|
62
|
-
|
|
63
|
-
// Named colors
|
|
64
|
-
const named = NAMED_COLORS[trimmed];
|
|
65
|
-
if (named) return parseHex(named);
|
|
66
|
-
|
|
67
|
-
// Hex formats
|
|
68
|
-
if (trimmed.startsWith('#')) return parseHex(trimmed);
|
|
69
|
-
|
|
70
|
-
// rgb()/rgba()
|
|
71
|
-
const rgbMatch = trimmed.match(
|
|
72
|
-
/^rgba?\(\s*(\d+(?:\.\d+)?%?)\s*[,\s]\s*(\d+(?:\.\d+)?%?)\s*[,\s]\s*(\d+(?:\.\d+)?%?)\s*(?:[,/]\s*(\d+(?:\.\d+)?%?))?\s*\)$/
|
|
73
|
-
);
|
|
74
|
-
if (rgbMatch) {
|
|
75
|
-
return {
|
|
76
|
-
r: parseComponent(rgbMatch[1], 255) / 255,
|
|
77
|
-
g: parseComponent(rgbMatch[2], 255) / 255,
|
|
78
|
-
b: parseComponent(rgbMatch[3], 255) / 255,
|
|
79
|
-
a: rgbMatch[4] !== undefined ? parseComponent(rgbMatch[4], 1) : 1,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function parseHex(hex: string): RGBA | null {
|
|
87
|
-
const h = hex.slice(1);
|
|
88
|
-
let r: number, g: number, b: number, a = 1;
|
|
89
|
-
|
|
90
|
-
if (h.length === 3) {
|
|
91
|
-
r = parseInt(h[0] + h[0], 16) / 255;
|
|
92
|
-
g = parseInt(h[1] + h[1], 16) / 255;
|
|
93
|
-
b = parseInt(h[2] + h[2], 16) / 255;
|
|
94
|
-
} else if (h.length === 4) {
|
|
95
|
-
r = parseInt(h[0] + h[0], 16) / 255;
|
|
96
|
-
g = parseInt(h[1] + h[1], 16) / 255;
|
|
97
|
-
b = parseInt(h[2] + h[2], 16) / 255;
|
|
98
|
-
a = parseInt(h[3] + h[3], 16) / 255;
|
|
99
|
-
} else if (h.length === 6) {
|
|
100
|
-
r = parseInt(h.slice(0, 2), 16) / 255;
|
|
101
|
-
g = parseInt(h.slice(2, 4), 16) / 255;
|
|
102
|
-
b = parseInt(h.slice(4, 6), 16) / 255;
|
|
103
|
-
} else if (h.length === 8) {
|
|
104
|
-
r = parseInt(h.slice(0, 2), 16) / 255;
|
|
105
|
-
g = parseInt(h.slice(2, 4), 16) / 255;
|
|
106
|
-
b = parseInt(h.slice(4, 6), 16) / 255;
|
|
107
|
-
a = parseInt(h.slice(6, 8), 16) / 255;
|
|
108
|
-
} else {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return { r, g, b, a };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function parseComponent(value: string, max: number): number {
|
|
116
|
-
if (value.endsWith('%')) {
|
|
117
|
-
return (parseFloat(value) / 100) * max;
|
|
118
|
-
}
|
|
119
|
-
return parseFloat(value);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Default color: opaque black */
|
|
123
|
-
export const BLACK: RGBA = { r: 0, g: 0, b: 0, a: 1 };
|
|
124
|
-
/** Transparent black */
|
|
125
|
-
export const TRANSPARENT: RGBA = { r: 0, g: 0, b: 0, a: 0 };
|
package/src/image-data.ts
DELETED