@nmmty/lazycanvas 0.6.5 → 1.0.0-dev.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/ReadMe.md +1 -1
- package/biome.json +41 -0
- package/dist/core/Interpolation.d.ts +30 -0
- package/dist/core/Interpolation.js +200 -0
- package/dist/core/Scene.d.ts +96 -0
- package/dist/core/Scene.js +172 -0
- package/dist/core/Signal.d.ts +133 -0
- package/dist/core/Signal.js +255 -0
- package/dist/core/SignalUtils.d.ts +133 -0
- package/dist/core/SignalUtils.js +333 -0
- package/dist/core/ThreadScheduler.d.ts +38 -0
- package/dist/core/ThreadScheduler.js +74 -0
- package/dist/helpers/Filters.js +1 -1
- package/dist/helpers/FontsList.js +18 -18
- package/dist/helpers/Utlis.d.ts +3 -3
- package/dist/helpers/Utlis.js +15 -18
- package/dist/helpers/index.d.ts +3 -3
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/jsx-runtime.d.ts +17 -0
- package/dist/jsx-runtime.js +111 -0
- package/dist/structures/LazyCanvas.d.ts +3 -45
- package/dist/structures/LazyCanvas.js +11 -74
- package/dist/structures/components/BaseLayer.d.ts +34 -12
- package/dist/structures/components/BaseLayer.js +68 -35
- package/dist/structures/components/BezierLayer.d.ts +16 -37
- package/dist/structures/components/BezierLayer.js +83 -46
- package/dist/structures/components/{Group.d.ts → Div.d.ts} +22 -16
- package/dist/structures/components/{Group.js → Div.js} +38 -39
- package/dist/structures/components/ImageLayer.d.ts +1 -1
- package/dist/structures/components/ImageLayer.js +24 -25
- package/dist/structures/components/LineLayer.d.ts +11 -37
- package/dist/structures/components/LineLayer.js +42 -42
- package/dist/structures/components/MorphLayer.d.ts +3 -32
- package/dist/structures/components/MorphLayer.js +32 -46
- package/dist/structures/components/Path2DLayer.d.ts +4 -32
- package/dist/structures/components/Path2DLayer.js +28 -33
- package/dist/structures/components/PolygonLayer.d.ts +2 -31
- package/dist/structures/components/PolygonLayer.js +35 -38
- package/dist/structures/components/QuadraticLayer.d.ts +16 -33
- package/dist/structures/components/QuadraticLayer.js +80 -42
- package/dist/structures/components/TextLayer.d.ts +4 -33
- package/dist/structures/components/TextLayer.js +60 -62
- package/dist/structures/components/index.d.ts +10 -11
- package/dist/structures/components/index.js +1 -2
- package/dist/structures/helpers/Exporter.d.ts +13 -4
- package/dist/structures/helpers/Exporter.js +79 -42
- package/dist/structures/helpers/Font.js +1 -17
- package/dist/structures/helpers/Gradient.js +32 -45
- package/dist/structures/helpers/Link.js +2 -14
- package/dist/structures/helpers/Pattern.js +9 -17
- package/dist/structures/helpers/index.d.ts +7 -7
- package/dist/structures/helpers/readers/JSONReader.d.ts +4 -4
- package/dist/structures/helpers/readers/JSONReader.js +32 -40
- package/dist/structures/helpers/readers/YAMLReader.js +5 -5
- package/dist/structures/managers/FontsManager.js +9 -18
- package/dist/structures/managers/LayersManager.d.ts +18 -28
- package/dist/structures/managers/LayersManager.js +14 -36
- package/dist/structures/managers/RenderManager.d.ts +1 -15
- package/dist/structures/managers/RenderManager.js +17 -110
- package/dist/structures/managers/index.d.ts +3 -5
- package/dist/structures/managers/index.js +0 -2
- package/dist/types/enum.d.ts +1 -2
- package/dist/types/enum.js +1 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/APNGEncoder.d.ts +67 -0
- package/dist/utils/APNGEncoder.js +205 -0
- package/dist/utils/DrawUtils.d.ts +9 -0
- package/dist/utils/DrawUtils.js +42 -0
- package/dist/utils/LazyUtil.js +1 -2
- package/dist/utils/utils.d.ts +4 -7
- package/dist/utils/utils.js +133 -76
- package/package.json +62 -59
- package/dist/structures/components/ClearLayer.d.ts +0 -147
- package/dist/structures/components/ClearLayer.js +0 -158
- package/dist/structures/managers/AnimationManager.d.ts +0 -120
- package/dist/structures/managers/AnimationManager.js +0 -99
- package/dist/structures/managers/PluginManager.d.ts +0 -230
- package/dist/structures/managers/PluginManager.js +0 -182
- package/dist/types/types.d.ts +0 -107
|
@@ -7,50 +7,32 @@ const LazyUtil_1 = require("../../utils/LazyUtil");
|
|
|
7
7
|
* Class representing a manager for handling layers and groups.
|
|
8
8
|
*/
|
|
9
9
|
class LayersManager {
|
|
10
|
-
/**
|
|
11
|
-
* The LazyCanvas instance associated with this manager.
|
|
12
|
-
*/
|
|
13
|
-
lazyCanvas;
|
|
14
|
-
/**
|
|
15
|
-
* A map storing layers or groups with their IDs as keys.
|
|
16
|
-
*/
|
|
17
|
-
map;
|
|
18
|
-
/**
|
|
19
|
-
* Whether debugging is enabled.
|
|
20
|
-
*/
|
|
21
|
-
debug;
|
|
22
10
|
/**
|
|
23
11
|
* Constructs a new LayersManager instance.
|
|
24
|
-
* @param {LazyCanvas} [lazyCanvas] - The LazyCanvas instance to associate with this manager.
|
|
25
12
|
* @param {Object} [opts] - Optional settings for the LayersManager.
|
|
26
13
|
* @param {boolean} [opts.debug] - Whether debugging is enabled.
|
|
27
14
|
*/
|
|
28
|
-
constructor(
|
|
29
|
-
this.lazyCanvas = lazyCanvas;
|
|
15
|
+
constructor(opts) {
|
|
30
16
|
this.map = new Map();
|
|
31
17
|
this.debug = opts?.debug || false;
|
|
32
18
|
}
|
|
33
19
|
/**
|
|
34
20
|
* Adds layers or groups to the map.
|
|
35
|
-
* @param {Array<AnyLayer |
|
|
21
|
+
* @param {Array<AnyLayer | Div>} [layers] - The layers or groups to add to the map.
|
|
36
22
|
* @returns {this} The current instance for chaining.
|
|
37
23
|
* @throws {LazyError} If a layer with the same ID already exists.
|
|
38
24
|
*/
|
|
39
25
|
add(...layers) {
|
|
40
26
|
if (this.debug)
|
|
41
|
-
LazyUtil_1.LazyLog.log(
|
|
27
|
+
LazyUtil_1.LazyLog.log("info", `Adding layers...\nlength: ${layers.length}`);
|
|
42
28
|
let layersArray = layers.flat();
|
|
43
|
-
layersArray = layersArray.filter(l => l !== undefined);
|
|
29
|
+
layersArray = layersArray.filter((l) => l !== undefined);
|
|
44
30
|
for (const layer of layersArray) {
|
|
45
31
|
if (this.debug)
|
|
46
|
-
LazyUtil_1.LazyLog.log(
|
|
32
|
+
LazyUtil_1.LazyLog.log("none", `Data:`, "toJSON" in layer ? layer.toJSON() : layer);
|
|
47
33
|
if (this.map.has(layer.id))
|
|
48
34
|
throw new LazyUtil_1.LazyError("Layer already exists");
|
|
49
35
|
this.map.set(layer.id, layer);
|
|
50
|
-
// onLayerAdded hook
|
|
51
|
-
if (this.lazyCanvas && this.lazyCanvas.manager?.plugins) {
|
|
52
|
-
this.lazyCanvas.manager.plugins.executeHook('onLayerAdded', this.lazyCanvas, layer);
|
|
53
|
-
}
|
|
54
36
|
}
|
|
55
37
|
this.sort();
|
|
56
38
|
return this;
|
|
@@ -64,10 +46,6 @@ class LayersManager {
|
|
|
64
46
|
for (const id of ids) {
|
|
65
47
|
const layer = this.map.get(id);
|
|
66
48
|
this.map.delete(id);
|
|
67
|
-
// onLayerRemoved hook
|
|
68
|
-
if (this.lazyCanvas && this.lazyCanvas.manager?.plugins) {
|
|
69
|
-
this.lazyCanvas.manager.plugins.executeHook('onLayerRemoved', this.lazyCanvas, id);
|
|
70
|
-
}
|
|
71
49
|
}
|
|
72
50
|
return this;
|
|
73
51
|
}
|
|
@@ -83,7 +61,7 @@ class LayersManager {
|
|
|
83
61
|
* Retrieves a layer or group from the map by its ID.
|
|
84
62
|
* @param {string} [id] - The ID of the layer or group to retrieve.
|
|
85
63
|
* @param {boolean} [cross] - Whether to search within groups for the ID.
|
|
86
|
-
* @returns {AnyLayer |
|
|
64
|
+
* @returns {AnyLayer | Div | undefined} The retrieved layer or group, or undefined if not found.
|
|
87
65
|
*/
|
|
88
66
|
get(id, cross = false) {
|
|
89
67
|
if (cross)
|
|
@@ -108,7 +86,7 @@ class LayersManager {
|
|
|
108
86
|
}
|
|
109
87
|
/**
|
|
110
88
|
* Retrieves the values (layers and groups) from the map.
|
|
111
|
-
* @returns {IterableIterator<AnyLayer |
|
|
89
|
+
* @returns {IterableIterator<AnyLayer | Div>} An iterator for the map values.
|
|
112
90
|
*/
|
|
113
91
|
values() {
|
|
114
92
|
return this.map.values();
|
|
@@ -122,7 +100,7 @@ class LayersManager {
|
|
|
122
100
|
}
|
|
123
101
|
/**
|
|
124
102
|
* Retrieves the entries (key-value pairs) from the map.
|
|
125
|
-
* @returns {IterableIterator<[string, AnyLayer |
|
|
103
|
+
* @returns {IterableIterator<[string, AnyLayer | Div]>} An iterator for the map entries.
|
|
126
104
|
*/
|
|
127
105
|
entries() {
|
|
128
106
|
return this.map.entries();
|
|
@@ -154,18 +132,18 @@ class LayersManager {
|
|
|
154
132
|
}
|
|
155
133
|
/**
|
|
156
134
|
* Converts the map to an array of layers and groups.
|
|
157
|
-
* @returns {Array<AnyLayer |
|
|
135
|
+
* @returns {Array<AnyLayer | Div>} An array of layers and groups.
|
|
158
136
|
*/
|
|
159
137
|
toArray() {
|
|
160
138
|
return Array.from(this.map.values());
|
|
161
139
|
}
|
|
162
140
|
/**
|
|
163
141
|
* Populates the map from an array of layers and groups.
|
|
164
|
-
* @param {Array<AnyLayer |
|
|
142
|
+
* @param {Array<AnyLayer | Div>} [array] - The array of layers and groups to populate the map from.
|
|
165
143
|
* @returns {this} The current instance for chaining.
|
|
166
144
|
*/
|
|
167
145
|
fromArray(array) {
|
|
168
|
-
this.map = new Map(array.map(l => [l.id, l]));
|
|
146
|
+
this.map = new Map(array.map((l) => [l.id, l]));
|
|
169
147
|
return this;
|
|
170
148
|
}
|
|
171
149
|
/**
|
|
@@ -178,14 +156,14 @@ class LayersManager {
|
|
|
178
156
|
/**
|
|
179
157
|
* Searches for a layer or group by its ID, including within groups.
|
|
180
158
|
* @param {string} [id] - The ID of the layer or group to search for.
|
|
181
|
-
* @returns {AnyLayer |
|
|
159
|
+
* @returns {AnyLayer | Div | undefined} The found layer or group, or undefined if not found.
|
|
182
160
|
*/
|
|
183
161
|
crossSearch(id) {
|
|
184
162
|
for (const layer of Array.from(this.map.values())) {
|
|
185
163
|
if (layer.id === id)
|
|
186
164
|
return layer;
|
|
187
|
-
if (layer instanceof components_1.
|
|
188
|
-
const result = layer.layers.find(l => l.id === id);
|
|
165
|
+
if (layer instanceof components_1.Div) {
|
|
166
|
+
const result = layer.layers.find((l) => l.id === id);
|
|
189
167
|
if (result)
|
|
190
168
|
return result;
|
|
191
169
|
}
|
|
@@ -35,18 +35,9 @@ export declare class RenderManager implements IRenderManager {
|
|
|
35
35
|
constructor(lazyCanvas: LazyCanvas, opts?: {
|
|
36
36
|
debug?: boolean;
|
|
37
37
|
});
|
|
38
|
-
/**
|
|
39
|
-
* Merges multiple ImageData objects into a single ImageData object.
|
|
40
|
-
* @param {SKRSContext2D} [ctx] - The canvas rendering context.
|
|
41
|
-
* @param {ImageData[]} [imageDataList] - The list of ImageData objects to merge.
|
|
42
|
-
* @param {number} [width] - The width of the resulting ImageData.
|
|
43
|
-
* @param {number} [height] - The height of the resulting ImageData.
|
|
44
|
-
* @returns {ImageData} The merged ImageData object.
|
|
45
|
-
*/
|
|
46
|
-
private mergeImageData;
|
|
47
38
|
/**
|
|
48
39
|
* Renders a single layer or group of layers.
|
|
49
|
-
* @param {AnyLayer |
|
|
40
|
+
* @param {AnyLayer | Div} [layer] - The layer or group to render.
|
|
50
41
|
* @returns {Promise<SKRSContext2D>} The canvas rendering context after rendering.
|
|
51
42
|
*/
|
|
52
43
|
private renderLayer;
|
|
@@ -56,11 +47,6 @@ export declare class RenderManager implements IRenderManager {
|
|
|
56
47
|
* @returns {Promise<Buffer | SKRSContext2D | string>} The rendered output in the specified format.
|
|
57
48
|
*/
|
|
58
49
|
private renderStatic;
|
|
59
|
-
/**
|
|
60
|
-
* Renders an animated sequence of layers and exports it as a GIF.
|
|
61
|
-
* @returns {Promise<Buffer>} The rendered animation as a Buffer.
|
|
62
|
-
*/
|
|
63
|
-
private renderAnimation;
|
|
64
50
|
/**
|
|
65
51
|
* Renders all layers and exports the result in the specified format.
|
|
66
52
|
* @param {AnyExport} [format] - The export format (e.g., buffer, context, SVG, or canvas).
|
|
@@ -3,20 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RenderManager = void 0;
|
|
4
4
|
const types_1 = require("../../types");
|
|
5
5
|
const LazyUtil_1 = require("../../utils/LazyUtil");
|
|
6
|
-
// @ts-ignore
|
|
7
|
-
const gifenc_1 = require("gifenc");
|
|
8
6
|
/**
|
|
9
7
|
* Class responsible for managing rendering operations, including static and animated exports.
|
|
10
8
|
*/
|
|
11
9
|
class RenderManager {
|
|
12
|
-
/**
|
|
13
|
-
* The LazyCanvas instance used for rendering.
|
|
14
|
-
*/
|
|
15
|
-
lazyCanvas;
|
|
16
|
-
/**
|
|
17
|
-
* Whether debugging is enabled.
|
|
18
|
-
*/
|
|
19
|
-
debug;
|
|
20
10
|
/**
|
|
21
11
|
* Constructs a new RenderManager instance.
|
|
22
12
|
* @param {LazyCanvas} [lazyCanvas] - The LazyCanvas instance to use for rendering.
|
|
@@ -27,48 +17,18 @@ class RenderManager {
|
|
|
27
17
|
this.lazyCanvas = lazyCanvas;
|
|
28
18
|
this.debug = opts?.debug || false;
|
|
29
19
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Merges multiple ImageData objects into a single ImageData object.
|
|
32
|
-
* @param {SKRSContext2D} [ctx] - The canvas rendering context.
|
|
33
|
-
* @param {ImageData[]} [imageDataList] - The list of ImageData objects to merge.
|
|
34
|
-
* @param {number} [width] - The width of the resulting ImageData.
|
|
35
|
-
* @param {number} [height] - The height of the resulting ImageData.
|
|
36
|
-
* @returns {ImageData} The merged ImageData object.
|
|
37
|
-
*/
|
|
38
|
-
mergeImageData(ctx, imageDataList, width, height) {
|
|
39
|
-
const mergedData = ctx.createImageData(width, height);
|
|
40
|
-
const mergedPixels = mergedData.data;
|
|
41
|
-
for (const imageData of imageDataList) {
|
|
42
|
-
const pixels = imageData.data;
|
|
43
|
-
for (let i = 0; i < pixels.length; i += 4) {
|
|
44
|
-
const r = pixels[i];
|
|
45
|
-
const g = pixels[i + 1];
|
|
46
|
-
const b = pixels[i + 2];
|
|
47
|
-
const a = pixels[i + 3] / 255;
|
|
48
|
-
const existingAlpha = mergedPixels[i + 3] / 255;
|
|
49
|
-
const newAlpha = a + existingAlpha * (1 - a);
|
|
50
|
-
if (newAlpha > 0) {
|
|
51
|
-
mergedPixels[i] = (r * a + mergedPixels[i] * existingAlpha * (1 - a)) / newAlpha;
|
|
52
|
-
mergedPixels[i + 1] = (g * a + mergedPixels[i + 1] * existingAlpha * (1 - a)) / newAlpha;
|
|
53
|
-
mergedPixels[i + 2] = (b * a + mergedPixels[i + 2] * existingAlpha * (1 - a)) / newAlpha;
|
|
54
|
-
mergedPixels[i + 3] = newAlpha * 255;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return mergedData;
|
|
59
|
-
}
|
|
60
20
|
/**
|
|
61
21
|
* Renders a single layer or group of layers.
|
|
62
|
-
* @param {AnyLayer |
|
|
22
|
+
* @param {AnyLayer | Div} [layer] - The layer or group to render.
|
|
63
23
|
* @returns {Promise<SKRSContext2D>} The canvas rendering context after rendering.
|
|
64
24
|
*/
|
|
65
25
|
async renderLayer(layer) {
|
|
66
26
|
if (this.debug)
|
|
67
|
-
LazyUtil_1.LazyLog.log(
|
|
27
|
+
LazyUtil_1.LazyLog.log("info", `Rendering ${layer.id}...\nData:`, layer.toJSON());
|
|
68
28
|
if (layer.visible) {
|
|
69
|
-
this.lazyCanvas.ctx.globalCompositeOperation = layer.props?.globalComposite ||
|
|
29
|
+
this.lazyCanvas.ctx.globalCompositeOperation = layer.props?.globalComposite || "source-over";
|
|
70
30
|
await layer.draw(this.lazyCanvas.ctx, this.lazyCanvas.canvas, this.lazyCanvas.manager.layers, this.debug);
|
|
71
|
-
this.lazyCanvas.ctx.shadowColor =
|
|
31
|
+
this.lazyCanvas.ctx.shadowColor = "transparent";
|
|
72
32
|
}
|
|
73
33
|
return this.lazyCanvas.ctx;
|
|
74
34
|
}
|
|
@@ -79,7 +39,7 @@ class RenderManager {
|
|
|
79
39
|
*/
|
|
80
40
|
async renderStatic(exportType) {
|
|
81
41
|
if (this.debug)
|
|
82
|
-
LazyUtil_1.LazyLog.log(
|
|
42
|
+
LazyUtil_1.LazyLog.log("info", `Rendering static...`);
|
|
83
43
|
for (const layer of this.lazyCanvas.manager.layers.toArray()) {
|
|
84
44
|
await this.renderLayer(layer);
|
|
85
45
|
}
|
|
@@ -88,96 +48,43 @@ class RenderManager {
|
|
|
88
48
|
case "buffer":
|
|
89
49
|
case types_1.Export.SVG:
|
|
90
50
|
case "svg":
|
|
91
|
-
if (
|
|
92
|
-
return this.lazyCanvas.canvas.getContent().toString(
|
|
51
|
+
if ("getContent" in this.lazyCanvas.canvas) {
|
|
52
|
+
return this.lazyCanvas.canvas.getContent().toString("utf8");
|
|
93
53
|
}
|
|
94
|
-
return this.lazyCanvas.canvas.toBuffer(
|
|
54
|
+
return this.lazyCanvas.canvas.toBuffer("image/png");
|
|
95
55
|
case types_1.Export.CTX:
|
|
96
56
|
case "ctx":
|
|
97
57
|
return this.lazyCanvas.ctx;
|
|
98
58
|
default:
|
|
99
|
-
if (
|
|
100
|
-
return this.lazyCanvas.canvas.getContent().toString(
|
|
59
|
+
if ("getContent" in this.lazyCanvas.canvas) {
|
|
60
|
+
return this.lazyCanvas.canvas.getContent().toString("utf8");
|
|
101
61
|
}
|
|
102
|
-
return this.lazyCanvas.canvas.toBuffer(
|
|
62
|
+
return this.lazyCanvas.canvas.toBuffer("image/png");
|
|
103
63
|
}
|
|
104
64
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Renders an animated sequence of layers and exports it as a GIF.
|
|
107
|
-
* @returns {Promise<Buffer>} The rendered animation as a Buffer.
|
|
108
|
-
*/
|
|
109
|
-
async renderAnimation() {
|
|
110
|
-
const encoder = new gifenc_1.GIFEncoder();
|
|
111
|
-
if (this.debug)
|
|
112
|
-
LazyUtil_1.LazyLog.log('info', `Rendering animation...\nData:`, this.lazyCanvas.manager.animation.options);
|
|
113
|
-
const frameBuffer = [];
|
|
114
|
-
const { width, height } = this.lazyCanvas.options;
|
|
115
|
-
const delay = 1000 / this.lazyCanvas.manager.animation.options.frameRate;
|
|
116
|
-
const { loop, colorSpace, maxColors, transparency, utils } = this.lazyCanvas.manager.animation.options;
|
|
117
|
-
let frameNumber = 0;
|
|
118
|
-
for (const layer of this.lazyCanvas.manager.layers.toArray()) {
|
|
119
|
-
// onAnimationFrame hook
|
|
120
|
-
this.lazyCanvas.manager.plugins.executeHook('onAnimationFrame', this.lazyCanvas, frameNumber);
|
|
121
|
-
const ctx = await this.renderLayer(layer);
|
|
122
|
-
frameBuffer.push(ctx.getImageData(0, 0, width, height));
|
|
123
|
-
if (frameBuffer.length > utils.buffer.size) {
|
|
124
|
-
frameBuffer.shift();
|
|
125
|
-
}
|
|
126
|
-
const mergeData = this.mergeImageData(ctx, frameBuffer, width, height);
|
|
127
|
-
const palette = (0, gifenc_1.quantize)(mergeData.data, maxColors, { format: colorSpace });
|
|
128
|
-
const index = (0, gifenc_1.applyPalette)(mergeData.data, palette, colorSpace);
|
|
129
|
-
encoder.writeFrame(index, width, height, {
|
|
130
|
-
palette,
|
|
131
|
-
transparent: transparency,
|
|
132
|
-
delay,
|
|
133
|
-
loop
|
|
134
|
-
});
|
|
135
|
-
if (utils.clear)
|
|
136
|
-
ctx.clearRect(0, 0, width, height);
|
|
137
|
-
frameNumber++;
|
|
138
|
-
}
|
|
139
|
-
encoder.finish();
|
|
140
|
-
return encoder.bytesView();
|
|
141
|
-
}
|
|
142
65
|
/**
|
|
143
66
|
* Renders all layers and exports the result in the specified format.
|
|
144
67
|
* @param {AnyExport} [format] - The export format (e.g., buffer, context, SVG, or canvas).
|
|
145
68
|
* @returns {Promise<Buffer | SKRSContext2D | Canvas | SvgCanvas | string>} The rendered output in the specified format.
|
|
146
69
|
*/
|
|
147
70
|
async render(format) {
|
|
148
|
-
let result;
|
|
149
|
-
// beforeRender hook
|
|
150
|
-
this.lazyCanvas.manager.plugins.executeHook('beforeRender', this.lazyCanvas);
|
|
151
71
|
switch (format) {
|
|
152
72
|
case types_1.Export.BUFFER:
|
|
153
73
|
case "buffer":
|
|
154
|
-
|
|
155
|
-
result = await this.renderAnimation();
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
result = await this.renderStatic(types_1.Export.BUFFER);
|
|
159
|
-
}
|
|
160
|
-
break;
|
|
74
|
+
return await this.renderStatic(types_1.Export.BUFFER);
|
|
161
75
|
case types_1.Export.CTX:
|
|
162
76
|
case "ctx":
|
|
163
|
-
|
|
164
|
-
break;
|
|
77
|
+
return this.lazyCanvas.ctx;
|
|
165
78
|
case types_1.Export.SVG:
|
|
166
79
|
case "svg":
|
|
167
|
-
|
|
168
|
-
break;
|
|
80
|
+
return await this.renderStatic(types_1.Export.SVG);
|
|
169
81
|
case types_1.Export.CANVAS:
|
|
170
82
|
case "canvas":
|
|
171
|
-
await this.renderStatic(this.lazyCanvas.options.exportType ===
|
|
172
|
-
|
|
173
|
-
break;
|
|
83
|
+
await this.renderStatic(this.lazyCanvas.options.exportType === "svg" ? types_1.Export.SVG : types_1.Export.BUFFER);
|
|
84
|
+
return this.lazyCanvas.canvas;
|
|
174
85
|
default:
|
|
175
|
-
|
|
176
|
-
break;
|
|
86
|
+
return await this.renderStatic(types_1.Export.BUFFER);
|
|
177
87
|
}
|
|
178
|
-
// afterRender hook
|
|
179
|
-
this.lazyCanvas.manager.plugins.executeHook('afterRender', this.lazyCanvas);
|
|
180
|
-
return result;
|
|
181
88
|
}
|
|
182
89
|
}
|
|
183
90
|
exports.RenderManager = RenderManager;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from './RenderManager';
|
|
5
|
-
export * from './AnimationManager';
|
|
1
|
+
export * from "./LayersManager";
|
|
2
|
+
export * from "./FontsManager";
|
|
3
|
+
export * from "./RenderManager";
|
|
@@ -15,7 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./LayersManager"), exports);
|
|
18
|
-
__exportStar(require("./PluginManager"), exports);
|
|
19
18
|
__exportStar(require("./FontsManager"), exports);
|
|
20
19
|
__exportStar(require("./RenderManager"), exports);
|
|
21
|
-
__exportStar(require("./AnimationManager"), exports);
|
package/dist/types/enum.d.ts
CHANGED
package/dist/types/enum.js
CHANGED
|
@@ -86,10 +86,9 @@ var Export;
|
|
|
86
86
|
Export["CTX"] = "ctx";
|
|
87
87
|
Export["BUFFER"] = "buffer";
|
|
88
88
|
Export["PNG"] = "png";
|
|
89
|
-
Export["
|
|
89
|
+
Export["APNG"] = "apng";
|
|
90
90
|
Export["JPG"] = "jpg";
|
|
91
91
|
Export["SVG"] = "svg";
|
|
92
|
-
Export["GIF"] = "gif";
|
|
93
92
|
Export["WEBP"] = "webp";
|
|
94
93
|
Export["YAML"] = "yaml";
|
|
95
94
|
Export["JSON"] = "json";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type * from "./types";
|
|
2
|
-
export * from
|
|
2
|
+
export * from "./enum";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* APNG Encoder that works directly with Uint8ClampedArray (ImageData)
|
|
3
|
+
* Much faster than encoding each frame to PNG separately
|
|
4
|
+
*/
|
|
5
|
+
export declare class APNGEncoder {
|
|
6
|
+
private frames;
|
|
7
|
+
private width;
|
|
8
|
+
private height;
|
|
9
|
+
private fps;
|
|
10
|
+
constructor(width: number, height: number, fps?: number);
|
|
11
|
+
/**
|
|
12
|
+
* Add a frame from ImageData
|
|
13
|
+
*/
|
|
14
|
+
addFrame(imageData: Uint8ClampedArray): this;
|
|
15
|
+
addFrames(...imageDatas: Uint8ClampedArray[]): this;
|
|
16
|
+
/**
|
|
17
|
+
* Encode all frames to APNG buffer
|
|
18
|
+
*/
|
|
19
|
+
encode(): Buffer;
|
|
20
|
+
/**
|
|
21
|
+
* Create IHDR chunk (image header)
|
|
22
|
+
*/
|
|
23
|
+
private createIHDR;
|
|
24
|
+
/**
|
|
25
|
+
* Create acTL chunk (animation control)
|
|
26
|
+
*/
|
|
27
|
+
private createACTL;
|
|
28
|
+
/**
|
|
29
|
+
* Create frame chunks (fcTL + fdAT or IDAT)
|
|
30
|
+
* @param frameIndex - Index of the frame (0-based)
|
|
31
|
+
* @param sequenceNumber - Global sequence number for fcTL/fdAT chunks
|
|
32
|
+
*/
|
|
33
|
+
private createFrame;
|
|
34
|
+
/**
|
|
35
|
+
* Compress ImageData using PNG filter and zlib
|
|
36
|
+
*/
|
|
37
|
+
private compressImageData;
|
|
38
|
+
/**
|
|
39
|
+
* Create IEND chunk
|
|
40
|
+
*/
|
|
41
|
+
private createIEND;
|
|
42
|
+
/**
|
|
43
|
+
* Create a PNG chunk with length, type, data, and CRC
|
|
44
|
+
*/
|
|
45
|
+
private createChunk;
|
|
46
|
+
/**
|
|
47
|
+
* Calculate CRC32 checksum
|
|
48
|
+
*/
|
|
49
|
+
private crc32;
|
|
50
|
+
/**
|
|
51
|
+
* CRC32 lookup table
|
|
52
|
+
*/
|
|
53
|
+
private crcTable;
|
|
54
|
+
/**
|
|
55
|
+
* Get number of frames
|
|
56
|
+
*/
|
|
57
|
+
getFrameCount(): number;
|
|
58
|
+
/**
|
|
59
|
+
* Clear all frames
|
|
60
|
+
*/
|
|
61
|
+
clear(): void;
|
|
62
|
+
}
|
|
63
|
+
export default APNGEncoder;
|
|
64
|
+
/**
|
|
65
|
+
* Helper function to create APNG from ImageData array
|
|
66
|
+
*/
|
|
67
|
+
export declare function createAPNG(frames: Uint8ClampedArray[], width: number, height: number, fps?: number): Buffer;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APNGEncoder = void 0;
|
|
4
|
+
exports.createAPNG = createAPNG;
|
|
5
|
+
const zlib_1 = require("zlib");
|
|
6
|
+
/**
|
|
7
|
+
* APNG Encoder that works directly with Uint8ClampedArray (ImageData)
|
|
8
|
+
* Much faster than encoding each frame to PNG separately
|
|
9
|
+
*/
|
|
10
|
+
class APNGEncoder {
|
|
11
|
+
constructor(width, height, fps = 30) {
|
|
12
|
+
this.frames = [];
|
|
13
|
+
/**
|
|
14
|
+
* CRC32 lookup table
|
|
15
|
+
*/
|
|
16
|
+
this.crcTable = (() => {
|
|
17
|
+
const table = new Uint32Array(256);
|
|
18
|
+
for (let i = 0; i < 256; i++) {
|
|
19
|
+
let c = i;
|
|
20
|
+
for (let k = 0; k < 8; k++) {
|
|
21
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
22
|
+
}
|
|
23
|
+
table[i] = c;
|
|
24
|
+
}
|
|
25
|
+
return table;
|
|
26
|
+
})();
|
|
27
|
+
this.width = width;
|
|
28
|
+
this.height = height;
|
|
29
|
+
this.fps = fps;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add a frame from ImageData
|
|
33
|
+
*/
|
|
34
|
+
addFrame(imageData) {
|
|
35
|
+
if (imageData.length !== this.width * this.height * 4) {
|
|
36
|
+
throw new Error(`Invalid ImageData size. Expected ${this.width * this.height * 4}, got ${imageData.length}`);
|
|
37
|
+
}
|
|
38
|
+
this.frames.push(imageData);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
addFrames(...imageDatas) {
|
|
42
|
+
for (const imageData of imageDatas) {
|
|
43
|
+
this.addFrame(imageData);
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Encode all frames to APNG buffer
|
|
49
|
+
*/
|
|
50
|
+
encode() {
|
|
51
|
+
if (this.frames.length === 0) {
|
|
52
|
+
throw new Error("No frames to encode");
|
|
53
|
+
}
|
|
54
|
+
const chunks = [];
|
|
55
|
+
// PNG signature
|
|
56
|
+
chunks.push(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
|
|
57
|
+
// IHDR chunk
|
|
58
|
+
chunks.push(this.createIHDR());
|
|
59
|
+
// acTL chunk (animation control)
|
|
60
|
+
chunks.push(this.createACTL());
|
|
61
|
+
// Add all frames with proper sequence numbering
|
|
62
|
+
let sequenceNumber = 0;
|
|
63
|
+
for (let i = 0; i < this.frames.length; i++) {
|
|
64
|
+
chunks.push(...this.createFrame(i, sequenceNumber));
|
|
65
|
+
// First frame: fcTL (0) + IDAT = 1 chunk with sequence
|
|
66
|
+
// Other frames: fcTL (n) + fdAT (n+1) = 2 chunks with sequences
|
|
67
|
+
sequenceNumber += i === 0 ? 1 : 2;
|
|
68
|
+
}
|
|
69
|
+
// IEND chunk
|
|
70
|
+
chunks.push(this.createIEND());
|
|
71
|
+
return Buffer.concat(chunks);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create IHDR chunk (image header)
|
|
75
|
+
*/
|
|
76
|
+
createIHDR() {
|
|
77
|
+
const data = Buffer.alloc(13);
|
|
78
|
+
data.writeUInt32BE(this.width, 0);
|
|
79
|
+
data.writeUInt32BE(this.height, 4);
|
|
80
|
+
data[8] = 8; // bit depth
|
|
81
|
+
data[9] = 6; // color type: RGBA
|
|
82
|
+
data[10] = 0; // compression method
|
|
83
|
+
data[11] = 0; // filter method
|
|
84
|
+
data[12] = 0; // interlace method
|
|
85
|
+
return this.createChunk("IHDR", data);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create acTL chunk (animation control)
|
|
89
|
+
*/
|
|
90
|
+
createACTL() {
|
|
91
|
+
const data = Buffer.alloc(8);
|
|
92
|
+
data.writeUInt32BE(this.frames.length, 0); // num_frames
|
|
93
|
+
data.writeUInt32BE(0, 4); // num_plays (0 = infinite)
|
|
94
|
+
return this.createChunk("acTL", data);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create frame chunks (fcTL + fdAT or IDAT)
|
|
98
|
+
* @param frameIndex - Index of the frame (0-based)
|
|
99
|
+
* @param sequenceNumber - Global sequence number for fcTL/fdAT chunks
|
|
100
|
+
*/
|
|
101
|
+
createFrame(frameIndex, sequenceNumber) {
|
|
102
|
+
const chunks = [];
|
|
103
|
+
// fcTL chunk (frame control) - always comes first
|
|
104
|
+
const fctl = Buffer.alloc(26);
|
|
105
|
+
fctl.writeUInt32BE(sequenceNumber, 0); // sequence_number
|
|
106
|
+
fctl.writeUInt32BE(this.width, 4); // width
|
|
107
|
+
fctl.writeUInt32BE(this.height, 8); // height
|
|
108
|
+
fctl.writeUInt32BE(0, 12); // x_offset
|
|
109
|
+
fctl.writeUInt32BE(0, 16); // y_offset
|
|
110
|
+
// Frame delay (delay_num / delay_den seconds)
|
|
111
|
+
const delayNum = 1;
|
|
112
|
+
const delayDen = this.fps;
|
|
113
|
+
fctl.writeUInt16BE(delayNum, 20);
|
|
114
|
+
fctl.writeUInt16BE(delayDen, 22);
|
|
115
|
+
fctl[24] = 0; // dispose_op: APNG_DISPOSE_OP_NONE
|
|
116
|
+
fctl[25] = 0; // blend_op: APNG_BLEND_OP_SOURCE
|
|
117
|
+
chunks.push(this.createChunk("fcTL", fctl));
|
|
118
|
+
// Compress frame data
|
|
119
|
+
const imageData = this.frames[frameIndex];
|
|
120
|
+
const compressed = this.compressImageData(imageData);
|
|
121
|
+
// First frame uses IDAT (no sequence number), subsequent frames use fdAT
|
|
122
|
+
if (frameIndex === 0) {
|
|
123
|
+
chunks.push(this.createChunk("IDAT", compressed));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// fdAT includes sequence number (sequenceNumber + 1 for the data chunk)
|
|
127
|
+
const fdatData = Buffer.alloc(4 + compressed.length);
|
|
128
|
+
fdatData.writeUInt32BE(sequenceNumber + 1, 0);
|
|
129
|
+
compressed.copy(fdatData, 4);
|
|
130
|
+
chunks.push(this.createChunk("fdAT", fdatData));
|
|
131
|
+
}
|
|
132
|
+
return chunks;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Compress ImageData using PNG filter and zlib
|
|
136
|
+
*/
|
|
137
|
+
compressImageData(imageData) {
|
|
138
|
+
const bytesPerPixel = 4; // RGBA
|
|
139
|
+
const stride = this.width * bytesPerPixel;
|
|
140
|
+
const filtered = Buffer.alloc(imageData.length + this.height);
|
|
141
|
+
// Apply PNG filter type 0 (None) to each scanline
|
|
142
|
+
for (let y = 0; y < this.height; y++) {
|
|
143
|
+
const offset = y * stride;
|
|
144
|
+
const filteredOffset = y * (stride + 1);
|
|
145
|
+
filtered[filteredOffset] = 0; // Filter type: None
|
|
146
|
+
imageData.subarray(offset, offset + stride).forEach((byte, i) => {
|
|
147
|
+
filtered[filteredOffset + 1 + i] = byte;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
// Compress with zlib
|
|
151
|
+
return (0, zlib_1.deflateSync)(filtered, { level: 6 });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create IEND chunk
|
|
155
|
+
*/
|
|
156
|
+
createIEND() {
|
|
157
|
+
return this.createChunk("IEND", Buffer.alloc(0));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a PNG chunk with length, type, data, and CRC
|
|
161
|
+
*/
|
|
162
|
+
createChunk(type, data) {
|
|
163
|
+
const length = Buffer.alloc(4);
|
|
164
|
+
length.writeUInt32BE(data.length, 0);
|
|
165
|
+
const typeBuffer = Buffer.from(type, "ascii");
|
|
166
|
+
const crc = this.crc32(Buffer.concat([typeBuffer, data]));
|
|
167
|
+
const crcBuffer = Buffer.alloc(4);
|
|
168
|
+
crcBuffer.writeUInt32BE(crc, 0);
|
|
169
|
+
return Buffer.concat([length, typeBuffer, data, crcBuffer]);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Calculate CRC32 checksum
|
|
173
|
+
*/
|
|
174
|
+
crc32(data) {
|
|
175
|
+
let crc = 0xffffffff;
|
|
176
|
+
for (let i = 0; i < data.length; i++) {
|
|
177
|
+
crc = this.crcTable[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
|
|
178
|
+
}
|
|
179
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get number of frames
|
|
183
|
+
*/
|
|
184
|
+
getFrameCount() {
|
|
185
|
+
return this.frames.length;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Clear all frames
|
|
189
|
+
*/
|
|
190
|
+
clear() {
|
|
191
|
+
this.frames = [];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.APNGEncoder = APNGEncoder;
|
|
195
|
+
exports.default = APNGEncoder;
|
|
196
|
+
/**
|
|
197
|
+
* Helper function to create APNG from ImageData array
|
|
198
|
+
*/
|
|
199
|
+
function createAPNG(frames, width, height, fps = 30) {
|
|
200
|
+
const encoder = new APNGEncoder(width, height, fps);
|
|
201
|
+
for (const frame of frames) {
|
|
202
|
+
encoder.addFrame(frame);
|
|
203
|
+
}
|
|
204
|
+
return encoder.encode();
|
|
205
|
+
}
|