@gjsify/canvas2d 0.1.0

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.
@@ -0,0 +1,39 @@
1
+ import { BLACK } from "./color.js";
2
+ function createDefaultState() {
3
+ return {
4
+ fillStyle: "#000000",
5
+ fillColor: { ...BLACK },
6
+ strokeStyle: "#000000",
7
+ strokeColor: { ...BLACK },
8
+ lineWidth: 1,
9
+ lineCap: "butt",
10
+ lineJoin: "miter",
11
+ miterLimit: 10,
12
+ lineDash: [],
13
+ lineDashOffset: 0,
14
+ globalAlpha: 1,
15
+ globalCompositeOperation: "source-over",
16
+ shadowColor: "rgba(0, 0, 0, 0)",
17
+ shadowBlur: 0,
18
+ shadowOffsetX: 0,
19
+ shadowOffsetY: 0,
20
+ font: "10px sans-serif",
21
+ textAlign: "start",
22
+ textBaseline: "alphabetic",
23
+ direction: "ltr",
24
+ imageSmoothingEnabled: true,
25
+ imageSmoothingQuality: "low"
26
+ };
27
+ }
28
+ function cloneState(state) {
29
+ return {
30
+ ...state,
31
+ fillColor: { ...state.fillColor },
32
+ strokeColor: { ...state.strokeColor },
33
+ lineDash: [...state.lineDash]
34
+ };
35
+ }
36
+ export {
37
+ cloneState,
38
+ createDefaultState
39
+ };
@@ -0,0 +1,209 @@
1
+ const NAMED_COLORS = {
2
+ aliceblue: "#f0f8ff",
3
+ antiquewhite: "#faebd7",
4
+ aqua: "#00ffff",
5
+ aquamarine: "#7fffd4",
6
+ azure: "#f0ffff",
7
+ beige: "#f5f5dc",
8
+ bisque: "#ffe4c4",
9
+ black: "#000000",
10
+ blanchedalmond: "#ffebcd",
11
+ blue: "#0000ff",
12
+ blueviolet: "#8a2be2",
13
+ brown: "#a52a2a",
14
+ burlywood: "#deb887",
15
+ cadetblue: "#5f9ea0",
16
+ chartreuse: "#7fff00",
17
+ chocolate: "#d2691e",
18
+ coral: "#ff7f50",
19
+ cornflowerblue: "#6495ed",
20
+ cornsilk: "#fff8dc",
21
+ crimson: "#dc143c",
22
+ cyan: "#00ffff",
23
+ darkblue: "#00008b",
24
+ darkcyan: "#008b8b",
25
+ darkgoldenrod: "#b8860b",
26
+ darkgray: "#a9a9a9",
27
+ darkgreen: "#006400",
28
+ darkgrey: "#a9a9a9",
29
+ darkkhaki: "#bdb76b",
30
+ darkmagenta: "#8b008b",
31
+ darkolivegreen: "#556b2f",
32
+ darkorange: "#ff8c00",
33
+ darkorchid: "#9932cc",
34
+ darkred: "#8b0000",
35
+ darksalmon: "#e9967a",
36
+ darkseagreen: "#8fbc8f",
37
+ darkslateblue: "#483d8b",
38
+ darkslategray: "#2f4f4f",
39
+ darkslategrey: "#2f4f4f",
40
+ darkturquoise: "#00ced1",
41
+ darkviolet: "#9400d3",
42
+ deeppink: "#ff1493",
43
+ deepskyblue: "#00bfff",
44
+ dimgray: "#696969",
45
+ dimgrey: "#696969",
46
+ dodgerblue: "#1e90ff",
47
+ firebrick: "#b22222",
48
+ floralwhite: "#fffaf0",
49
+ forestgreen: "#228b22",
50
+ fuchsia: "#ff00ff",
51
+ gainsboro: "#dcdcdc",
52
+ ghostwhite: "#f8f8ff",
53
+ gold: "#ffd700",
54
+ goldenrod: "#daa520",
55
+ gray: "#808080",
56
+ green: "#008000",
57
+ greenyellow: "#adff2f",
58
+ grey: "#808080",
59
+ honeydew: "#f0fff0",
60
+ hotpink: "#ff69b4",
61
+ indianred: "#cd5c5c",
62
+ indigo: "#4b0082",
63
+ ivory: "#fffff0",
64
+ khaki: "#f0e68c",
65
+ lavender: "#e6e6fa",
66
+ lavenderblush: "#fff0f5",
67
+ lawngreen: "#7cfc00",
68
+ lemonchiffon: "#fffacd",
69
+ lightblue: "#add8e6",
70
+ lightcoral: "#f08080",
71
+ lightcyan: "#e0ffff",
72
+ lightgoldenrodyellow: "#fafad2",
73
+ lightgray: "#d3d3d3",
74
+ lightgreen: "#90ee90",
75
+ lightgrey: "#d3d3d3",
76
+ lightpink: "#ffb6c1",
77
+ lightsalmon: "#ffa07a",
78
+ lightseagreen: "#20b2aa",
79
+ lightskyblue: "#87cefa",
80
+ lightslategray: "#778899",
81
+ lightslategrey: "#778899",
82
+ lightsteelblue: "#b0c4de",
83
+ lightyellow: "#ffffe0",
84
+ lime: "#00ff00",
85
+ limegreen: "#32cd32",
86
+ linen: "#faf0e6",
87
+ magenta: "#ff00ff",
88
+ maroon: "#800000",
89
+ mediumaquamarine: "#66cdaa",
90
+ mediumblue: "#0000cd",
91
+ mediumorchid: "#ba55d3",
92
+ mediumpurple: "#9370db",
93
+ mediumseagreen: "#3cb371",
94
+ mediumslateblue: "#7b68ee",
95
+ mediumspringgreen: "#00fa9a",
96
+ mediumturquoise: "#48d1cc",
97
+ mediumvioletred: "#c71585",
98
+ midnightblue: "#191970",
99
+ mintcream: "#f5fffa",
100
+ mistyrose: "#ffe4e1",
101
+ moccasin: "#ffe4b5",
102
+ navajowhite: "#ffdead",
103
+ navy: "#000080",
104
+ oldlace: "#fdf5e6",
105
+ olive: "#808000",
106
+ olivedrab: "#6b8e23",
107
+ orange: "#ffa500",
108
+ orangered: "#ff4500",
109
+ orchid: "#da70d6",
110
+ palegoldenrod: "#eee8aa",
111
+ palegreen: "#98fb98",
112
+ paleturquoise: "#afeeee",
113
+ palevioletred: "#db7093",
114
+ papayawhip: "#ffefd5",
115
+ peachpuff: "#ffdab9",
116
+ peru: "#cd853f",
117
+ pink: "#ffc0cb",
118
+ plum: "#dda0dd",
119
+ powderblue: "#b0e0e6",
120
+ purple: "#800080",
121
+ rebeccapurple: "#663399",
122
+ red: "#ff0000",
123
+ rosybrown: "#bc8f8f",
124
+ royalblue: "#4169e1",
125
+ saddlebrown: "#8b4513",
126
+ salmon: "#fa8072",
127
+ sandybrown: "#f4a460",
128
+ seagreen: "#2e8b57",
129
+ seashell: "#fff5ee",
130
+ sienna: "#a0522d",
131
+ silver: "#c0c0c0",
132
+ skyblue: "#87ceeb",
133
+ slateblue: "#6a5acd",
134
+ slategray: "#708090",
135
+ slategrey: "#708090",
136
+ snow: "#fffafa",
137
+ springgreen: "#00ff7f",
138
+ steelblue: "#4682b4",
139
+ tan: "#d2b48c",
140
+ teal: "#008080",
141
+ thistle: "#d8bfd8",
142
+ tomato: "#ff6347",
143
+ turquoise: "#40e0d0",
144
+ violet: "#ee82ee",
145
+ wheat: "#f5deb3",
146
+ white: "#ffffff",
147
+ whitesmoke: "#f5f5f5",
148
+ yellow: "#ffff00",
149
+ yellowgreen: "#9acd32",
150
+ transparent: "#00000000"
151
+ };
152
+ function parseColor(color) {
153
+ if (!color || typeof color !== "string") return null;
154
+ const trimmed = color.trim().toLowerCase();
155
+ const named = NAMED_COLORS[trimmed];
156
+ if (named) return parseHex(named);
157
+ if (trimmed.startsWith("#")) return parseHex(trimmed);
158
+ const rgbMatch = trimmed.match(
159
+ /^rgba?\(\s*(\d+(?:\.\d+)?%?)\s*[,\s]\s*(\d+(?:\.\d+)?%?)\s*[,\s]\s*(\d+(?:\.\d+)?%?)\s*(?:[,/]\s*(\d+(?:\.\d+)?%?))?\s*\)$/
160
+ );
161
+ if (rgbMatch) {
162
+ return {
163
+ r: parseComponent(rgbMatch[1], 255) / 255,
164
+ g: parseComponent(rgbMatch[2], 255) / 255,
165
+ b: parseComponent(rgbMatch[3], 255) / 255,
166
+ a: rgbMatch[4] !== void 0 ? parseComponent(rgbMatch[4], 1) : 1
167
+ };
168
+ }
169
+ return null;
170
+ }
171
+ function parseHex(hex) {
172
+ const h = hex.slice(1);
173
+ let r, g, b, a = 1;
174
+ if (h.length === 3) {
175
+ r = parseInt(h[0] + h[0], 16) / 255;
176
+ g = parseInt(h[1] + h[1], 16) / 255;
177
+ b = parseInt(h[2] + h[2], 16) / 255;
178
+ } else if (h.length === 4) {
179
+ r = parseInt(h[0] + h[0], 16) / 255;
180
+ g = parseInt(h[1] + h[1], 16) / 255;
181
+ b = parseInt(h[2] + h[2], 16) / 255;
182
+ a = parseInt(h[3] + h[3], 16) / 255;
183
+ } else if (h.length === 6) {
184
+ r = parseInt(h.slice(0, 2), 16) / 255;
185
+ g = parseInt(h.slice(2, 4), 16) / 255;
186
+ b = parseInt(h.slice(4, 6), 16) / 255;
187
+ } else if (h.length === 8) {
188
+ r = parseInt(h.slice(0, 2), 16) / 255;
189
+ g = parseInt(h.slice(2, 4), 16) / 255;
190
+ b = parseInt(h.slice(4, 6), 16) / 255;
191
+ a = parseInt(h.slice(6, 8), 16) / 255;
192
+ } else {
193
+ return null;
194
+ }
195
+ return { r, g, b, a };
196
+ }
197
+ function parseComponent(value, max) {
198
+ if (value.endsWith("%")) {
199
+ return parseFloat(value) / 100 * max;
200
+ }
201
+ return parseFloat(value);
202
+ }
203
+ const BLACK = { r: 0, g: 0, b: 0, a: 1 };
204
+ const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 };
205
+ export {
206
+ BLACK,
207
+ TRANSPARENT,
208
+ parseColor
209
+ };
@@ -0,0 +1,22 @@
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
+ }
20
+ export {
21
+ OurImageData
22
+ };
@@ -0,0 +1,43 @@
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";
7
+ import { Canvas2DWidget } from "./canvas-drawing-area.js";
8
+ import { HTMLCanvasElement as GjsifyHTMLCanvasElement } from "@gjsify/dom-elements";
9
+ import { CanvasRenderingContext2D as CanvasRenderingContext2D2 } from "./canvas-rendering-context-2d.js";
10
+ const CONTEXT_KEY = /* @__PURE__ */ Symbol.for("gjsify_canvas2d_context");
11
+ GjsifyHTMLCanvasElement.registerContextFactory("2d", (canvas, options) => {
12
+ const existing = canvas[CONTEXT_KEY];
13
+ if (existing) return existing;
14
+ const ctx = new CanvasRenderingContext2D2(canvas, options);
15
+ canvas[CONTEXT_KEY] = ctx;
16
+ return ctx;
17
+ });
18
+ import { OurImageData as OurImageData2 } from "./image-data.js";
19
+ import { Path2D as Path2D2 } from "./canvas-path.js";
20
+ Object.defineProperty(globalThis, "CanvasRenderingContext2D", {
21
+ value: CanvasRenderingContext2D2,
22
+ writable: true,
23
+ configurable: true
24
+ });
25
+ Object.defineProperty(globalThis, "ImageData", {
26
+ value: OurImageData2,
27
+ writable: true,
28
+ configurable: true
29
+ });
30
+ Object.defineProperty(globalThis, "Path2D", {
31
+ value: Path2D2,
32
+ writable: true,
33
+ configurable: true
34
+ });
35
+ export {
36
+ Canvas2DWidget,
37
+ CanvasGradient,
38
+ CanvasPattern,
39
+ CanvasRenderingContext2D,
40
+ OurImageData as ImageData,
41
+ Path2D,
42
+ parseColor
43
+ };
@@ -0,0 +1,54 @@
1
+ import type Cairo from 'cairo';
2
+ /**
3
+ * Convert quadratic Bezier control point to cubic Bezier control points.
4
+ * Canvas 2D has quadraticCurveTo but Cairo only has cubic curveTo.
5
+ *
6
+ * Given current point (cx, cy), quadratic control point (cpx, cpy), and end (x, y):
7
+ * cp1 = current + 2/3 * (cp - current)
8
+ * cp2 = end + 2/3 * (cp - end)
9
+ */
10
+ export declare function quadraticToCubic(cx: number, cy: number, cpx: number, cpy: number, x: number, y: number): {
11
+ cp1x: number;
12
+ cp1y: number;
13
+ cp2x: number;
14
+ cp2y: number;
15
+ };
16
+ /**
17
+ * Compute arcTo parameters.
18
+ * Canvas arcTo(x1,y1,x2,y2,radius) draws a line from current point to the tangent point,
19
+ * then an arc of the given radius tangent to both lines (current→p1 and p1→p2).
20
+ *
21
+ * Returns the two tangent points and arc center, or null if degenerate (collinear points).
22
+ */
23
+ export declare function computeArcTo(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, radius: number): {
24
+ tx0: number;
25
+ ty0: number;
26
+ tx1: number;
27
+ ty1: number;
28
+ cx: number;
29
+ cy: number;
30
+ startAngle: number;
31
+ endAngle: number;
32
+ counterclockwise: boolean;
33
+ } | null;
34
+ /**
35
+ * Apply an arcTo operation to a Cairo context.
36
+ */
37
+ export declare function cairoArcTo(ctx: Cairo.Context, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, radius: number): void;
38
+ /**
39
+ * Draw an ellipse on a Cairo context.
40
+ * Canvas ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise)
41
+ * is implemented via save/translate/rotate/scale/arc/restore.
42
+ */
43
+ export declare function cairoEllipse(ctx: Cairo.Context, x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise: boolean): void;
44
+ /**
45
+ * Draw a rounded rectangle path on a Cairo context.
46
+ * Implements the Canvas roundRect(x, y, w, h, radii) method.
47
+ */
48
+ export declare function cairoRoundRect(ctx: Cairo.Context, x: number, y: number, w: number, h: number, radii: number | number[]): void;
49
+ /** Map Canvas globalCompositeOperation to Cairo.Operator values */
50
+ export declare const COMPOSITE_OP_MAP: Record<string, number>;
51
+ /** Map Canvas lineCap to Cairo.LineCap values */
52
+ export declare const LINE_CAP_MAP: Record<string, number>;
53
+ /** Map Canvas lineJoin to Cairo.LineJoin values */
54
+ export declare const LINE_JOIN_MAP: Record<string, number>;