@openfairygui/functions 0.1.0 → 0.2.0-alpha.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.
- package/README.md +36 -1
- package/dist/index.cjs +859 -11
- package/dist/index.d.cts +69 -2
- package/dist/index.d.ts +69 -2
- package/dist/index.js +859 -13
- package/package.json +26 -26
- package/src/atlas.ts +335 -391
- package/src/index.ts +19 -3
- package/src/inspect.ts +98 -98
- package/src/max-rects-packer-compat.ts +3 -3
- package/src/rename.ts +66 -66
- package/src/restore.ts +1277 -0
- package/src/uam-transaction.ts +68 -0
- package/src/validate.ts +173 -173
package/src/atlas.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
1
|
import { GearType, TransitionActionType, type Component, type Document, type DragonBonesResource, type FontResource, type ILogger, type ImageResource, type MovieClipResource, type Package, type SpineResource, type Transform } from '@openfairygui/core';
|
|
2
2
|
import { COMPAT_NODE_RECT_FLAGS, type CompatNodeRect } from './max-rects-compat.js';
|
|
3
3
|
import { MaxRectsPackerCompat } from './max-rects-packer-compat.js';
|
|
4
|
-
import type { ExtrasMap, HasOptionalSrc, HasOptionalUrl } from './shared-types.js';
|
|
5
|
-
import { createTransform } from './utils.js';
|
|
6
|
-
|
|
4
|
+
import type { ExtrasMap, HasOptionalSrc, HasOptionalUrl } from './shared-types.js';
|
|
5
|
+
import { createTransform } from './utils.js';
|
|
6
|
+
|
|
7
7
|
export interface AtlasOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Sharp module instance, injected by the caller.
|
|
10
|
-
* Required for actual image compositing and trimImage.
|
|
11
|
-
*
|
|
12
|
-
* ```ts
|
|
13
|
-
* import sharp from 'sharp';
|
|
14
|
-
* await doc.transform(atlas({ encoder: sharp }));
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
encoder?: unknown;
|
|
18
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Sharp module instance, injected by the caller.
|
|
10
|
+
* Required for actual image compositing and trimImage.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import sharp from 'sharp';
|
|
14
|
+
* await doc.transform(atlas({ encoder: sharp }));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
encoder?: unknown;
|
|
18
|
+
|
|
19
19
|
/** Maximum atlas texture size (width and height). Default: 2048. */
|
|
20
20
|
maxSize?: number;
|
|
21
21
|
|
|
22
22
|
/** Whether to use the fast editor-compatible packing heuristics. Default: true. */
|
|
23
23
|
fast?: boolean;
|
|
24
|
-
|
|
25
|
-
/** Allow rotating sprites 90° for better packing. Default: true. */
|
|
26
|
-
allowRotation?: boolean;
|
|
27
|
-
|
|
28
|
-
/** Pixel padding between sprites. Default: 1. */
|
|
29
|
-
padding?: number;
|
|
30
|
-
|
|
31
|
-
/** Constrain atlas dimensions to powers of two. Default: false. */
|
|
32
|
-
powerOfTwo?: boolean;
|
|
33
|
-
|
|
24
|
+
|
|
25
|
+
/** Allow rotating sprites 90° for better packing. Default: true. */
|
|
26
|
+
allowRotation?: boolean;
|
|
27
|
+
|
|
28
|
+
/** Pixel padding between sprites. Default: 1. */
|
|
29
|
+
padding?: number;
|
|
30
|
+
|
|
31
|
+
/** Constrain atlas dimensions to powers of two. Default: false. */
|
|
32
|
+
powerOfTwo?: boolean;
|
|
33
|
+
|
|
34
34
|
/** Force square atlas (width === height). Default: false. */
|
|
35
35
|
square?: boolean;
|
|
36
36
|
|
|
37
37
|
/** Allow spilling into multiple atlas pages. Default: true. */
|
|
38
38
|
multiPage?: boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Trim transparent pixels from image edges before packing.
|
|
42
|
-
* Requires encoder (sharp). Stores offset/originalSize in Sprite nodes.
|
|
43
|
-
* Default: false.
|
|
44
|
-
*/
|
|
45
|
-
trimImage?: boolean;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Base path for reading source images. If not set, images must have
|
|
49
|
-
* their pixel data stored in extras._imageData as Uint8Array.
|
|
50
|
-
*/
|
|
51
|
-
basePath?: string;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Output directory for generated atlas PNGs.
|
|
55
|
-
* Required when encoder is provided.
|
|
56
|
-
*/
|
|
57
|
-
outputPath?: string;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Optional mkdir function to ensure output directory exists.
|
|
61
|
-
* If not provided, the outputPath directory must already exist.
|
|
62
|
-
*/
|
|
63
|
-
mkdir?: (path: string) => Promise<void>;
|
|
64
|
-
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Trim transparent pixels from image edges before packing.
|
|
42
|
+
* Requires encoder (sharp). Stores offset/originalSize in Sprite nodes.
|
|
43
|
+
* Default: false.
|
|
44
|
+
*/
|
|
45
|
+
trimImage?: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Base path for reading source images. If not set, images must have
|
|
49
|
+
* their pixel data stored in extras._imageData as Uint8Array.
|
|
50
|
+
*/
|
|
51
|
+
basePath?: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Output directory for generated atlas PNGs.
|
|
55
|
+
* Required when encoder is provided.
|
|
56
|
+
*/
|
|
57
|
+
outputPath?: string;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Optional mkdir function to ensure output directory exists.
|
|
61
|
+
* If not provided, the outputPath directory must already exist.
|
|
62
|
+
*/
|
|
63
|
+
mkdir?: (path: string) => Promise<void>;
|
|
64
|
+
|
|
65
65
|
/**
|
|
66
66
|
* Optional raw file reader for reading .jta MovieClip files.
|
|
67
67
|
* Required for MovieClip frame atlas packing.
|
|
@@ -109,34 +109,33 @@ const ATLAS_DEFAULTS: Required<Omit<AtlasOptions, 'encoder' | 'basePath' | 'outp
|
|
|
109
109
|
extractAlpha: false,
|
|
110
110
|
separatedAtlasForBranch: false,
|
|
111
111
|
};
|
|
112
|
-
|
|
113
|
-
/** Trim info for a single image. */
|
|
114
|
-
interface TrimInfo {
|
|
115
|
-
/** Trimmed pixel data (PNG). */
|
|
116
|
-
buffer: Uint8Array;
|
|
117
|
-
/** Trimmed width. */
|
|
118
|
-
width: number;
|
|
119
|
-
/** Trimmed height. */
|
|
120
|
-
height: number;
|
|
121
|
-
/** Offset from original left edge. */
|
|
122
|
-
offsetX: number;
|
|
123
|
-
/** Offset from original top edge. */
|
|
124
|
-
offsetY: number;
|
|
125
|
-
/** Original width before trim. */
|
|
126
|
-
originalWidth: number;
|
|
127
|
-
/** Original height before trim. */
|
|
128
|
-
originalHeight: number;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
type PackageResource = ReturnType<Package['listResources']>[number];
|
|
132
|
-
type PackableResource = ImageResource | MovieClipResource | FontResource;
|
|
133
|
-
type PackInputResource = ImageResource | MovieClipResource;
|
|
134
|
-
type ParsedFnt = ReturnType<typeof _parseFnt>;
|
|
112
|
+
|
|
113
|
+
/** Trim info for a single image. */
|
|
114
|
+
interface TrimInfo {
|
|
115
|
+
/** Trimmed pixel data (PNG). */
|
|
116
|
+
buffer: Uint8Array;
|
|
117
|
+
/** Trimmed width. */
|
|
118
|
+
width: number;
|
|
119
|
+
/** Trimmed height. */
|
|
120
|
+
height: number;
|
|
121
|
+
/** Offset from original left edge. */
|
|
122
|
+
offsetX: number;
|
|
123
|
+
/** Offset from original top edge. */
|
|
124
|
+
offsetY: number;
|
|
125
|
+
/** Original width before trim. */
|
|
126
|
+
originalWidth: number;
|
|
127
|
+
/** Original height before trim. */
|
|
128
|
+
originalHeight: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type PackageResource = ReturnType<Package['listResources']>[number];
|
|
132
|
+
type PackableResource = ImageResource | MovieClipResource | FontResource;
|
|
133
|
+
type PackInputResource = ImageResource | MovieClipResource;
|
|
135
134
|
|
|
136
135
|
function getPublishedItemId(resource: { getId(): string; getExtras(): ExtrasMap | undefined }): string {
|
|
137
136
|
return ((resource.getExtras() as ImageResourceExtras | undefined) ?? {})._publishedId ?? resource.getId();
|
|
138
137
|
}
|
|
139
|
-
|
|
138
|
+
|
|
140
139
|
interface AtlasReferenceItem {
|
|
141
140
|
icon?: string | null;
|
|
142
141
|
url?: string | null;
|
|
@@ -176,12 +175,12 @@ interface ChildWithReferenceUrls extends HasOptionalSrc, HasOptionalUrl {
|
|
|
176
175
|
getListItems?(): AtlasReferenceItem[];
|
|
177
176
|
listGears?(): GearWithAtlasRefs[];
|
|
178
177
|
}
|
|
179
|
-
|
|
178
|
+
|
|
180
179
|
interface ImageResourceExtras extends ExtrasMap {
|
|
181
180
|
_fileName?: string;
|
|
182
181
|
_publishedId?: string;
|
|
183
182
|
}
|
|
184
|
-
|
|
183
|
+
|
|
185
184
|
interface FontSpriteAlias {
|
|
186
185
|
fontId: string;
|
|
187
186
|
textureId: string;
|
|
@@ -209,18 +208,18 @@ interface AtlasEncoderMetadata {
|
|
|
209
208
|
trimOffsetLeft?: number;
|
|
210
209
|
trimOffsetTop?: number;
|
|
211
210
|
}
|
|
212
|
-
|
|
211
|
+
|
|
213
212
|
interface AtlasEncoderResolvedBuffer {
|
|
214
213
|
data: Uint8Array;
|
|
215
214
|
info: Required<Pick<AtlasEncoderMetadata, 'width' | 'height' | 'channels'>> & AtlasEncoderMetadata;
|
|
216
215
|
}
|
|
217
|
-
|
|
218
|
-
interface AtlasCompositeInput {
|
|
219
|
-
input: Uint8Array;
|
|
220
|
-
left: number;
|
|
221
|
-
top: number;
|
|
222
|
-
}
|
|
223
|
-
|
|
216
|
+
|
|
217
|
+
interface AtlasCompositeInput {
|
|
218
|
+
input: Uint8Array;
|
|
219
|
+
left: number;
|
|
220
|
+
top: number;
|
|
221
|
+
}
|
|
222
|
+
|
|
224
223
|
interface AtlasEncoderPipeline {
|
|
225
224
|
ensureAlpha(): AtlasEncoderPipeline;
|
|
226
225
|
raw(): AtlasEncoderPipeline;
|
|
@@ -228,85 +227,31 @@ interface AtlasEncoderPipeline {
|
|
|
228
227
|
toBuffer(options: { resolveWithObject: true }): Promise<AtlasEncoderResolvedBuffer>;
|
|
229
228
|
toBuffer(options?: { resolveWithObject?: false }): Promise<Uint8Array>;
|
|
230
229
|
toBuffer(options?: { resolveWithObject?: boolean }): Promise<Uint8Array | AtlasEncoderResolvedBuffer>;
|
|
231
|
-
png(): AtlasEncoderPipeline;
|
|
232
|
-
metadata(): Promise<AtlasEncoderMetadata>;
|
|
233
|
-
rotate(angle: number): AtlasEncoderPipeline;
|
|
234
|
-
composite(inputs: AtlasCompositeInput[]): AtlasEncoderPipeline;
|
|
235
|
-
toFile(path: string): Promise<unknown>;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
type AtlasEncoderInput =
|
|
239
|
-
| string
|
|
240
|
-
| Uint8Array
|
|
241
|
-
| {
|
|
242
|
-
create: {
|
|
243
|
-
width: number;
|
|
244
|
-
height: number;
|
|
245
|
-
channels: 4;
|
|
246
|
-
background: { r: number; g: number; b: number; alpha: number };
|
|
247
|
-
};
|
|
248
|
-
};
|
|
249
|
-
|
|
230
|
+
png(): AtlasEncoderPipeline;
|
|
231
|
+
metadata(): Promise<AtlasEncoderMetadata>;
|
|
232
|
+
rotate(angle: number): AtlasEncoderPipeline;
|
|
233
|
+
composite(inputs: AtlasCompositeInput[]): AtlasEncoderPipeline;
|
|
234
|
+
toFile(path: string): Promise<unknown>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
type AtlasEncoderInput =
|
|
238
|
+
| string
|
|
239
|
+
| Uint8Array
|
|
240
|
+
| {
|
|
241
|
+
create: {
|
|
242
|
+
width: number;
|
|
243
|
+
height: number;
|
|
244
|
+
channels: 4;
|
|
245
|
+
background: { r: number; g: number; b: number; alpha: number };
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
|
|
250
249
|
type AtlasEncoder = (input: AtlasEncoderInput) => AtlasEncoderPipeline;
|
|
251
250
|
|
|
252
251
|
function resolveFontFileName(fontName: string): string {
|
|
253
252
|
return /\.fnt$/i.test(fontName) ? fontName : `${fontName}.fnt`;
|
|
254
253
|
}
|
|
255
254
|
|
|
256
|
-
function addLocalResourceByUiUrl(
|
|
257
|
-
target: PackageResource[],
|
|
258
|
-
added: Set<string>,
|
|
259
|
-
resourceMap: Map<string, PackageResource>,
|
|
260
|
-
pkgId: string,
|
|
261
|
-
value: string | null | undefined,
|
|
262
|
-
): void {
|
|
263
|
-
if (!value || typeof value !== 'string' || !value.startsWith('ui://')) return;
|
|
264
|
-
const normalized = value.slice(5).split(',')[0] ?? '';
|
|
265
|
-
if (!normalized) return;
|
|
266
|
-
let resourceId = '';
|
|
267
|
-
const slashIndex = normalized.indexOf('/');
|
|
268
|
-
if (slashIndex >= 0) {
|
|
269
|
-
const packageToken = normalized.slice(0, slashIndex);
|
|
270
|
-
if (packageToken !== pkgId) return;
|
|
271
|
-
resourceId = normalized.slice(slashIndex + 1);
|
|
272
|
-
} else if (normalized.length > 8) {
|
|
273
|
-
const packageToken = normalized.slice(0, 8);
|
|
274
|
-
if (packageToken !== pkgId) return;
|
|
275
|
-
resourceId = normalized.slice(8);
|
|
276
|
-
}
|
|
277
|
-
if (!resourceId) return;
|
|
278
|
-
const resource = resourceMap.get(resourceId);
|
|
279
|
-
if (!resource || added.has(resourceId)) return;
|
|
280
|
-
added.add(resourceId);
|
|
281
|
-
target.push(resource);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function addLocalResourcesByText(
|
|
285
|
-
target: PackageResource[],
|
|
286
|
-
added: Set<string>,
|
|
287
|
-
resourceMap: Map<string, PackageResource>,
|
|
288
|
-
pkgId: string,
|
|
289
|
-
value: string | null | undefined,
|
|
290
|
-
): void {
|
|
291
|
-
if (!value || typeof value !== 'string') return;
|
|
292
|
-
for (const match of value.matchAll(/ui:\/\/[0-9a-z]{8}[0-9a-z]+/gi)) {
|
|
293
|
-
addLocalResourceByUiUrl(target, added, resourceMap, pkgId, match[0]);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function addResourceById(
|
|
298
|
-
target: PackageResource[],
|
|
299
|
-
added: Set<string>,
|
|
300
|
-
resourceMap: Map<string, PackageResource>,
|
|
301
|
-
resourceId: string | null | undefined,
|
|
302
|
-
): void {
|
|
303
|
-
if (!resourceId) return;
|
|
304
|
-
const resource = resourceMap.get(resourceId);
|
|
305
|
-
if (!resource || added.has(resourceId)) return;
|
|
306
|
-
added.add(resourceId);
|
|
307
|
-
target.push(resource);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
255
|
async function resolveEditorCompatibleResourceOrder(
|
|
311
256
|
pkg: Package,
|
|
312
257
|
allResources: PackageResource[],
|
|
@@ -449,38 +394,38 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
449
394
|
|
|
450
395
|
return ordered;
|
|
451
396
|
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Packs image resources into texture atlases.
|
|
455
|
-
*
|
|
456
|
-
* This transform performs MaxRects bin-packing on all ImageResource items
|
|
457
|
-
* within each package, creating Atlas and Sprite property nodes. When an
|
|
458
|
-
* `encoder` (sharp) is provided, it also composites the actual PNG files.
|
|
459
|
-
*
|
|
460
|
-
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
461
|
-
* at image edges are trimmed before packing. The trimmed offset and original
|
|
462
|
-
* dimensions are stored in the Sprite nodes for runtime reconstruction.
|
|
463
|
-
*
|
|
464
|
-
* ```ts
|
|
465
|
-
* import sharp from 'sharp';
|
|
466
|
-
* await doc.transform(atlas({
|
|
467
|
-
* encoder: sharp,
|
|
468
|
-
* maxSize: 2048,
|
|
469
|
-
* trimImage: true,
|
|
470
|
-
* basePath: './assets/',
|
|
471
|
-
* outputPath: './dist/',
|
|
472
|
-
* }));
|
|
473
|
-
* ```
|
|
474
|
-
*/
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Packs image resources into texture atlases.
|
|
400
|
+
*
|
|
401
|
+
* This transform performs MaxRects bin-packing on all ImageResource items
|
|
402
|
+
* within each package, creating Atlas and Sprite property nodes. When an
|
|
403
|
+
* `encoder` (sharp) is provided, it also composites the actual PNG files.
|
|
404
|
+
*
|
|
405
|
+
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
406
|
+
* at image edges are trimmed before packing. The trimmed offset and original
|
|
407
|
+
* dimensions are stored in the Sprite nodes for runtime reconstruction.
|
|
408
|
+
*
|
|
409
|
+
* ```ts
|
|
410
|
+
* import sharp from 'sharp';
|
|
411
|
+
* await doc.transform(atlas({
|
|
412
|
+
* encoder: sharp,
|
|
413
|
+
* maxSize: 2048,
|
|
414
|
+
* trimImage: true,
|
|
415
|
+
* basePath: './assets/',
|
|
416
|
+
* outputPath: './dist/',
|
|
417
|
+
* }));
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
475
420
|
export function atlas(_options: AtlasOptions = {}): Transform {
|
|
476
|
-
const options = { ...ATLAS_DEFAULTS, ..._options };
|
|
477
|
-
|
|
478
|
-
return createTransform('atlas', async (doc: Document): Promise<void> => {
|
|
479
|
-
const root = doc.getRoot();
|
|
480
|
-
const logger = doc.getLogger();
|
|
481
|
-
const encoder = options.encoder as AtlasEncoder | undefined;
|
|
482
|
-
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
483
|
-
|
|
421
|
+
const options = { ...ATLAS_DEFAULTS, ..._options };
|
|
422
|
+
|
|
423
|
+
return createTransform('atlas', async (doc: Document): Promise<void> => {
|
|
424
|
+
const root = doc.getRoot();
|
|
425
|
+
const logger = doc.getLogger();
|
|
426
|
+
const encoder = options.encoder as AtlasEncoder | undefined;
|
|
427
|
+
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
428
|
+
|
|
484
429
|
for (const pkg of root.listPackages()) {
|
|
485
430
|
// Respect publish-selected resources when publish() precomputes a merged branch view.
|
|
486
431
|
const selectedPublishIds = new Set(((pkg.getExtras() as PackageAtlasExtras | undefined) ?? {}).publishedResourceIds ?? []);
|
|
@@ -494,30 +439,30 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
494
439
|
const orderedAllResources = sortResourcesByOrder(allResources, resourceOrder, inputOrder);
|
|
495
440
|
const hasPackable = allResources.some((resource) => isPackableResource(resource));
|
|
496
441
|
if (!hasPackable) continue;
|
|
497
|
-
|
|
498
|
-
// Collect packable items in declaration order
|
|
499
|
-
const inputs: InputItem[] = [];
|
|
500
|
-
|
|
501
|
-
// Build set of referenced resource IDs (editor only packs referenced images)
|
|
502
|
-
// Walk component tree recursively to find all image references
|
|
503
|
-
const referencedIds = new Set<string>();
|
|
504
|
-
const resourceMap = new Map<string, PackageResource>();
|
|
505
|
-
for (const res of allResources) {
|
|
506
|
-
const id = res.getId();
|
|
507
|
-
if (id) resourceMap.set(id, res);
|
|
508
|
-
}
|
|
509
|
-
function collectRefs(component: Component, visited: Set<string>): void {
|
|
510
|
-
for (const child of component.listChildren()) {
|
|
511
|
-
const refChild = child as ChildWithReferenceUrls;
|
|
512
|
-
const src = refChild.getSrc?.();
|
|
513
|
-
if (src && !visited.has(src)) {
|
|
514
|
-
referencedIds.add(src);
|
|
515
|
-
visited.add(src);
|
|
516
|
-
const srcRes = resourceMap.get(src);
|
|
517
|
-
if (srcRes && isComponentResource(srcRes)) {
|
|
518
|
-
collectRefs(srcRes, visited);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
442
|
+
|
|
443
|
+
// Collect packable items in declaration order
|
|
444
|
+
const inputs: InputItem[] = [];
|
|
445
|
+
|
|
446
|
+
// Build set of referenced resource IDs (editor only packs referenced images)
|
|
447
|
+
// Walk component tree recursively to find all image references
|
|
448
|
+
const referencedIds = new Set<string>();
|
|
449
|
+
const resourceMap = new Map<string, PackageResource>();
|
|
450
|
+
for (const res of allResources) {
|
|
451
|
+
const id = res.getId();
|
|
452
|
+
if (id) resourceMap.set(id, res);
|
|
453
|
+
}
|
|
454
|
+
function collectRefs(component: Component, visited: Set<string>): void {
|
|
455
|
+
for (const child of component.listChildren()) {
|
|
456
|
+
const refChild = child as ChildWithReferenceUrls;
|
|
457
|
+
const src = refChild.getSrc?.();
|
|
458
|
+
if (src && !visited.has(src)) {
|
|
459
|
+
referencedIds.add(src);
|
|
460
|
+
visited.add(src);
|
|
461
|
+
const srcRes = resourceMap.get(src);
|
|
462
|
+
if (srcRes && isComponentResource(srcRes)) {
|
|
463
|
+
collectRefs(srcRes, visited);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
521
466
|
for (const ref of [
|
|
522
467
|
refChild.getIcon?.(),
|
|
523
468
|
refChild.getSelectedIcon?.(),
|
|
@@ -565,23 +510,23 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
565
510
|
if (isFontResource(res)) {
|
|
566
511
|
const textureId = res.getTextureId?.() ?? '';
|
|
567
512
|
if (textureId) referencedIds.add(textureId);
|
|
568
|
-
// Parse .fnt file for glyph image references
|
|
513
|
+
// Parse .fnt file for glyph image references
|
|
569
514
|
if (options.readFileRaw && options.basePath) {
|
|
570
515
|
const fontName = resolveFontFileName(res.getName());
|
|
571
516
|
const fontPath = res.getPath() ?? '/';
|
|
572
517
|
const fntFile = `${options.basePath}/${pkg.getName()}${fontPath}${fontName}`;
|
|
573
|
-
try {
|
|
574
|
-
const fntData = await options.readFileRaw(fntFile);
|
|
575
|
-
const fntText = new TextDecoder().decode(fntData);
|
|
576
|
-
for (const line of fntText.split(/\r?\n/)) {
|
|
577
|
-
const match = line.match(/img=(\w+)/);
|
|
578
|
-
if (match) referencedIds.add(match[1]);
|
|
579
|
-
}
|
|
580
|
-
} catch { /* .fnt file not found — OK */ }
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
518
|
+
try {
|
|
519
|
+
const fntData = await options.readFileRaw(fntFile);
|
|
520
|
+
const fntText = new TextDecoder().decode(fntData);
|
|
521
|
+
for (const line of fntText.split(/\r?\n/)) {
|
|
522
|
+
const match = line.match(/img=(\w+)/);
|
|
523
|
+
if (match) referencedIds.add(match[1]);
|
|
524
|
+
}
|
|
525
|
+
} catch { /* .fnt file not found — OK */ }
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
585
530
|
for (const res of orderedAllResources) {
|
|
586
531
|
if (isImageResource(res)) {
|
|
587
532
|
// Pack referenced images, plus explicitly exported standalone images.
|
|
@@ -801,7 +746,7 @@ function buildBranchAtlasGroups(doc: Document, inputs: InputItem[], options: Atl
|
|
|
801
746
|
inputs: groups.get(branchName) ?? [],
|
|
802
747
|
}));
|
|
803
748
|
}
|
|
804
|
-
|
|
749
|
+
|
|
805
750
|
function inputToCompatRect(input: InputItem, index: number): CompatNodeRect {
|
|
806
751
|
const duplicatePadding = isImageResource(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
807
752
|
return {
|
|
@@ -983,12 +928,12 @@ interface ExtractedJtaData {
|
|
|
983
928
|
frames: Uint8Array[];
|
|
984
929
|
meta?: ExtractedJtaMeta;
|
|
985
930
|
}
|
|
986
|
-
|
|
987
|
-
/**
|
|
988
|
-
* Trim transparent edges from an image using sharp.
|
|
989
|
-
* Returns the trimmed buffer, dimensions, and offsets.
|
|
990
|
-
* Falls back to the original image if trim fails (e.g. no alpha channel, no transparent edges).
|
|
991
|
-
*/
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Trim transparent edges from an image using sharp.
|
|
934
|
+
* Returns the trimmed buffer, dimensions, and offsets.
|
|
935
|
+
* Falls back to the original image if trim fails (e.g. no alpha channel, no transparent edges).
|
|
936
|
+
*/
|
|
992
937
|
async function _trimImage(
|
|
993
938
|
encoder: AtlasEncoder,
|
|
994
939
|
filePath: string,
|
|
@@ -1055,24 +1000,24 @@ async function _trimImage(
|
|
|
1055
1000
|
originalWidth,
|
|
1056
1001
|
originalHeight,
|
|
1057
1002
|
};
|
|
1058
|
-
} catch {
|
|
1059
|
-
// Trim failed (e.g. JPEG without alpha, nothing to trim) — return original
|
|
1060
|
-
const buf = await encoder(filePath).png().toBuffer();
|
|
1061
|
-
return {
|
|
1062
|
-
buffer: buf,
|
|
1063
|
-
width: originalWidth,
|
|
1064
|
-
height: originalHeight,
|
|
1065
|
-
offsetX: 0,
|
|
1066
|
-
offsetY: 0,
|
|
1067
|
-
originalWidth,
|
|
1068
|
-
originalHeight,
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
/**
|
|
1074
|
-
* Resolve an ImageResource to its actual file path on disk.
|
|
1075
|
-
*/
|
|
1003
|
+
} catch {
|
|
1004
|
+
// Trim failed (e.g. JPEG without alpha, nothing to trim) — return original
|
|
1005
|
+
const buf = await encoder(filePath).png().toBuffer();
|
|
1006
|
+
return {
|
|
1007
|
+
buffer: buf,
|
|
1008
|
+
width: originalWidth,
|
|
1009
|
+
height: originalHeight,
|
|
1010
|
+
offsetX: 0,
|
|
1011
|
+
offsetY: 0,
|
|
1012
|
+
originalWidth,
|
|
1013
|
+
originalHeight,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Resolve an ImageResource to its actual file path on disk.
|
|
1020
|
+
*/
|
|
1076
1021
|
function _resolveImagePath(resource: ImageResource, pkg: Package, basePath: string): string {
|
|
1077
1022
|
const imgPath = resource.getPath() ?? '/';
|
|
1078
1023
|
const fileName = resolveImageFileName(resource);
|
|
@@ -1085,7 +1030,7 @@ function _resolveImagePath(resource: ImageResource, pkg: Package, basePath: stri
|
|
|
1085
1030
|
: `${normalizedBasePath}_${branchName}`;
|
|
1086
1031
|
return `${packageBasePath}/${pkg.getName()}${imgPath}${fileName}`;
|
|
1087
1032
|
}
|
|
1088
|
-
|
|
1033
|
+
|
|
1089
1034
|
type InputItem = {
|
|
1090
1035
|
id: string; width: number; height: number;
|
|
1091
1036
|
originalWidth: number; originalHeight: number;
|
|
@@ -1093,17 +1038,17 @@ type InputItem = {
|
|
|
1093
1038
|
resource: PackInputResource; trimBuffer?: Uint8Array;
|
|
1094
1039
|
sourceKind: 'image' | 'movieclip-frame';
|
|
1095
1040
|
};
|
|
1096
|
-
|
|
1097
|
-
/** Collect a single ImageResource into the inputs array. */
|
|
1041
|
+
|
|
1042
|
+
/** Collect a single ImageResource into the inputs array. */
|
|
1098
1043
|
async function _collectImage(
|
|
1099
1044
|
resource: ImageResource,
|
|
1100
|
-
pkg: Package,
|
|
1101
|
-
inputs: InputItem[],
|
|
1102
|
-
encoder: AtlasEncoder | undefined,
|
|
1103
|
-
options: AtlasOptions,
|
|
1104
|
-
doTrim: boolean,
|
|
1105
|
-
logger: ILogger,
|
|
1106
|
-
): Promise<void> {
|
|
1045
|
+
pkg: Package,
|
|
1046
|
+
inputs: InputItem[],
|
|
1047
|
+
encoder: AtlasEncoder | undefined,
|
|
1048
|
+
options: AtlasOptions,
|
|
1049
|
+
doTrim: boolean,
|
|
1050
|
+
logger: ILogger,
|
|
1051
|
+
): Promise<void> {
|
|
1107
1052
|
let origW = resource.getWidth() ?? 0;
|
|
1108
1053
|
let origH = resource.getHeight() ?? 0;
|
|
1109
1054
|
let sourceHasAlpha = false;
|
|
@@ -1128,24 +1073,24 @@ async function _collectImage(
|
|
|
1128
1073
|
}
|
|
1129
1074
|
|
|
1130
1075
|
if (origW <= 0 || origH <= 0) return;
|
|
1131
|
-
|
|
1132
|
-
let packW = origW, packH = origH, offX = 0, offY = 0;
|
|
1133
|
-
let trimBuf: Uint8Array | undefined;
|
|
1134
|
-
|
|
1076
|
+
|
|
1077
|
+
let packW = origW, packH = origH, offX = 0, offY = 0;
|
|
1078
|
+
let trimBuf: Uint8Array | undefined;
|
|
1079
|
+
|
|
1135
1080
|
if (doTrim && sourceHasAlpha && options.basePath && encoder) {
|
|
1136
1081
|
const filePath = _resolveImagePath(resource, pkg, options.basePath);
|
|
1137
1082
|
try {
|
|
1138
1083
|
const trimResult = await _trimImage(encoder, filePath, origW, origH);
|
|
1139
|
-
packW = trimResult.width;
|
|
1140
|
-
packH = trimResult.height;
|
|
1141
|
-
offX = trimResult.offsetX;
|
|
1142
|
-
offY = trimResult.offsetY;
|
|
1143
|
-
trimBuf = trimResult.buffer;
|
|
1144
|
-
} catch {
|
|
1145
|
-
logger.warn(`atlas: Could not trim "${filePath}", using original.`);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1084
|
+
packW = trimResult.width;
|
|
1085
|
+
packH = trimResult.height;
|
|
1086
|
+
offX = trimResult.offsetX;
|
|
1087
|
+
offY = trimResult.offsetY;
|
|
1088
|
+
trimBuf = trimResult.buffer;
|
|
1089
|
+
} catch {
|
|
1090
|
+
logger.warn(`atlas: Could not trim "${filePath}", using original.`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1149
1094
|
inputs.push({
|
|
1150
1095
|
id: getPublishedItemId(resource), width: packW, height: packH,
|
|
1151
1096
|
originalWidth: origW, originalHeight: origH,
|
|
@@ -1155,22 +1100,22 @@ async function _collectImage(
|
|
|
1155
1100
|
sourceKind: 'image',
|
|
1156
1101
|
});
|
|
1157
1102
|
}
|
|
1158
|
-
|
|
1159
|
-
/** Collect MovieClip frame textures from a .jta file into the inputs array. */
|
|
1103
|
+
|
|
1104
|
+
/** Collect MovieClip frame textures from a .jta file into the inputs array. */
|
|
1160
1105
|
async function _collectMovieClipFrames(
|
|
1161
1106
|
doc: Document,
|
|
1162
1107
|
resource: MovieClipResource,
|
|
1163
1108
|
pkg: Package,
|
|
1164
1109
|
inputs: InputItem[],
|
|
1165
|
-
encoder: AtlasEncoder | undefined,
|
|
1166
|
-
options: AtlasOptions,
|
|
1167
|
-
logger: ILogger,
|
|
1168
|
-
): Promise<void> {
|
|
1169
|
-
if (!options.basePath || !options.readFileRaw) return;
|
|
1170
|
-
|
|
1171
|
-
const mcId = resource.getId();
|
|
1172
|
-
const mcName = resource.getName() + '.jta';
|
|
1173
|
-
const mcPath = resource.getPath() ?? '/';
|
|
1110
|
+
encoder: AtlasEncoder | undefined,
|
|
1111
|
+
options: AtlasOptions,
|
|
1112
|
+
logger: ILogger,
|
|
1113
|
+
): Promise<void> {
|
|
1114
|
+
if (!options.basePath || !options.readFileRaw) return;
|
|
1115
|
+
|
|
1116
|
+
const mcId = resource.getId();
|
|
1117
|
+
const mcName = resource.getName() + '.jta';
|
|
1118
|
+
const mcPath = resource.getPath() ?? '/';
|
|
1174
1119
|
const filePath = `${options.basePath}/${pkg.getName()}${mcPath}${mcName}`;
|
|
1175
1120
|
|
|
1176
1121
|
try {
|
|
@@ -1465,32 +1410,31 @@ function _readUint32BE(data: Uint8Array, offset: number): number {
|
|
|
1465
1410
|
(data[offset + 3] ?? 0)
|
|
1466
1411
|
);
|
|
1467
1412
|
}
|
|
1468
|
-
|
|
1469
|
-
/** Collect a Bitmap Font's texture image, packed under the font's ID. */
|
|
1413
|
+
|
|
1414
|
+
/** Collect a Bitmap Font's texture image, packed under the font's ID. */
|
|
1470
1415
|
async function _collectFontTexture(
|
|
1471
1416
|
doc: Document,
|
|
1472
1417
|
fontRes: FontResource,
|
|
1473
1418
|
pkg: Package,
|
|
1474
1419
|
options: AtlasOptions,
|
|
1475
1420
|
): Promise<void> {
|
|
1476
|
-
const extras = fontRes.getExtras() as FontResourceExtras;
|
|
1477
1421
|
const textureId = fontRes.getTextureId?.() ?? '';
|
|
1478
|
-
|
|
1479
|
-
if (textureId) {
|
|
1480
|
-
// Record font→texture mapping so we can add a duplicate sprite entry
|
|
1481
|
-
// after atlas packing. The editor stores both the image ID (jb800) and
|
|
1482
|
-
// the font ID (wa8u2r) as separate sprites at the same atlas position.
|
|
1483
|
-
const fontId = fontRes.getId();
|
|
1484
|
-
fontRes.setExtras({ ...fontRes.getExtras(), _fontSpriteAlias: { fontId, textureId } });
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
// Parse .fnt file for glyph data (needed for binary encoding)
|
|
1488
|
-
// This applies to ALL fonts, not just those with a textureId
|
|
1422
|
+
|
|
1423
|
+
if (textureId) {
|
|
1424
|
+
// Record font→texture mapping so we can add a duplicate sprite entry
|
|
1425
|
+
// after atlas packing. The editor stores both the image ID (jb800) and
|
|
1426
|
+
// the font ID (wa8u2r) as separate sprites at the same atlas position.
|
|
1427
|
+
const fontId = fontRes.getId();
|
|
1428
|
+
fontRes.setExtras({ ...fontRes.getExtras(), _fontSpriteAlias: { fontId, textureId } });
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// Parse .fnt file for glyph data (needed for binary encoding)
|
|
1432
|
+
// This applies to ALL fonts, not just those with a textureId
|
|
1489
1433
|
if (options.readFileRaw && options.basePath) {
|
|
1490
1434
|
const fontName = resolveFontFileName(fontRes.getName());
|
|
1491
1435
|
const fontPath = fontRes.getPath() ?? '/';
|
|
1492
1436
|
const pkgName = pkg.getName();
|
|
1493
|
-
const fntFile = `${options.basePath}/${pkgName}${fontPath}${fontName}`;
|
|
1437
|
+
const fntFile = `${options.basePath}/${pkgName}${fontPath}${fontName}`;
|
|
1494
1438
|
try {
|
|
1495
1439
|
const fntData = await options.readFileRaw(fntFile);
|
|
1496
1440
|
const fntText = new TextDecoder().decode(fntData);
|
|
@@ -1526,84 +1470,84 @@ async function _collectFontTexture(
|
|
|
1526
1470
|
} catch { /* .fnt not found */ }
|
|
1527
1471
|
}
|
|
1528
1472
|
}
|
|
1529
|
-
|
|
1530
|
-
/** Parse a BMFont .fnt text file into structured data for binary encoding. */
|
|
1531
|
-
function _parseFnt(text: string): {
|
|
1532
|
-
hasFace: boolean; colored: boolean; resizable: boolean; hasChannel: boolean;
|
|
1533
|
-
fontSize: number; xadvance: number; lineHeight: number;
|
|
1534
|
-
glyphs: Array<{
|
|
1535
|
-
charId: number; img: string | null;
|
|
1536
|
-
x: number; y: number; xoffset: number; yoffset: number;
|
|
1537
|
-
width: number; height: number; xadvance: number; channel: number;
|
|
1538
|
-
}>;
|
|
1539
|
-
} {
|
|
1540
|
-
const lines = text.split(/\r?\n/);
|
|
1541
|
-
let hasFace = false, colored = false, resizable = false, hasChannel = false;
|
|
1542
|
-
let fontSize = 0, globalXadvance = 0, lineHeight = 0;
|
|
1543
|
-
const glyphs: Array<{
|
|
1544
|
-
charId: number; img: string | null;
|
|
1545
|
-
x: number; y: number; xoffset: number; yoffset: number;
|
|
1546
|
-
width: number; height: number; xadvance: number; channel: number;
|
|
1547
|
-
}> = [];
|
|
1548
|
-
|
|
1549
|
-
for (const line of lines) {
|
|
1550
|
-
const trimmed = line.trim();
|
|
1551
|
-
if (!trimmed) continue;
|
|
1552
|
-
const parts = trimmed.split(/\s+/);
|
|
1553
|
-
const attrs: Record<string, string> = {};
|
|
1554
|
-
for (let i = 1; i < parts.length; i++) {
|
|
1555
|
-
const eq = parts[i].split('=');
|
|
1556
|
-
if (eq.length === 2) attrs[eq[0]] = eq[1];
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
switch (parts[0]) {
|
|
1560
|
-
case 'info':
|
|
1561
|
-
hasFace = attrs.face != null;
|
|
1562
|
-
colored = hasFace;
|
|
1563
|
-
if (attrs.colored !== undefined) colored = attrs.colored === 'true';
|
|
1564
|
-
fontSize = parseInt(attrs.size, 10) || 0;
|
|
1565
|
-
resizable = attrs.resizable === 'true';
|
|
1566
|
-
break;
|
|
1567
|
-
case 'common':
|
|
1568
|
-
lineHeight = parseInt(attrs.lineHeight, 10) || 0;
|
|
1569
|
-
globalXadvance = parseInt(attrs.xadvance, 10) || 0;
|
|
1570
|
-
if (fontSize === 0) fontSize = lineHeight;
|
|
1571
|
-
else if (lineHeight === 0) lineHeight = fontSize;
|
|
1572
|
-
break;
|
|
1573
|
-
case 'char': {
|
|
1574
|
-
const charId = parseInt(attrs.id, 10) || 0;
|
|
1575
|
-
if (charId === 0) continue;
|
|
1576
|
-
const img = attrs.img || null;
|
|
1577
|
-
if (!hasFace && !img) continue;
|
|
1578
|
-
const chnl = parseInt(attrs.chnl, 10) || 0;
|
|
1579
|
-
if (chnl !== 0 && chnl !== 15) hasChannel = true;
|
|
1580
|
-
glyphs.push({
|
|
1581
|
-
charId, img,
|
|
1582
|
-
x: parseInt(attrs.x, 10) || 0,
|
|
1583
|
-
y: parseInt(attrs.y, 10) || 0,
|
|
1584
|
-
xoffset: parseInt(attrs.xoffset, 10) || 0,
|
|
1585
|
-
yoffset: parseInt(attrs.yoffset, 10) || 0,
|
|
1586
|
-
width: parseInt(attrs.width, 10) || 0,
|
|
1587
|
-
height: parseInt(attrs.height, 10) || 0,
|
|
1588
|
-
xadvance: parseInt(attrs.xadvance, 10) || 0,
|
|
1589
|
-
channel: chnl,
|
|
1590
|
-
});
|
|
1591
|
-
break;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
return { hasFace, colored, resizable: fontSize > 0 ? resizable : false, hasChannel, fontSize, xadvance: globalXadvance, lineHeight, glyphs };
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
function isComponentResource(resource: PackageResource): resource is Component {
|
|
1600
|
-
return resource.propertyType === 'Component';
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
function isImageResource(resource: PackageResource): resource is ImageResource {
|
|
1604
|
-
return resource.propertyType === 'ImageResource';
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1473
|
+
|
|
1474
|
+
/** Parse a BMFont .fnt text file into structured data for binary encoding. */
|
|
1475
|
+
function _parseFnt(text: string): {
|
|
1476
|
+
hasFace: boolean; colored: boolean; resizable: boolean; hasChannel: boolean;
|
|
1477
|
+
fontSize: number; xadvance: number; lineHeight: number;
|
|
1478
|
+
glyphs: Array<{
|
|
1479
|
+
charId: number; img: string | null;
|
|
1480
|
+
x: number; y: number; xoffset: number; yoffset: number;
|
|
1481
|
+
width: number; height: number; xadvance: number; channel: number;
|
|
1482
|
+
}>;
|
|
1483
|
+
} {
|
|
1484
|
+
const lines = text.split(/\r?\n/);
|
|
1485
|
+
let hasFace = false, colored = false, resizable = false, hasChannel = false;
|
|
1486
|
+
let fontSize = 0, globalXadvance = 0, lineHeight = 0;
|
|
1487
|
+
const glyphs: Array<{
|
|
1488
|
+
charId: number; img: string | null;
|
|
1489
|
+
x: number; y: number; xoffset: number; yoffset: number;
|
|
1490
|
+
width: number; height: number; xadvance: number; channel: number;
|
|
1491
|
+
}> = [];
|
|
1492
|
+
|
|
1493
|
+
for (const line of lines) {
|
|
1494
|
+
const trimmed = line.trim();
|
|
1495
|
+
if (!trimmed) continue;
|
|
1496
|
+
const parts = trimmed.split(/\s+/);
|
|
1497
|
+
const attrs: Record<string, string> = {};
|
|
1498
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1499
|
+
const eq = parts[i].split('=');
|
|
1500
|
+
if (eq.length === 2) attrs[eq[0]] = eq[1];
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
switch (parts[0]) {
|
|
1504
|
+
case 'info':
|
|
1505
|
+
hasFace = attrs.face != null;
|
|
1506
|
+
colored = hasFace;
|
|
1507
|
+
if (attrs.colored !== undefined) colored = attrs.colored === 'true';
|
|
1508
|
+
fontSize = parseInt(attrs.size, 10) || 0;
|
|
1509
|
+
resizable = attrs.resizable === 'true';
|
|
1510
|
+
break;
|
|
1511
|
+
case 'common':
|
|
1512
|
+
lineHeight = parseInt(attrs.lineHeight, 10) || 0;
|
|
1513
|
+
globalXadvance = parseInt(attrs.xadvance, 10) || 0;
|
|
1514
|
+
if (fontSize === 0) fontSize = lineHeight;
|
|
1515
|
+
else if (lineHeight === 0) lineHeight = fontSize;
|
|
1516
|
+
break;
|
|
1517
|
+
case 'char': {
|
|
1518
|
+
const charId = parseInt(attrs.id, 10) || 0;
|
|
1519
|
+
if (charId === 0) continue;
|
|
1520
|
+
const img = attrs.img || null;
|
|
1521
|
+
if (!hasFace && !img) continue;
|
|
1522
|
+
const chnl = parseInt(attrs.chnl, 10) || 0;
|
|
1523
|
+
if (chnl !== 0 && chnl !== 15) hasChannel = true;
|
|
1524
|
+
glyphs.push({
|
|
1525
|
+
charId, img,
|
|
1526
|
+
x: parseInt(attrs.x, 10) || 0,
|
|
1527
|
+
y: parseInt(attrs.y, 10) || 0,
|
|
1528
|
+
xoffset: parseInt(attrs.xoffset, 10) || 0,
|
|
1529
|
+
yoffset: parseInt(attrs.yoffset, 10) || 0,
|
|
1530
|
+
width: parseInt(attrs.width, 10) || 0,
|
|
1531
|
+
height: parseInt(attrs.height, 10) || 0,
|
|
1532
|
+
xadvance: parseInt(attrs.xadvance, 10) || 0,
|
|
1533
|
+
channel: chnl,
|
|
1534
|
+
});
|
|
1535
|
+
break;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
return { hasFace, colored, resizable: fontSize > 0 ? resizable : false, hasChannel, fontSize, xadvance: globalXadvance, lineHeight, glyphs };
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function isComponentResource(resource: PackageResource): resource is Component {
|
|
1544
|
+
return resource.propertyType === 'Component';
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
function isImageResource(resource: PackageResource): resource is ImageResource {
|
|
1548
|
+
return resource.propertyType === 'ImageResource';
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1607
1551
|
function isMovieClipResource(resource: PackageResource): resource is MovieClipResource {
|
|
1608
1552
|
return resource.propertyType === 'MovieClipResource';
|
|
1609
1553
|
}
|
|
@@ -1611,15 +1555,15 @@ function isMovieClipResource(resource: PackageResource): resource is MovieClipRe
|
|
|
1611
1555
|
function isSkeletonResource(resource: PackageResource): resource is SpineResource | DragonBonesResource {
|
|
1612
1556
|
return resource.propertyType === 'SpineResource' || resource.propertyType === 'DragonBonesResource';
|
|
1613
1557
|
}
|
|
1614
|
-
|
|
1615
|
-
function isFontResource(resource: PackageResource): resource is FontResource {
|
|
1616
|
-
return resource.propertyType === 'FontResource';
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
function isPackableResource(resource: PackageResource): resource is PackableResource {
|
|
1620
|
-
return isImageResource(resource) || isMovieClipResource(resource) || isFontResource(resource);
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1558
|
+
|
|
1559
|
+
function isFontResource(resource: PackageResource): resource is FontResource {
|
|
1560
|
+
return resource.propertyType === 'FontResource';
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function isPackableResource(resource: PackageResource): resource is PackableResource {
|
|
1564
|
+
return isImageResource(resource) || isMovieClipResource(resource) || isFontResource(resource);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1623
1567
|
function addUiResourceRef(target: Set<string>, value: string | undefined | null): void {
|
|
1624
1568
|
if (!value?.startsWith('ui://')) return;
|
|
1625
1569
|
const refId = value.slice(5).slice(8);
|
|
@@ -1645,7 +1589,7 @@ function addUiResourceRefsFromUnknown(target: Set<string>, value: unknown): void
|
|
|
1645
1589
|
addUiResourceRefsFromText(target, value);
|
|
1646
1590
|
}
|
|
1647
1591
|
}
|
|
1648
|
-
|
|
1649
|
-
function isResolvedBuffer(value: Uint8Array | AtlasEncoderResolvedBuffer): value is AtlasEncoderResolvedBuffer {
|
|
1650
|
-
return typeof value === 'object' && value !== null && 'data' in value && 'info' in value;
|
|
1651
|
-
}
|
|
1592
|
+
|
|
1593
|
+
function isResolvedBuffer(value: Uint8Array | AtlasEncoderResolvedBuffer): value is AtlasEncoderResolvedBuffer {
|
|
1594
|
+
return typeof value === 'object' && value !== null && 'data' in value && 'info' in value;
|
|
1595
|
+
}
|