@openfairygui/functions 0.2.0-alpha.2 → 0.2.0-alpha.21
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 +48 -6
- package/dist/atlas-C6tbl7nn.d.ts +193 -0
- package/dist/atlas-CHsu2Y8i.d.cts +193 -0
- package/dist/index.cjs +16 -3603
- package/dist/index.d.cts +5 -294
- package/dist/index.d.ts +5 -294
- package/dist/index.js +3 -3594
- package/dist/node.cjs +256 -0
- package/dist/node.d.cts +36 -0
- package/dist/node.d.ts +36 -0
- package/dist/node.js +254 -0
- package/dist/publish-BJ_eelME.js +3267 -0
- package/dist/publish-xFWT9Slz.cjs +3338 -0
- package/dist/restore-BW2xacB3.cjs +936 -0
- package/dist/restore-BeWaJNjR.d.cts +288 -0
- package/dist/restore-Clk62n0O.js +931 -0
- package/dist/restore-Dh0-Nvms.d.ts +288 -0
- package/dist/uam-transaction.cjs +44 -1
- package/dist/uam-transaction.d.cts +16 -1
- package/dist/uam-transaction.d.ts +16 -1
- package/dist/uam-transaction.js +44 -1
- package/dist/web.cjs +274 -0
- package/dist/web.d.cts +41 -0
- package/dist/web.d.ts +41 -0
- package/dist/web.js +273 -0
- package/package.json +28 -4
- package/src/adapters/node/plugins.ts +82 -0
- package/src/adapters/node/publish.ts +130 -0
- package/src/adapters/node/restore.ts +187 -0
- package/src/adapters/web/publish.ts +159 -0
- package/src/adapters/web/raster.ts +251 -0
- package/src/atlas/font.ts +95 -0
- package/src/atlas/inputs.ts +515 -0
- package/src/atlas/jta.ts +211 -0
- package/src/atlas/packing.ts +767 -0
- package/src/atlas.ts +116 -1221
- package/src/codegen.ts +106 -67
- package/src/index.ts +43 -3
- package/src/node.ts +8 -0
- package/src/plugins/types.ts +56 -0
- package/src/publish/contracts.ts +80 -0
- package/src/publish/external-resources.ts +117 -0
- package/src/publish/options.ts +180 -0
- package/src/publish/package-context.ts +608 -0
- package/src/publish/resource-references.ts +210 -0
- package/src/publish.ts +290 -968
- package/src/restore-internals/font.ts +100 -0
- package/src/restore-internals/movie-clip.ts +104 -0
- package/src/restore-internals/output-transaction.ts +164 -0
- package/src/restore.ts +112 -311
- package/src/shared-types.ts +4 -8
- package/src/uam-transaction.ts +68 -0
- package/src/utils.ts +28 -0
- package/src/web.ts +11 -0
package/dist/web.cjs
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_publish = require("./publish-xFWT9Slz.cjs");
|
|
3
|
+
let _openfairygui_core = require("@openfairygui/core");
|
|
4
|
+
//#region src/adapters/web/raster.ts
|
|
5
|
+
function getBrowserContext(canvas) {
|
|
6
|
+
const context = canvas.getContext("2d");
|
|
7
|
+
if (!context) throw new Error("publishBrowser: a 2D canvas context is unavailable.");
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
function createBrowserCanvas(width, height) {
|
|
11
|
+
if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(width, height);
|
|
12
|
+
if (typeof globalThis.document === "undefined") throw new Error("publishBrowser: OffscreenCanvas or a DOM canvas is required for atlas PNG generation.");
|
|
13
|
+
const canvas = globalThis.document.createElement("canvas");
|
|
14
|
+
canvas.width = width;
|
|
15
|
+
canvas.height = height;
|
|
16
|
+
return canvas;
|
|
17
|
+
}
|
|
18
|
+
function assertBrowserImageSupport() {
|
|
19
|
+
if (typeof createImageBitmap !== "function") throw new Error("publishBrowser: createImageBitmap is required for atlas PNG generation.");
|
|
20
|
+
if (typeof OffscreenCanvas === "undefined" && typeof globalThis.document === "undefined") throw new Error("publishBrowser: OffscreenCanvas or a DOM canvas is required for atlas PNG generation.");
|
|
21
|
+
}
|
|
22
|
+
function createRaster(width, height, background) {
|
|
23
|
+
const canvas = createBrowserCanvas(width, height);
|
|
24
|
+
const context = getBrowserContext(canvas);
|
|
25
|
+
context.clearRect(0, 0, width, height);
|
|
26
|
+
if (background && background.alpha > 0) {
|
|
27
|
+
context.fillStyle = `rgba(${background.r}, ${background.g}, ${background.b}, ${background.alpha})`;
|
|
28
|
+
context.fillRect(0, 0, width, height);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
canvas,
|
|
32
|
+
width,
|
|
33
|
+
height
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function imageMimeType(path) {
|
|
37
|
+
if (/\.svg$/iu.test(path)) return "image/svg+xml";
|
|
38
|
+
if (/\.jpe?g$/iu.test(path)) return "image/jpeg";
|
|
39
|
+
if (/\.webp$/iu.test(path)) return "image/webp";
|
|
40
|
+
if (/\.gif$/iu.test(path)) return "image/gif";
|
|
41
|
+
return "image/png";
|
|
42
|
+
}
|
|
43
|
+
function imageMimeTypeFromBytes(bytes) {
|
|
44
|
+
if (bytes[0] === 255 && bytes[1] === 216) return "image/jpeg";
|
|
45
|
+
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) return "image/gif";
|
|
46
|
+
if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70) return "image/webp";
|
|
47
|
+
return "image/png";
|
|
48
|
+
}
|
|
49
|
+
async function canvasToPng(canvas) {
|
|
50
|
+
let blob;
|
|
51
|
+
if ("convertToBlob" in canvas && typeof canvas.convertToBlob === "function") blob = await canvas.convertToBlob({ type: "image/png" });
|
|
52
|
+
else blob = await new Promise((resolve, reject) => {
|
|
53
|
+
canvas.toBlob((value) => {
|
|
54
|
+
if (value) resolve(value);
|
|
55
|
+
else reject(/* @__PURE__ */ new Error("publishBrowser: canvas PNG encoding failed."));
|
|
56
|
+
}, "image/png");
|
|
57
|
+
});
|
|
58
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
59
|
+
}
|
|
60
|
+
async function decodeRaster(bytes, mimeType) {
|
|
61
|
+
if (typeof createImageBitmap !== "function") throw new Error("publishBrowser: createImageBitmap is required for atlas PNG generation.");
|
|
62
|
+
const copy = bytes.slice();
|
|
63
|
+
const bitmap = await createImageBitmap(new Blob([copy.buffer], { type: mimeType }));
|
|
64
|
+
try {
|
|
65
|
+
const raster = createRaster(bitmap.width, bitmap.height);
|
|
66
|
+
getBrowserContext(raster.canvas).drawImage(bitmap, 0, 0);
|
|
67
|
+
return raster;
|
|
68
|
+
} finally {
|
|
69
|
+
bitmap.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
var BrowserImagePipeline = class {
|
|
73
|
+
rawOutput = false;
|
|
74
|
+
constructor(raster, decode, write) {
|
|
75
|
+
this.raster = raster;
|
|
76
|
+
this.decode = decode;
|
|
77
|
+
this.write = write;
|
|
78
|
+
}
|
|
79
|
+
ensureAlpha() {
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
resize(options) {
|
|
83
|
+
this.raster = this.raster.then((source) => {
|
|
84
|
+
const target = createRaster(options.width, options.height);
|
|
85
|
+
getBrowserContext(target.canvas).drawImage(source.canvas, 0, 0, options.width, options.height);
|
|
86
|
+
return target;
|
|
87
|
+
});
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
raw() {
|
|
91
|
+
this.rawOutput = true;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
extract(options) {
|
|
95
|
+
this.raster = this.raster.then((source) => {
|
|
96
|
+
const target = createRaster(options.width, options.height);
|
|
97
|
+
getBrowserContext(target.canvas).drawImage(source.canvas, options.left, options.top, options.width, options.height, 0, 0, options.width, options.height);
|
|
98
|
+
return target;
|
|
99
|
+
});
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
png() {
|
|
103
|
+
this.rawOutput = false;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
rotate(angle) {
|
|
107
|
+
this.raster = this.raster.then((source) => {
|
|
108
|
+
if (angle % 180 === 0) return source;
|
|
109
|
+
const target = createRaster(source.height, source.width);
|
|
110
|
+
const context = getBrowserContext(target.canvas);
|
|
111
|
+
context.save();
|
|
112
|
+
if (angle === 270 || angle === -90) {
|
|
113
|
+
context.translate(0, source.width);
|
|
114
|
+
context.rotate(-Math.PI / 2);
|
|
115
|
+
} else {
|
|
116
|
+
context.translate(source.height, 0);
|
|
117
|
+
context.rotate(Math.PI / 2);
|
|
118
|
+
}
|
|
119
|
+
context.drawImage(source.canvas, 0, 0);
|
|
120
|
+
context.restore();
|
|
121
|
+
return target;
|
|
122
|
+
});
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
composite(inputs) {
|
|
126
|
+
this.raster = this.raster.then(async (target) => {
|
|
127
|
+
const context = getBrowserContext(target.canvas);
|
|
128
|
+
for (const input of inputs) {
|
|
129
|
+
const source = await this.decode(input.input);
|
|
130
|
+
context.drawImage(source.canvas, input.left, input.top);
|
|
131
|
+
}
|
|
132
|
+
return target;
|
|
133
|
+
});
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
async metadata() {
|
|
137
|
+
const raster = await this.raster;
|
|
138
|
+
return {
|
|
139
|
+
width: raster.width,
|
|
140
|
+
height: raster.height,
|
|
141
|
+
channels: 4,
|
|
142
|
+
hasAlpha: true
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async toBuffer(options) {
|
|
146
|
+
const raster = await this.raster;
|
|
147
|
+
if (options?.resolveWithObject) {
|
|
148
|
+
const data = getBrowserContext(raster.canvas).getImageData(0, 0, raster.width, raster.height).data;
|
|
149
|
+
return {
|
|
150
|
+
data: new Uint8Array(data),
|
|
151
|
+
info: {
|
|
152
|
+
width: raster.width,
|
|
153
|
+
height: raster.height,
|
|
154
|
+
channels: 4
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (this.rawOutput) return new Uint8Array(getBrowserContext(raster.canvas).getImageData(0, 0, raster.width, raster.height).data);
|
|
159
|
+
return canvasToPng(raster.canvas);
|
|
160
|
+
}
|
|
161
|
+
async toFile(path) {
|
|
162
|
+
const raster = await this.raster;
|
|
163
|
+
await this.write(path, await canvasToPng(raster.canvas));
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
function createBrowserImageEncoder(sourceFileSystem, outputFileSystem) {
|
|
167
|
+
const decode = (bytes) => decodeRaster(bytes, imageMimeTypeFromBytes(bytes));
|
|
168
|
+
return (input) => {
|
|
169
|
+
return new BrowserImagePipeline(typeof input === "string" ? sourceFileSystem.readFileRaw(input).then((bytes) => decodeRaster(bytes, imageMimeType(input))) : input instanceof Uint8Array ? decode(input) : Promise.resolve(createRaster(input.create.width, input.create.height, input.create.background)), decode, outputFileSystem.writeFileRaw);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/adapters/web/publish.ts
|
|
174
|
+
function createTrackingFileSystem(fileSystem, files) {
|
|
175
|
+
return {
|
|
176
|
+
join: (...paths) => fileSystem.join(...paths),
|
|
177
|
+
mkdir: (path) => fileSystem.mkdir(path),
|
|
178
|
+
writeFileRaw: async (path, data) => {
|
|
179
|
+
await fileSystem.writeFileRaw(path, data);
|
|
180
|
+
files.set(path, data.byteLength);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function createDiagnosticLogger(logger, diagnostics) {
|
|
185
|
+
return {
|
|
186
|
+
debug(message) {
|
|
187
|
+
diagnostics.push({
|
|
188
|
+
level: "debug",
|
|
189
|
+
message
|
|
190
|
+
});
|
|
191
|
+
logger.debug(message);
|
|
192
|
+
},
|
|
193
|
+
info(message) {
|
|
194
|
+
diagnostics.push({
|
|
195
|
+
level: "info",
|
|
196
|
+
message
|
|
197
|
+
});
|
|
198
|
+
logger.info(message);
|
|
199
|
+
},
|
|
200
|
+
warn(message) {
|
|
201
|
+
diagnostics.push({
|
|
202
|
+
level: "warning",
|
|
203
|
+
message
|
|
204
|
+
});
|
|
205
|
+
logger.warn(message);
|
|
206
|
+
},
|
|
207
|
+
error(message) {
|
|
208
|
+
diagnostics.push({
|
|
209
|
+
level: "error",
|
|
210
|
+
message
|
|
211
|
+
});
|
|
212
|
+
logger.error(message);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function toResult(success, files, diagnostics) {
|
|
217
|
+
return {
|
|
218
|
+
success,
|
|
219
|
+
files: [...files].map(([path, size]) => ({
|
|
220
|
+
path,
|
|
221
|
+
size
|
|
222
|
+
})),
|
|
223
|
+
diagnostics
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Publish a loaded FairyGUI project to browser-provided storage.
|
|
228
|
+
*
|
|
229
|
+
* The adapter uses browser Canvas APIs for atlas composition, writes only through
|
|
230
|
+
* the supplied output filesystem, and intentionally skips Node publish plugins.
|
|
231
|
+
*/
|
|
232
|
+
async function publishBrowser(options) {
|
|
233
|
+
const files = /* @__PURE__ */ new Map();
|
|
234
|
+
const diagnostics = [];
|
|
235
|
+
const root = options.document.getRoot();
|
|
236
|
+
const previousProjectType = root.getProjectType();
|
|
237
|
+
const previousLogger = options.document.getLogger();
|
|
238
|
+
options.document.setLogger(createDiagnosticLogger(previousLogger, diagnostics));
|
|
239
|
+
try {
|
|
240
|
+
if (options.projectType !== "layabox") throw new Error(`publishBrowser: unsupported project type "${String(options.projectType)}".`);
|
|
241
|
+
assertBrowserImageSupport();
|
|
242
|
+
root.setProjectType(_openfairygui_core.ProjectType.LayaBox);
|
|
243
|
+
const outputFileSystem = createTrackingFileSystem(options.outputFileSystem, files);
|
|
244
|
+
const sourceAssetsPath = options.sourceFileSystem.join(options.document.getProjectDir(), "assets");
|
|
245
|
+
await options.document.transform(require_publish.publish({
|
|
246
|
+
output: options.output,
|
|
247
|
+
compressed: options.compressed,
|
|
248
|
+
fileExtension: "fui",
|
|
249
|
+
packages: options.packages,
|
|
250
|
+
branch: options.branch,
|
|
251
|
+
basePath: sourceAssetsPath,
|
|
252
|
+
encoder: createBrowserImageEncoder(options.sourceFileSystem, outputFileSystem),
|
|
253
|
+
atlas: {
|
|
254
|
+
...options.atlas,
|
|
255
|
+
readFileRaw: (path) => options.sourceFileSystem.readFileRaw(path)
|
|
256
|
+
},
|
|
257
|
+
fs: outputFileSystem,
|
|
258
|
+
plugins: [],
|
|
259
|
+
codeGeneration: false
|
|
260
|
+
}));
|
|
261
|
+
return toResult(true, files, diagnostics);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
diagnostics.push({
|
|
264
|
+
level: "error",
|
|
265
|
+
message: error instanceof Error ? error.message : String(error)
|
|
266
|
+
});
|
|
267
|
+
return toResult(false, files, diagnostics);
|
|
268
|
+
} finally {
|
|
269
|
+
root.setProjectType(previousProjectType);
|
|
270
|
+
options.document.setLogger(previousLogger);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
exports.publishBrowser = publishBrowser;
|
package/dist/web.d.cts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { d as PublishSourceFileSystem, t as AtlasOptions, u as PublishOutputFileSystem } from "./atlas-CHsu2Y8i.cjs";
|
|
2
|
+
import { Document } from "@openfairygui/core";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/web/publish.d.ts
|
|
5
|
+
type BrowserPublishProjectType = 'layabox';
|
|
6
|
+
type BrowserPublishAtlasOptions = Pick<AtlasOptions, 'maxSize' | 'fast' | 'allowRotation' | 'padding' | 'powerOfTwo' | 'square' | 'multiPage' | 'trimImage' | 'extractAlpha'>;
|
|
7
|
+
type BrowserPublishSourceFileSystem = PublishSourceFileSystem;
|
|
8
|
+
type BrowserPublishOutputFileSystem = PublishOutputFileSystem;
|
|
9
|
+
interface BrowserPublishOptions {
|
|
10
|
+
document: Document;
|
|
11
|
+
sourceFileSystem: BrowserPublishSourceFileSystem;
|
|
12
|
+
outputFileSystem: BrowserPublishOutputFileSystem;
|
|
13
|
+
projectType: BrowserPublishProjectType;
|
|
14
|
+
output: string;
|
|
15
|
+
compressed?: boolean;
|
|
16
|
+
packages?: string[];
|
|
17
|
+
branch?: string;
|
|
18
|
+
atlas?: BrowserPublishAtlasOptions;
|
|
19
|
+
}
|
|
20
|
+
interface BrowserPublishDiagnostic {
|
|
21
|
+
level: 'debug' | 'info' | 'warning' | 'error';
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
interface BrowserPublishedFile {
|
|
25
|
+
path: string;
|
|
26
|
+
size: number;
|
|
27
|
+
}
|
|
28
|
+
interface BrowserPublishResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
files: BrowserPublishedFile[];
|
|
31
|
+
diagnostics: BrowserPublishDiagnostic[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Publish a loaded FairyGUI project to browser-provided storage.
|
|
35
|
+
*
|
|
36
|
+
* The adapter uses browser Canvas APIs for atlas composition, writes only through
|
|
37
|
+
* the supplied output filesystem, and intentionally skips Node publish plugins.
|
|
38
|
+
*/
|
|
39
|
+
declare function publishBrowser(options: BrowserPublishOptions): Promise<BrowserPublishResult>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { type BrowserPublishAtlasOptions, type BrowserPublishDiagnostic, type BrowserPublishOptions, type BrowserPublishOutputFileSystem, type BrowserPublishProjectType, type BrowserPublishResult, type BrowserPublishSourceFileSystem, type BrowserPublishedFile, publishBrowser };
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { d as PublishSourceFileSystem, t as AtlasOptions, u as PublishOutputFileSystem } from "./atlas-C6tbl7nn.js";
|
|
2
|
+
import { Document } from "@openfairygui/core";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/web/publish.d.ts
|
|
5
|
+
type BrowserPublishProjectType = 'layabox';
|
|
6
|
+
type BrowserPublishAtlasOptions = Pick<AtlasOptions, 'maxSize' | 'fast' | 'allowRotation' | 'padding' | 'powerOfTwo' | 'square' | 'multiPage' | 'trimImage' | 'extractAlpha'>;
|
|
7
|
+
type BrowserPublishSourceFileSystem = PublishSourceFileSystem;
|
|
8
|
+
type BrowserPublishOutputFileSystem = PublishOutputFileSystem;
|
|
9
|
+
interface BrowserPublishOptions {
|
|
10
|
+
document: Document;
|
|
11
|
+
sourceFileSystem: BrowserPublishSourceFileSystem;
|
|
12
|
+
outputFileSystem: BrowserPublishOutputFileSystem;
|
|
13
|
+
projectType: BrowserPublishProjectType;
|
|
14
|
+
output: string;
|
|
15
|
+
compressed?: boolean;
|
|
16
|
+
packages?: string[];
|
|
17
|
+
branch?: string;
|
|
18
|
+
atlas?: BrowserPublishAtlasOptions;
|
|
19
|
+
}
|
|
20
|
+
interface BrowserPublishDiagnostic {
|
|
21
|
+
level: 'debug' | 'info' | 'warning' | 'error';
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
interface BrowserPublishedFile {
|
|
25
|
+
path: string;
|
|
26
|
+
size: number;
|
|
27
|
+
}
|
|
28
|
+
interface BrowserPublishResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
files: BrowserPublishedFile[];
|
|
31
|
+
diagnostics: BrowserPublishDiagnostic[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Publish a loaded FairyGUI project to browser-provided storage.
|
|
35
|
+
*
|
|
36
|
+
* The adapter uses browser Canvas APIs for atlas composition, writes only through
|
|
37
|
+
* the supplied output filesystem, and intentionally skips Node publish plugins.
|
|
38
|
+
*/
|
|
39
|
+
declare function publishBrowser(options: BrowserPublishOptions): Promise<BrowserPublishResult>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { type BrowserPublishAtlasOptions, type BrowserPublishDiagnostic, type BrowserPublishOptions, type BrowserPublishOutputFileSystem, type BrowserPublishProjectType, type BrowserPublishResult, type BrowserPublishSourceFileSystem, type BrowserPublishedFile, publishBrowser };
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { t as publish } from "./publish-BJ_eelME.js";
|
|
2
|
+
import { ProjectType } from "@openfairygui/core";
|
|
3
|
+
//#region src/adapters/web/raster.ts
|
|
4
|
+
function getBrowserContext(canvas) {
|
|
5
|
+
const context = canvas.getContext("2d");
|
|
6
|
+
if (!context) throw new Error("publishBrowser: a 2D canvas context is unavailable.");
|
|
7
|
+
return context;
|
|
8
|
+
}
|
|
9
|
+
function createBrowserCanvas(width, height) {
|
|
10
|
+
if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(width, height);
|
|
11
|
+
if (typeof globalThis.document === "undefined") throw new Error("publishBrowser: OffscreenCanvas or a DOM canvas is required for atlas PNG generation.");
|
|
12
|
+
const canvas = globalThis.document.createElement("canvas");
|
|
13
|
+
canvas.width = width;
|
|
14
|
+
canvas.height = height;
|
|
15
|
+
return canvas;
|
|
16
|
+
}
|
|
17
|
+
function assertBrowserImageSupport() {
|
|
18
|
+
if (typeof createImageBitmap !== "function") throw new Error("publishBrowser: createImageBitmap is required for atlas PNG generation.");
|
|
19
|
+
if (typeof OffscreenCanvas === "undefined" && typeof globalThis.document === "undefined") throw new Error("publishBrowser: OffscreenCanvas or a DOM canvas is required for atlas PNG generation.");
|
|
20
|
+
}
|
|
21
|
+
function createRaster(width, height, background) {
|
|
22
|
+
const canvas = createBrowserCanvas(width, height);
|
|
23
|
+
const context = getBrowserContext(canvas);
|
|
24
|
+
context.clearRect(0, 0, width, height);
|
|
25
|
+
if (background && background.alpha > 0) {
|
|
26
|
+
context.fillStyle = `rgba(${background.r}, ${background.g}, ${background.b}, ${background.alpha})`;
|
|
27
|
+
context.fillRect(0, 0, width, height);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
canvas,
|
|
31
|
+
width,
|
|
32
|
+
height
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function imageMimeType(path) {
|
|
36
|
+
if (/\.svg$/iu.test(path)) return "image/svg+xml";
|
|
37
|
+
if (/\.jpe?g$/iu.test(path)) return "image/jpeg";
|
|
38
|
+
if (/\.webp$/iu.test(path)) return "image/webp";
|
|
39
|
+
if (/\.gif$/iu.test(path)) return "image/gif";
|
|
40
|
+
return "image/png";
|
|
41
|
+
}
|
|
42
|
+
function imageMimeTypeFromBytes(bytes) {
|
|
43
|
+
if (bytes[0] === 255 && bytes[1] === 216) return "image/jpeg";
|
|
44
|
+
if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) return "image/gif";
|
|
45
|
+
if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70) return "image/webp";
|
|
46
|
+
return "image/png";
|
|
47
|
+
}
|
|
48
|
+
async function canvasToPng(canvas) {
|
|
49
|
+
let blob;
|
|
50
|
+
if ("convertToBlob" in canvas && typeof canvas.convertToBlob === "function") blob = await canvas.convertToBlob({ type: "image/png" });
|
|
51
|
+
else blob = await new Promise((resolve, reject) => {
|
|
52
|
+
canvas.toBlob((value) => {
|
|
53
|
+
if (value) resolve(value);
|
|
54
|
+
else reject(/* @__PURE__ */ new Error("publishBrowser: canvas PNG encoding failed."));
|
|
55
|
+
}, "image/png");
|
|
56
|
+
});
|
|
57
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
58
|
+
}
|
|
59
|
+
async function decodeRaster(bytes, mimeType) {
|
|
60
|
+
if (typeof createImageBitmap !== "function") throw new Error("publishBrowser: createImageBitmap is required for atlas PNG generation.");
|
|
61
|
+
const copy = bytes.slice();
|
|
62
|
+
const bitmap = await createImageBitmap(new Blob([copy.buffer], { type: mimeType }));
|
|
63
|
+
try {
|
|
64
|
+
const raster = createRaster(bitmap.width, bitmap.height);
|
|
65
|
+
getBrowserContext(raster.canvas).drawImage(bitmap, 0, 0);
|
|
66
|
+
return raster;
|
|
67
|
+
} finally {
|
|
68
|
+
bitmap.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
var BrowserImagePipeline = class {
|
|
72
|
+
rawOutput = false;
|
|
73
|
+
constructor(raster, decode, write) {
|
|
74
|
+
this.raster = raster;
|
|
75
|
+
this.decode = decode;
|
|
76
|
+
this.write = write;
|
|
77
|
+
}
|
|
78
|
+
ensureAlpha() {
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
resize(options) {
|
|
82
|
+
this.raster = this.raster.then((source) => {
|
|
83
|
+
const target = createRaster(options.width, options.height);
|
|
84
|
+
getBrowserContext(target.canvas).drawImage(source.canvas, 0, 0, options.width, options.height);
|
|
85
|
+
return target;
|
|
86
|
+
});
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
raw() {
|
|
90
|
+
this.rawOutput = true;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
extract(options) {
|
|
94
|
+
this.raster = this.raster.then((source) => {
|
|
95
|
+
const target = createRaster(options.width, options.height);
|
|
96
|
+
getBrowserContext(target.canvas).drawImage(source.canvas, options.left, options.top, options.width, options.height, 0, 0, options.width, options.height);
|
|
97
|
+
return target;
|
|
98
|
+
});
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
png() {
|
|
102
|
+
this.rawOutput = false;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
rotate(angle) {
|
|
106
|
+
this.raster = this.raster.then((source) => {
|
|
107
|
+
if (angle % 180 === 0) return source;
|
|
108
|
+
const target = createRaster(source.height, source.width);
|
|
109
|
+
const context = getBrowserContext(target.canvas);
|
|
110
|
+
context.save();
|
|
111
|
+
if (angle === 270 || angle === -90) {
|
|
112
|
+
context.translate(0, source.width);
|
|
113
|
+
context.rotate(-Math.PI / 2);
|
|
114
|
+
} else {
|
|
115
|
+
context.translate(source.height, 0);
|
|
116
|
+
context.rotate(Math.PI / 2);
|
|
117
|
+
}
|
|
118
|
+
context.drawImage(source.canvas, 0, 0);
|
|
119
|
+
context.restore();
|
|
120
|
+
return target;
|
|
121
|
+
});
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
composite(inputs) {
|
|
125
|
+
this.raster = this.raster.then(async (target) => {
|
|
126
|
+
const context = getBrowserContext(target.canvas);
|
|
127
|
+
for (const input of inputs) {
|
|
128
|
+
const source = await this.decode(input.input);
|
|
129
|
+
context.drawImage(source.canvas, input.left, input.top);
|
|
130
|
+
}
|
|
131
|
+
return target;
|
|
132
|
+
});
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
async metadata() {
|
|
136
|
+
const raster = await this.raster;
|
|
137
|
+
return {
|
|
138
|
+
width: raster.width,
|
|
139
|
+
height: raster.height,
|
|
140
|
+
channels: 4,
|
|
141
|
+
hasAlpha: true
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async toBuffer(options) {
|
|
145
|
+
const raster = await this.raster;
|
|
146
|
+
if (options?.resolveWithObject) {
|
|
147
|
+
const data = getBrowserContext(raster.canvas).getImageData(0, 0, raster.width, raster.height).data;
|
|
148
|
+
return {
|
|
149
|
+
data: new Uint8Array(data),
|
|
150
|
+
info: {
|
|
151
|
+
width: raster.width,
|
|
152
|
+
height: raster.height,
|
|
153
|
+
channels: 4
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (this.rawOutput) return new Uint8Array(getBrowserContext(raster.canvas).getImageData(0, 0, raster.width, raster.height).data);
|
|
158
|
+
return canvasToPng(raster.canvas);
|
|
159
|
+
}
|
|
160
|
+
async toFile(path) {
|
|
161
|
+
const raster = await this.raster;
|
|
162
|
+
await this.write(path, await canvasToPng(raster.canvas));
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
function createBrowserImageEncoder(sourceFileSystem, outputFileSystem) {
|
|
166
|
+
const decode = (bytes) => decodeRaster(bytes, imageMimeTypeFromBytes(bytes));
|
|
167
|
+
return (input) => {
|
|
168
|
+
return new BrowserImagePipeline(typeof input === "string" ? sourceFileSystem.readFileRaw(input).then((bytes) => decodeRaster(bytes, imageMimeType(input))) : input instanceof Uint8Array ? decode(input) : Promise.resolve(createRaster(input.create.width, input.create.height, input.create.background)), decode, outputFileSystem.writeFileRaw);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/adapters/web/publish.ts
|
|
173
|
+
function createTrackingFileSystem(fileSystem, files) {
|
|
174
|
+
return {
|
|
175
|
+
join: (...paths) => fileSystem.join(...paths),
|
|
176
|
+
mkdir: (path) => fileSystem.mkdir(path),
|
|
177
|
+
writeFileRaw: async (path, data) => {
|
|
178
|
+
await fileSystem.writeFileRaw(path, data);
|
|
179
|
+
files.set(path, data.byteLength);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function createDiagnosticLogger(logger, diagnostics) {
|
|
184
|
+
return {
|
|
185
|
+
debug(message) {
|
|
186
|
+
diagnostics.push({
|
|
187
|
+
level: "debug",
|
|
188
|
+
message
|
|
189
|
+
});
|
|
190
|
+
logger.debug(message);
|
|
191
|
+
},
|
|
192
|
+
info(message) {
|
|
193
|
+
diagnostics.push({
|
|
194
|
+
level: "info",
|
|
195
|
+
message
|
|
196
|
+
});
|
|
197
|
+
logger.info(message);
|
|
198
|
+
},
|
|
199
|
+
warn(message) {
|
|
200
|
+
diagnostics.push({
|
|
201
|
+
level: "warning",
|
|
202
|
+
message
|
|
203
|
+
});
|
|
204
|
+
logger.warn(message);
|
|
205
|
+
},
|
|
206
|
+
error(message) {
|
|
207
|
+
diagnostics.push({
|
|
208
|
+
level: "error",
|
|
209
|
+
message
|
|
210
|
+
});
|
|
211
|
+
logger.error(message);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function toResult(success, files, diagnostics) {
|
|
216
|
+
return {
|
|
217
|
+
success,
|
|
218
|
+
files: [...files].map(([path, size]) => ({
|
|
219
|
+
path,
|
|
220
|
+
size
|
|
221
|
+
})),
|
|
222
|
+
diagnostics
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Publish a loaded FairyGUI project to browser-provided storage.
|
|
227
|
+
*
|
|
228
|
+
* The adapter uses browser Canvas APIs for atlas composition, writes only through
|
|
229
|
+
* the supplied output filesystem, and intentionally skips Node publish plugins.
|
|
230
|
+
*/
|
|
231
|
+
async function publishBrowser(options) {
|
|
232
|
+
const files = /* @__PURE__ */ new Map();
|
|
233
|
+
const diagnostics = [];
|
|
234
|
+
const root = options.document.getRoot();
|
|
235
|
+
const previousProjectType = root.getProjectType();
|
|
236
|
+
const previousLogger = options.document.getLogger();
|
|
237
|
+
options.document.setLogger(createDiagnosticLogger(previousLogger, diagnostics));
|
|
238
|
+
try {
|
|
239
|
+
if (options.projectType !== "layabox") throw new Error(`publishBrowser: unsupported project type "${String(options.projectType)}".`);
|
|
240
|
+
assertBrowserImageSupport();
|
|
241
|
+
root.setProjectType(ProjectType.LayaBox);
|
|
242
|
+
const outputFileSystem = createTrackingFileSystem(options.outputFileSystem, files);
|
|
243
|
+
const sourceAssetsPath = options.sourceFileSystem.join(options.document.getProjectDir(), "assets");
|
|
244
|
+
await options.document.transform(publish({
|
|
245
|
+
output: options.output,
|
|
246
|
+
compressed: options.compressed,
|
|
247
|
+
fileExtension: "fui",
|
|
248
|
+
packages: options.packages,
|
|
249
|
+
branch: options.branch,
|
|
250
|
+
basePath: sourceAssetsPath,
|
|
251
|
+
encoder: createBrowserImageEncoder(options.sourceFileSystem, outputFileSystem),
|
|
252
|
+
atlas: {
|
|
253
|
+
...options.atlas,
|
|
254
|
+
readFileRaw: (path) => options.sourceFileSystem.readFileRaw(path)
|
|
255
|
+
},
|
|
256
|
+
fs: outputFileSystem,
|
|
257
|
+
plugins: [],
|
|
258
|
+
codeGeneration: false
|
|
259
|
+
}));
|
|
260
|
+
return toResult(true, files, diagnostics);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
diagnostics.push({
|
|
263
|
+
level: "error",
|
|
264
|
+
message: error instanceof Error ? error.message : String(error)
|
|
265
|
+
});
|
|
266
|
+
return toResult(false, files, diagnostics);
|
|
267
|
+
} finally {
|
|
268
|
+
root.setProjectType(previousProjectType);
|
|
269
|
+
options.document.setLogger(previousLogger);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
export { publishBrowser };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfairygui/functions",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.21",
|
|
4
4
|
"description": "FairyGUI Headless Authoring SDK — composable transform functions.",
|
|
5
5
|
"author": "OpenFairyGUI Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,6 +38,26 @@
|
|
|
38
38
|
"types": "./dist/uam-transaction.d.ts",
|
|
39
39
|
"default": "./dist/uam-transaction.js"
|
|
40
40
|
}
|
|
41
|
+
},
|
|
42
|
+
"./node": {
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./dist/node.d.cts",
|
|
45
|
+
"default": "./dist/node.cjs"
|
|
46
|
+
},
|
|
47
|
+
"default": {
|
|
48
|
+
"types": "./dist/node.d.ts",
|
|
49
|
+
"default": "./dist/node.js"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"./web": {
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/web.d.cts",
|
|
55
|
+
"default": "./dist/web.cjs"
|
|
56
|
+
},
|
|
57
|
+
"default": {
|
|
58
|
+
"types": "./dist/web.d.ts",
|
|
59
|
+
"default": "./dist/web.js"
|
|
60
|
+
}
|
|
41
61
|
}
|
|
42
62
|
},
|
|
43
63
|
"files": [
|
|
@@ -53,14 +73,18 @@
|
|
|
53
73
|
"publish"
|
|
54
74
|
],
|
|
55
75
|
"dependencies": {
|
|
56
|
-
"
|
|
76
|
+
"jiti": "^2.6.1",
|
|
77
|
+
"@openfairygui/core": "0.2.0-alpha.21"
|
|
78
|
+
},
|
|
79
|
+
"optionalDependencies": {
|
|
80
|
+
"sharp": ">=0.33.0"
|
|
57
81
|
},
|
|
58
82
|
"devDependencies": {
|
|
59
83
|
"ava": "^7.0.0",
|
|
60
84
|
"tsx": "^4.0.0"
|
|
61
85
|
},
|
|
62
86
|
"scripts": {
|
|
63
|
-
"build": "tsdown src/index.ts src/uam-transaction.ts --format esm,cjs --platform neutral --env.PACKAGE_VERSION=$npm_package_version",
|
|
64
|
-
"build:watch": "tsdown src/index.ts src/uam-transaction.ts --watch --format esm,cjs --platform neutral --env.PACKAGE_VERSION=$npm_package_version"
|
|
87
|
+
"build": "tsdown src/index.ts src/uam-transaction.ts src/node.ts src/web.ts --format esm,cjs --platform neutral --env.PACKAGE_VERSION=$npm_package_version",
|
|
88
|
+
"build:watch": "tsdown src/index.ts src/uam-transaction.ts src/node.ts src/web.ts --watch --format esm,cjs --platform neutral --env.PACKAGE_VERSION=$npm_package_version"
|
|
65
89
|
}
|
|
66
90
|
}
|