@openfairygui/functions 0.2.0-alpha.11 → 0.2.0-alpha.13
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 +28 -3
- package/dist/atlas-CDn6TirX.d.ts +193 -0
- package/dist/atlas-CHsu2Y8i.d.cts +193 -0
- package/dist/codegen-C7PPZrB3.d.cts +241 -0
- package/dist/codegen-CEnmtjBP.d.ts +241 -0
- package/dist/index.cjs +41 -3429
- package/dist/index.d.cts +4 -337
- package/dist/index.d.ts +4 -337
- package/dist/index.js +29 -3417
- package/dist/node.cjs +128 -0
- package/dist/node.d.cts +31 -0
- package/dist/node.d.ts +31 -0
- package/dist/node.js +127 -0
- package/dist/publish-Bl-GK9kt.js +3337 -0
- package/dist/publish-CtCXsMdj.cjs +3408 -0
- package/dist/web.cjs +272 -0
- package/dist/web.d.cts +41 -0
- package/dist/web.d.ts +41 -0
- package/dist/web.js +271 -0
- package/package.json +24 -4
- package/src/{plugins/loader.ts → adapters/node/plugins.ts} +8 -11
- package/src/adapters/node/publish.ts +129 -0
- package/src/adapters/web/publish.ts +404 -0
- package/src/atlas.ts +236 -144
- package/src/codegen.ts +3 -2
- package/src/index.ts +19 -3
- package/src/node.ts +4 -0
- package/src/plugins/types.ts +9 -0
- package/src/publish/contracts.ts +80 -0
- package/src/publish.ts +131 -102
- package/src/shared-types.ts +3 -8
- package/src/web.ts +11 -0
package/src/atlas.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
GearType,
|
|
3
|
+
TransitionActionType,
|
|
4
|
+
type Component,
|
|
5
|
+
type Document,
|
|
6
|
+
type DragonBonesResource,
|
|
7
|
+
type FontResource,
|
|
8
|
+
type ILogger,
|
|
9
|
+
type ImageResource,
|
|
10
|
+
type MovieClipResource,
|
|
11
|
+
type Package,
|
|
12
|
+
type SpineResource,
|
|
13
|
+
type Transform,
|
|
14
|
+
} from '@openfairygui/core';
|
|
2
15
|
import { COMPAT_NODE_RECT_FLAGS, type CompatNodeRect } from './max-rects-compat.js';
|
|
3
16
|
import { MaxRectsPackerCompat } from './max-rects-packer-compat.js';
|
|
17
|
+
import type { AtlasRasterBackend, AtlasRasterInput, AtlasRasterResolvedBuffer } from './publish/contracts.js';
|
|
4
18
|
import type { ExtrasMap, HasOptionalSrc, HasOptionalUrl } from './shared-types.js';
|
|
5
19
|
import { createTransform, parseTextureSetMode, type TextureSetMode } from './utils.js';
|
|
6
20
|
|
|
@@ -12,7 +26,7 @@ export interface AtlasOptions {
|
|
|
12
26
|
packages?: string[];
|
|
13
27
|
|
|
14
28
|
/**
|
|
15
|
-
*
|
|
29
|
+
* Raster backend, injected by the host adapter.
|
|
16
30
|
* Required for actual image compositing and trimImage.
|
|
17
31
|
*
|
|
18
32
|
* ```ts
|
|
@@ -20,7 +34,7 @@ export interface AtlasOptions {
|
|
|
20
34
|
* await doc.transform(atlas({ encoder: sharp }));
|
|
21
35
|
* ```
|
|
22
36
|
*/
|
|
23
|
-
encoder?:
|
|
37
|
+
encoder?: AtlasRasterBackend;
|
|
24
38
|
|
|
25
39
|
/** Maximum atlas texture size (width and height). Default: 2048. */
|
|
26
40
|
maxSize?: number;
|
|
@@ -45,7 +59,7 @@ export interface AtlasOptions {
|
|
|
45
59
|
|
|
46
60
|
/**
|
|
47
61
|
* Trim transparent pixels from image edges before packing.
|
|
48
|
-
* Requires
|
|
62
|
+
* Requires a raster backend. Stores offset/originalSize in Sprite nodes.
|
|
49
63
|
* Default: false.
|
|
50
64
|
*/
|
|
51
65
|
trimImage?: boolean;
|
|
@@ -98,10 +112,11 @@ export interface AtlasOptions {
|
|
|
98
112
|
* separate atlas pages/files per branch instead of mixing them with main.
|
|
99
113
|
*/
|
|
100
114
|
separatedAtlasForBranch?: boolean;
|
|
101
|
-
|
|
102
115
|
}
|
|
103
116
|
|
|
104
|
-
const ATLAS_DEFAULTS: Required<
|
|
117
|
+
const ATLAS_DEFAULTS: Required<
|
|
118
|
+
Omit<AtlasOptions, 'packages' | 'encoder' | 'basePath' | 'outputPath' | 'mkdir' | 'readFileRaw'>
|
|
119
|
+
> = {
|
|
105
120
|
maxSize: 2048,
|
|
106
121
|
fast: true,
|
|
107
122
|
allowRotation: true,
|
|
@@ -206,26 +221,6 @@ interface BranchAtlasGroup {
|
|
|
206
221
|
inputs: InputItem[];
|
|
207
222
|
}
|
|
208
223
|
|
|
209
|
-
interface AtlasEncoderMetadata {
|
|
210
|
-
width?: number;
|
|
211
|
-
height?: number;
|
|
212
|
-
channels?: number;
|
|
213
|
-
hasAlpha?: boolean;
|
|
214
|
-
trimOffsetLeft?: number;
|
|
215
|
-
trimOffsetTop?: number;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
interface AtlasEncoderResolvedBuffer {
|
|
219
|
-
data: Uint8Array;
|
|
220
|
-
info: Required<Pick<AtlasEncoderMetadata, 'width' | 'height' | 'channels'>> & AtlasEncoderMetadata;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
interface AtlasCompositeInput {
|
|
224
|
-
input: Uint8Array;
|
|
225
|
-
left: number;
|
|
226
|
-
top: number;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
224
|
function getSelectedSkeletonDependencyImageIds(resources: PackageResource[]): Set<string> {
|
|
230
225
|
const imageIds = new Set<string>();
|
|
231
226
|
const resourcesById = new Map(resources.map((resource) => [resource.getId(), resource] as const));
|
|
@@ -240,35 +235,6 @@ function getSelectedSkeletonDependencyImageIds(resources: PackageResource[]): Se
|
|
|
240
235
|
return imageIds;
|
|
241
236
|
}
|
|
242
237
|
|
|
243
|
-
interface AtlasEncoderPipeline {
|
|
244
|
-
ensureAlpha(): AtlasEncoderPipeline;
|
|
245
|
-
resize(width: number, height: number, options?: { fit?: 'fill' }): AtlasEncoderPipeline;
|
|
246
|
-
raw(): AtlasEncoderPipeline;
|
|
247
|
-
extract(options: { left: number; top: number; width: number; height: number }): AtlasEncoderPipeline;
|
|
248
|
-
toBuffer(options: { resolveWithObject: true }): Promise<AtlasEncoderResolvedBuffer>;
|
|
249
|
-
toBuffer(options?: { resolveWithObject?: false }): Promise<Uint8Array>;
|
|
250
|
-
toBuffer(options?: { resolveWithObject?: boolean }): Promise<Uint8Array | AtlasEncoderResolvedBuffer>;
|
|
251
|
-
png(): AtlasEncoderPipeline;
|
|
252
|
-
metadata(): Promise<AtlasEncoderMetadata>;
|
|
253
|
-
rotate(angle: number): AtlasEncoderPipeline;
|
|
254
|
-
composite(inputs: AtlasCompositeInput[]): AtlasEncoderPipeline;
|
|
255
|
-
toFile(path: string): Promise<unknown>;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
type AtlasEncoderInput =
|
|
259
|
-
| string
|
|
260
|
-
| Uint8Array
|
|
261
|
-
| {
|
|
262
|
-
create: {
|
|
263
|
-
width: number;
|
|
264
|
-
height: number;
|
|
265
|
-
channels: 4;
|
|
266
|
-
background: { r: number; g: number; b: number; alpha: number };
|
|
267
|
-
};
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
type AtlasEncoder = (input: AtlasEncoderInput) => AtlasEncoderPipeline;
|
|
271
|
-
|
|
272
238
|
function resolveFontFileName(fontName: string): string {
|
|
273
239
|
return /\.fnt$/i.test(fontName) ? fontName : `${fontName}.fnt`;
|
|
274
240
|
}
|
|
@@ -303,7 +269,9 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
303
269
|
const imgMatch = line.match(/\bimg=(\w+)/);
|
|
304
270
|
if (imgMatch) await addResource(resourceMap.get(imgMatch[1] ?? ''));
|
|
305
271
|
}
|
|
306
|
-
} catch {
|
|
272
|
+
} catch {
|
|
273
|
+
/* ignore */
|
|
274
|
+
}
|
|
307
275
|
}
|
|
308
276
|
}
|
|
309
277
|
if (isComponentResource(resource)) {
|
|
@@ -392,7 +360,9 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
392
360
|
]) {
|
|
393
361
|
await addResourceByLocalUiUrl(ref);
|
|
394
362
|
}
|
|
395
|
-
for (const transition of (
|
|
363
|
+
for (const transition of (
|
|
364
|
+
component as Component & { listTransitions?(): TransitionWithAtlasRefs[] }
|
|
365
|
+
).listTransitions?.() ?? []) {
|
|
396
366
|
for (const item of transition.listItems?.() ?? []) {
|
|
397
367
|
const actionType = item.getActionType?.();
|
|
398
368
|
if (actionType !== TransitionActionType.Sound && actionType !== TransitionActionType.Icon) continue;
|
|
@@ -421,7 +391,7 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
421
391
|
*
|
|
422
392
|
* This transform performs MaxRects bin-packing on all ImageResource items
|
|
423
393
|
* within each package, creating Atlas and Sprite property nodes. When an
|
|
424
|
-
*
|
|
394
|
+
* a raster backend is provided, it also composites the actual PNG files.
|
|
425
395
|
*
|
|
426
396
|
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
427
397
|
* at image edges are trimmed before packing. The trimmed offset and original
|
|
@@ -444,17 +414,20 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
444
414
|
return createTransform('atlas', async (doc: Document): Promise<void> => {
|
|
445
415
|
const root = doc.getRoot();
|
|
446
416
|
const logger = doc.getLogger();
|
|
447
|
-
const encoder = options.encoder
|
|
417
|
+
const encoder = options.encoder;
|
|
448
418
|
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
449
419
|
const packageFilter = options.packages ? new Set(options.packages) : null;
|
|
450
420
|
|
|
451
421
|
for (const pkg of root.listPackages()) {
|
|
452
422
|
if (packageFilter && !packageFilter.has(pkg.getName())) continue;
|
|
453
423
|
// Respect publish-selected resources when publish() precomputes a merged branch view.
|
|
454
|
-
const selectedPublishIds = new Set(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
424
|
+
const selectedPublishIds = new Set(
|
|
425
|
+
((pkg.getExtras() as PackageAtlasExtras | undefined) ?? {}).publishedResourceIds ?? [],
|
|
426
|
+
);
|
|
427
|
+
const allResources =
|
|
428
|
+
selectedPublishIds.size > 0
|
|
429
|
+
? pkg.listResources().filter((resource) => selectedPublishIds.has(resource.getId()))
|
|
430
|
+
: pkg.listResources();
|
|
458
431
|
const skeletonDependencyImageIds = getSelectedSkeletonDependencyImageIds(allResources);
|
|
459
432
|
// Process resources in declaration order (matching editor behavior)
|
|
460
433
|
const orderedResources = await resolveEditorCompatibleResourceOrder(pkg, allResources, options);
|
|
@@ -517,7 +490,9 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
517
490
|
addUiResourceRefsFromUnknown(referencedIds, gear.getDefaultValue?.());
|
|
518
491
|
}
|
|
519
492
|
}
|
|
520
|
-
for (const transition of (
|
|
493
|
+
for (const transition of (
|
|
494
|
+
component as Component & { listTransitions?(): TransitionWithAtlasRefs[] }
|
|
495
|
+
).listTransitions?.() ?? []) {
|
|
521
496
|
for (const item of transition.listItems?.() ?? []) {
|
|
522
497
|
addUiResourceRefsFromUnknown(referencedIds, item.getStartValue?.());
|
|
523
498
|
addUiResourceRefsFromUnknown(referencedIds, item.getEndValue?.());
|
|
@@ -549,7 +524,9 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
549
524
|
const match = line.match(/img=(\w+)/);
|
|
550
525
|
if (match) referencedIds.add(match[1]);
|
|
551
526
|
}
|
|
552
|
-
} catch {
|
|
527
|
+
} catch {
|
|
528
|
+
/* .fnt file not found — OK */
|
|
529
|
+
}
|
|
553
530
|
}
|
|
554
531
|
}
|
|
555
532
|
}
|
|
@@ -559,15 +536,33 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
559
536
|
// Pack referenced images, plus explicitly exported standalone images.
|
|
560
537
|
const resId = res.getId();
|
|
561
538
|
if (skeletonDependencyImageIds.has(resId)) continue;
|
|
562
|
-
if (
|
|
539
|
+
if (
|
|
540
|
+
selectedPublishIds.size === 0 &&
|
|
541
|
+
!res.getExported() &&
|
|
542
|
+
referencedIds.size > 0 &&
|
|
543
|
+
!referencedIds.has(resId)
|
|
544
|
+
)
|
|
545
|
+
continue;
|
|
563
546
|
await _collectImage(res, pkg, inputs, encoder, options, doTrim, logger);
|
|
564
547
|
} else if (isMovieClipResource(res)) {
|
|
565
548
|
const resId = res.getId();
|
|
566
|
-
if (
|
|
549
|
+
if (
|
|
550
|
+
selectedPublishIds.size === 0 &&
|
|
551
|
+
!res.getExported() &&
|
|
552
|
+
referencedIds.size > 0 &&
|
|
553
|
+
!referencedIds.has(resId)
|
|
554
|
+
)
|
|
555
|
+
continue;
|
|
567
556
|
await _collectMovieClipFrames(doc, res, pkg, inputs, encoder, options, logger);
|
|
568
557
|
} else if (isFontResource(res)) {
|
|
569
558
|
const resId = res.getId();
|
|
570
|
-
if (
|
|
559
|
+
if (
|
|
560
|
+
selectedPublishIds.size === 0 &&
|
|
561
|
+
!res.getExported() &&
|
|
562
|
+
referencedIds.size > 0 &&
|
|
563
|
+
!referencedIds.has(resId)
|
|
564
|
+
)
|
|
565
|
+
continue;
|
|
571
566
|
await _collectFontTexture(doc, res, pkg, options);
|
|
572
567
|
}
|
|
573
568
|
}
|
|
@@ -575,16 +570,30 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
575
570
|
if (inputs.length === 0) continue;
|
|
576
571
|
let totalPageCount = 0;
|
|
577
572
|
let usedDirectOutput = false;
|
|
578
|
-
const { autoInputs, fixedPageGroups, standaloneGroups, reservedPageIndexes } = groupStandaloneInputs(
|
|
573
|
+
const { autoInputs, fixedPageGroups, standaloneGroups, reservedPageIndexes } = groupStandaloneInputs(
|
|
574
|
+
doc,
|
|
575
|
+
inputs,
|
|
576
|
+
options,
|
|
577
|
+
);
|
|
579
578
|
const branchGroups = buildBranchAtlasGroups(doc, autoInputs, options);
|
|
580
579
|
const branchPageOffsets = new Map<number, number>();
|
|
581
580
|
|
|
582
581
|
for (const group of branchGroups) {
|
|
583
|
-
const directOutput =
|
|
584
|
-
|
|
585
|
-
|
|
582
|
+
const directOutput =
|
|
583
|
+
fixedPageGroups.length === 0 && standaloneGroups.length === 0
|
|
584
|
+
? resolveDirectImageOutput(group.inputs, options)
|
|
585
|
+
: null;
|
|
586
586
|
if (directOutput) {
|
|
587
|
-
await emitDirectImageOutput(
|
|
587
|
+
await emitDirectImageOutput(
|
|
588
|
+
doc,
|
|
589
|
+
pkg,
|
|
590
|
+
directOutput,
|
|
591
|
+
encoder,
|
|
592
|
+
options,
|
|
593
|
+
logger,
|
|
594
|
+
group.branchName,
|
|
595
|
+
group.branchOrdinal,
|
|
596
|
+
);
|
|
588
597
|
usedDirectOutput = true;
|
|
589
598
|
totalPageCount += 1;
|
|
590
599
|
continue;
|
|
@@ -632,13 +641,18 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
632
641
|
logger,
|
|
633
642
|
});
|
|
634
643
|
totalPageCount += emittedPageCount;
|
|
635
|
-
standalonePageOffsets.set(
|
|
644
|
+
standalonePageOffsets.set(
|
|
645
|
+
group.branchOrdinal,
|
|
646
|
+
(standalonePageOffsets.get(group.branchOrdinal) ?? 0) + emittedPageCount,
|
|
647
|
+
);
|
|
636
648
|
}
|
|
637
649
|
|
|
638
650
|
if (usedDirectOutput) {
|
|
639
651
|
logger.info(`atlas: Direct output for single image package "${pkg.getName()}".`);
|
|
640
652
|
}
|
|
641
|
-
logger.info(
|
|
653
|
+
logger.info(
|
|
654
|
+
`atlas: Packed ${inputs.length} images into ${totalPageCount} atlas(es) for package "${pkg.getName()}".`,
|
|
655
|
+
);
|
|
642
656
|
}
|
|
643
657
|
});
|
|
644
658
|
}
|
|
@@ -648,14 +662,17 @@ function buildBranchAtlasGroups(doc: Document, inputs: InputItem[], options: Atl
|
|
|
648
662
|
return [{ branchName: '', branchOrdinal: 0, inputs }];
|
|
649
663
|
}
|
|
650
664
|
|
|
651
|
-
const discoveredBranchNames = [
|
|
652
|
-
.map((input) => getInputBranchName(input))
|
|
653
|
-
|
|
665
|
+
const discoveredBranchNames = [
|
|
666
|
+
...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName)),
|
|
667
|
+
];
|
|
654
668
|
if (discoveredBranchNames.length === 0) {
|
|
655
669
|
return [{ branchName: '', branchOrdinal: 0, inputs }];
|
|
656
670
|
}
|
|
657
671
|
|
|
658
|
-
const orderedBranchNames = doc
|
|
672
|
+
const orderedBranchNames = doc
|
|
673
|
+
.getRoot()
|
|
674
|
+
.listBranches()
|
|
675
|
+
.filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
659
676
|
for (const branchName of discoveredBranchNames) {
|
|
660
677
|
if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
661
678
|
}
|
|
@@ -709,7 +726,7 @@ async function emitPagedAtlasGroup(
|
|
|
709
726
|
pageStart: number;
|
|
710
727
|
fileNameAt: (pageIndex: number) => string;
|
|
711
728
|
options: AtlasOptions;
|
|
712
|
-
encoder:
|
|
729
|
+
encoder: AtlasRasterBackend | undefined;
|
|
713
730
|
logger: ILogger;
|
|
714
731
|
forceSinglePage?: boolean;
|
|
715
732
|
},
|
|
@@ -729,7 +746,15 @@ async function emitPagedAtlasGroup(
|
|
|
729
746
|
pkg.addAtlas(atlasNode);
|
|
730
747
|
|
|
731
748
|
attachSpritesToAtlas(doc, allResources, inputs, page.outputRects, atlasNode);
|
|
732
|
-
await writeAtlasPageImage(
|
|
749
|
+
await writeAtlasPageImage(
|
|
750
|
+
pkg,
|
|
751
|
+
inputs,
|
|
752
|
+
page,
|
|
753
|
+
atlasNode.getFile(),
|
|
754
|
+
context.encoder,
|
|
755
|
+
context.options,
|
|
756
|
+
context.logger,
|
|
757
|
+
);
|
|
733
758
|
}
|
|
734
759
|
|
|
735
760
|
return pages.length;
|
|
@@ -742,7 +767,7 @@ async function emitStandaloneAtlasGroup(
|
|
|
742
767
|
context: {
|
|
743
768
|
atlasIndexStart: number;
|
|
744
769
|
options: AtlasOptions;
|
|
745
|
-
encoder:
|
|
770
|
+
encoder: AtlasRasterBackend | undefined;
|
|
746
771
|
logger: ILogger;
|
|
747
772
|
},
|
|
748
773
|
): Promise<number> {
|
|
@@ -874,9 +899,13 @@ function attachSpritesToAtlas(
|
|
|
874
899
|
async function writeAtlasPageImage(
|
|
875
900
|
pkg: Package,
|
|
876
901
|
inputs: InputItem[],
|
|
877
|
-
page: {
|
|
902
|
+
page: {
|
|
903
|
+
width: number;
|
|
904
|
+
height: number;
|
|
905
|
+
outputRects: Array<{ index: number; x: number; y: number; width: number; height: number; rotated: boolean }>;
|
|
906
|
+
},
|
|
878
907
|
atlasFileName: string,
|
|
879
|
-
encoder:
|
|
908
|
+
encoder: AtlasRasterBackend | undefined,
|
|
880
909
|
options: AtlasOptions,
|
|
881
910
|
logger: ILogger,
|
|
882
911
|
): Promise<void> {
|
|
@@ -948,7 +977,12 @@ function inputToCompatRect(input: InputItem, index: number): CompatNodeRect {
|
|
|
948
977
|
};
|
|
949
978
|
}
|
|
950
979
|
|
|
951
|
-
function resolvePackedRectSize(
|
|
980
|
+
function resolvePackedRectSize(
|
|
981
|
+
input: InputItem,
|
|
982
|
+
width: number,
|
|
983
|
+
height: number,
|
|
984
|
+
rectRotated: boolean,
|
|
985
|
+
): { width: number; height: number } {
|
|
952
986
|
if (!rectRotated) return { width, height };
|
|
953
987
|
return {
|
|
954
988
|
width: input.height,
|
|
@@ -968,7 +1002,11 @@ function resolveDirectImageOutput(inputs: InputItem[], options: AtlasOptions): I
|
|
|
968
1002
|
return input;
|
|
969
1003
|
}
|
|
970
1004
|
|
|
971
|
-
function resolveDirectOutputAtlasSize(
|
|
1005
|
+
function resolveDirectOutputAtlasSize(
|
|
1006
|
+
width: number,
|
|
1007
|
+
height: number,
|
|
1008
|
+
options: AtlasOptions,
|
|
1009
|
+
): { width: number; height: number } {
|
|
972
1010
|
let resolvedWidth = width;
|
|
973
1011
|
let resolvedHeight = height;
|
|
974
1012
|
if (options.square) {
|
|
@@ -987,7 +1025,7 @@ async function emitDirectImageOutput(
|
|
|
987
1025
|
doc: Document,
|
|
988
1026
|
pkg: Package,
|
|
989
1027
|
input: InputItem,
|
|
990
|
-
encoder:
|
|
1028
|
+
encoder: AtlasRasterBackend | undefined,
|
|
991
1029
|
options: AtlasOptions,
|
|
992
1030
|
logger: ILogger,
|
|
993
1031
|
branchName: string = '',
|
|
@@ -1060,11 +1098,7 @@ function resolveAtlasOutputFileName(pkg: Package, pageIndex: number, branchName:
|
|
|
1060
1098
|
return `${pkg.getPublishName() || pkg.getName()}_atlas${pageIndex}${suffix}.png`;
|
|
1061
1099
|
}
|
|
1062
1100
|
|
|
1063
|
-
function resolveStandaloneAtlasOutputFileName(
|
|
1064
|
-
pkg: Package,
|
|
1065
|
-
resource: PackInputResource,
|
|
1066
|
-
branchName: string,
|
|
1067
|
-
): string {
|
|
1101
|
+
function resolveStandaloneAtlasOutputFileName(pkg: Package, resource: PackInputResource, branchName: string): string {
|
|
1068
1102
|
const baseName = `${pkg.getPublishName() || pkg.getName()}_atlas_${getPublishedItemId(resource)}`;
|
|
1069
1103
|
const suffix = branchName ? `_${branchName}` : '';
|
|
1070
1104
|
if (isImageResource(resource)) {
|
|
@@ -1130,11 +1164,23 @@ function sortResourcesByOrder(
|
|
|
1130
1164
|
ordered.sort((left, right) => {
|
|
1131
1165
|
const leftId = left.getId();
|
|
1132
1166
|
const rightId = right.getId();
|
|
1133
|
-
const leftOrder =
|
|
1134
|
-
|
|
1167
|
+
const leftOrder =
|
|
1168
|
+
leftId && orderMap.has(leftId)
|
|
1169
|
+
? (orderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER)
|
|
1170
|
+
: Number.MAX_SAFE_INTEGER;
|
|
1171
|
+
const rightOrder =
|
|
1172
|
+
rightId && orderMap.has(rightId)
|
|
1173
|
+
? (orderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER)
|
|
1174
|
+
: Number.MAX_SAFE_INTEGER;
|
|
1135
1175
|
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
1136
|
-
const leftInputOrder =
|
|
1137
|
-
|
|
1176
|
+
const leftInputOrder =
|
|
1177
|
+
leftId && inputOrderMap.has(leftId)
|
|
1178
|
+
? (inputOrderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER)
|
|
1179
|
+
: Number.MAX_SAFE_INTEGER;
|
|
1180
|
+
const rightInputOrder =
|
|
1181
|
+
rightId && inputOrderMap.has(rightId)
|
|
1182
|
+
? (inputOrderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER)
|
|
1183
|
+
: Number.MAX_SAFE_INTEGER;
|
|
1138
1184
|
if (leftInputOrder !== rightInputOrder) return leftInputOrder - rightInputOrder;
|
|
1139
1185
|
return (leftId ?? '').localeCompare(rightId ?? '');
|
|
1140
1186
|
});
|
|
@@ -1185,10 +1231,13 @@ function groupStandaloneInputs(
|
|
|
1185
1231
|
const fixedInputsByPage = new Map<string, PagedAtlasGroup>();
|
|
1186
1232
|
const standaloneGroups = new Map<string, StandaloneAtlasGroup>();
|
|
1187
1233
|
const reservedPageIndexes = new Set<number>();
|
|
1188
|
-
const discoveredBranchNames = [
|
|
1189
|
-
.map((input) => getInputBranchName(input))
|
|
1190
|
-
|
|
1191
|
-
const orderedBranchNames = doc
|
|
1234
|
+
const discoveredBranchNames = [
|
|
1235
|
+
...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName)),
|
|
1236
|
+
];
|
|
1237
|
+
const orderedBranchNames = doc
|
|
1238
|
+
.getRoot()
|
|
1239
|
+
.listBranches()
|
|
1240
|
+
.filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
1192
1241
|
for (const branchName of discoveredBranchNames) {
|
|
1193
1242
|
if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
1194
1243
|
}
|
|
@@ -1247,34 +1296,31 @@ function groupStandaloneInputs(
|
|
|
1247
1296
|
|
|
1248
1297
|
return {
|
|
1249
1298
|
autoInputs,
|
|
1250
|
-
fixedPageGroups: [...fixedInputsByPage.values()].sort(
|
|
1251
|
-
left.branchOrdinal - right.branchOrdinal
|
|
1252
|
-
|| left.pageIndex - right.pageIndex,
|
|
1299
|
+
fixedPageGroups: [...fixedInputsByPage.values()].sort(
|
|
1300
|
+
(left, right) => left.branchOrdinal - right.branchOrdinal || left.pageIndex - right.pageIndex,
|
|
1253
1301
|
),
|
|
1254
|
-
standaloneGroups: [...standaloneGroups.values()].sort(
|
|
1255
|
-
left
|
|
1256
|
-
|
|
1302
|
+
standaloneGroups: [...standaloneGroups.values()].sort(
|
|
1303
|
+
(left, right) =>
|
|
1304
|
+
left.branchOrdinal - right.branchOrdinal ||
|
|
1305
|
+
getPublishedItemId(left.resource).localeCompare(getPublishedItemId(right.resource)),
|
|
1257
1306
|
),
|
|
1258
1307
|
reservedPageIndexes,
|
|
1259
1308
|
};
|
|
1260
1309
|
}
|
|
1261
1310
|
|
|
1262
1311
|
/**
|
|
1263
|
-
* Trim transparent edges from an image using
|
|
1312
|
+
* Trim transparent edges from an image using the host raster backend.
|
|
1264
1313
|
* Returns the trimmed buffer, dimensions, and offsets.
|
|
1265
1314
|
* Falls back to the original image if trim fails (e.g. no alpha channel, no transparent edges).
|
|
1266
1315
|
*/
|
|
1267
1316
|
async function _trimImage(
|
|
1268
|
-
encoder:
|
|
1269
|
-
input:
|
|
1317
|
+
encoder: AtlasRasterBackend,
|
|
1318
|
+
input: AtlasRasterInput,
|
|
1270
1319
|
originalWidth: number,
|
|
1271
1320
|
originalHeight: number,
|
|
1272
1321
|
): Promise<TrimInfo> {
|
|
1273
1322
|
try {
|
|
1274
|
-
const trimResult = await encoder(input)
|
|
1275
|
-
.ensureAlpha()
|
|
1276
|
-
.raw()
|
|
1277
|
-
.toBuffer({ resolveWithObject: true });
|
|
1323
|
+
const trimResult = await encoder(input).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
1278
1324
|
if (!isResolvedBuffer(trimResult)) {
|
|
1279
1325
|
throw new Error('atlas: encoder raw alpha trim did not return resolved metadata.');
|
|
1280
1326
|
}
|
|
@@ -1362,10 +1408,16 @@ function _resolveImagePath(resource: ImageResource, pkg: Package, basePath: stri
|
|
|
1362
1408
|
}
|
|
1363
1409
|
|
|
1364
1410
|
type InputItem = {
|
|
1365
|
-
id: string;
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1411
|
+
id: string;
|
|
1412
|
+
width: number;
|
|
1413
|
+
height: number;
|
|
1414
|
+
originalWidth: number;
|
|
1415
|
+
originalHeight: number;
|
|
1416
|
+
offsetX: number;
|
|
1417
|
+
offsetY: number;
|
|
1418
|
+
resource: PackInputResource;
|
|
1419
|
+
trimBuffer?: Uint8Array;
|
|
1420
|
+
rasterizedBuffer?: Uint8Array;
|
|
1369
1421
|
sourceKind: 'image' | 'movieclip-frame';
|
|
1370
1422
|
};
|
|
1371
1423
|
|
|
@@ -1389,7 +1441,7 @@ async function _collectImage(
|
|
|
1389
1441
|
resource: ImageResource,
|
|
1390
1442
|
pkg: Package,
|
|
1391
1443
|
inputs: InputItem[],
|
|
1392
|
-
encoder:
|
|
1444
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1393
1445
|
options: AtlasOptions,
|
|
1394
1446
|
doTrim: boolean,
|
|
1395
1447
|
logger: ILogger,
|
|
@@ -1414,7 +1466,7 @@ async function _collectImage(
|
|
|
1414
1466
|
sourceHasAlpha = metadata.hasAlpha === true || metadata.channels === 4;
|
|
1415
1467
|
if (/\.svg$/i.test(resolveImageFileName(resource)) && declaredWidth > 0 && declaredHeight > 0) {
|
|
1416
1468
|
rasterizedBuffer = await encoder(filePath)
|
|
1417
|
-
.resize(declaredWidth, declaredHeight,
|
|
1469
|
+
.resize({ width: declaredWidth, height: declaredHeight, fit: 'fill' })
|
|
1418
1470
|
.png()
|
|
1419
1471
|
.toBuffer();
|
|
1420
1472
|
sourceHasAlpha = true;
|
|
@@ -1429,7 +1481,10 @@ async function _collectImage(
|
|
|
1429
1481
|
|
|
1430
1482
|
if (origW <= 0 || origH <= 0) return;
|
|
1431
1483
|
|
|
1432
|
-
let packW = origW,
|
|
1484
|
+
let packW = origW,
|
|
1485
|
+
packH = origH,
|
|
1486
|
+
offX = 0,
|
|
1487
|
+
offY = 0;
|
|
1433
1488
|
let trimBuf: Uint8Array | undefined;
|
|
1434
1489
|
|
|
1435
1490
|
if (doTrim && sourceHasAlpha && options.basePath && encoder) {
|
|
@@ -1447,9 +1502,13 @@ async function _collectImage(
|
|
|
1447
1502
|
}
|
|
1448
1503
|
|
|
1449
1504
|
inputs.push({
|
|
1450
|
-
id: getPublishedItemId(resource),
|
|
1451
|
-
|
|
1452
|
-
|
|
1505
|
+
id: getPublishedItemId(resource),
|
|
1506
|
+
width: packW,
|
|
1507
|
+
height: packH,
|
|
1508
|
+
originalWidth: origW,
|
|
1509
|
+
originalHeight: origH,
|
|
1510
|
+
offsetX: offX,
|
|
1511
|
+
offsetY: offY,
|
|
1453
1512
|
resource,
|
|
1454
1513
|
trimBuffer: trimBuf,
|
|
1455
1514
|
rasterizedBuffer,
|
|
@@ -1463,7 +1522,7 @@ async function _collectMovieClipFrames(
|
|
|
1463
1522
|
resource: MovieClipResource,
|
|
1464
1523
|
pkg: Package,
|
|
1465
1524
|
inputs: InputItem[],
|
|
1466
|
-
encoder:
|
|
1525
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1467
1526
|
options: AtlasOptions,
|
|
1468
1527
|
logger: ILogger,
|
|
1469
1528
|
): Promise<void> {
|
|
@@ -1553,7 +1612,7 @@ async function _createMovieClipFrameInput(
|
|
|
1553
1612
|
buffer: Uint8Array,
|
|
1554
1613
|
itemId: string,
|
|
1555
1614
|
resource: MovieClipResource,
|
|
1556
|
-
encoder:
|
|
1615
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1557
1616
|
): Promise<InputItem | null> {
|
|
1558
1617
|
if (!encoder || buffer.length === 0) return null;
|
|
1559
1618
|
try {
|
|
@@ -1626,10 +1685,7 @@ function _findPngEnd(data: Uint8Array, start: number): number {
|
|
|
1626
1685
|
pos += 8;
|
|
1627
1686
|
if (pos + length + 4 > data.length) return -1;
|
|
1628
1687
|
const isIEND =
|
|
1629
|
-
data[pos - 4] === 0x49 &&
|
|
1630
|
-
data[pos - 3] === 0x45 &&
|
|
1631
|
-
data[pos - 2] === 0x4e &&
|
|
1632
|
-
data[pos - 1] === 0x44;
|
|
1688
|
+
data[pos - 4] === 0x49 && data[pos - 3] === 0x45 && data[pos - 2] === 0x4e && data[pos - 1] === 0x44;
|
|
1633
1689
|
pos += length + 4;
|
|
1634
1690
|
if (isIEND) return pos;
|
|
1635
1691
|
}
|
|
@@ -1760,7 +1816,7 @@ function _readUint16BE(data: Uint8Array, offset: number): number {
|
|
|
1760
1816
|
function _readUint32BE(data: Uint8Array, offset: number): number {
|
|
1761
1817
|
if (offset + 3 >= data.length) return 0;
|
|
1762
1818
|
return (
|
|
1763
|
-
|
|
1819
|
+
data[offset] * 0x1000000 +
|
|
1764
1820
|
((data[offset + 1] ?? 0) << 16) +
|
|
1765
1821
|
((data[offset + 2] ?? 0) << 8) +
|
|
1766
1822
|
(data[offset + 3] ?? 0)
|
|
@@ -1823,27 +1879,53 @@ async function _collectFontTexture(
|
|
|
1823
1879
|
.setChannel(item.channel);
|
|
1824
1880
|
fontRes.addGlyph(glyph);
|
|
1825
1881
|
}
|
|
1826
|
-
} catch {
|
|
1882
|
+
} catch {
|
|
1883
|
+
/* .fnt not found */
|
|
1884
|
+
}
|
|
1827
1885
|
}
|
|
1828
1886
|
}
|
|
1829
1887
|
|
|
1830
1888
|
/** Parse a BMFont .fnt text file into structured data for binary encoding. */
|
|
1831
1889
|
function _parseFnt(text: string): {
|
|
1832
|
-
hasFace: boolean;
|
|
1833
|
-
|
|
1890
|
+
hasFace: boolean;
|
|
1891
|
+
colored: boolean;
|
|
1892
|
+
resizable: boolean;
|
|
1893
|
+
hasChannel: boolean;
|
|
1894
|
+
fontSize: number;
|
|
1895
|
+
xadvance: number;
|
|
1896
|
+
lineHeight: number;
|
|
1834
1897
|
glyphs: Array<{
|
|
1835
|
-
charId: number;
|
|
1836
|
-
|
|
1837
|
-
|
|
1898
|
+
charId: number;
|
|
1899
|
+
img: string | null;
|
|
1900
|
+
x: number;
|
|
1901
|
+
y: number;
|
|
1902
|
+
xoffset: number;
|
|
1903
|
+
yoffset: number;
|
|
1904
|
+
width: number;
|
|
1905
|
+
height: number;
|
|
1906
|
+
xadvance: number;
|
|
1907
|
+
channel: number;
|
|
1838
1908
|
}>;
|
|
1839
1909
|
} {
|
|
1840
1910
|
const lines = text.split(/\r?\n/);
|
|
1841
|
-
let hasFace = false,
|
|
1842
|
-
|
|
1911
|
+
let hasFace = false,
|
|
1912
|
+
colored = false,
|
|
1913
|
+
resizable = false,
|
|
1914
|
+
hasChannel = false;
|
|
1915
|
+
let fontSize = 0,
|
|
1916
|
+
globalXadvance = 0,
|
|
1917
|
+
lineHeight = 0;
|
|
1843
1918
|
const glyphs: Array<{
|
|
1844
|
-
charId: number;
|
|
1845
|
-
|
|
1846
|
-
|
|
1919
|
+
charId: number;
|
|
1920
|
+
img: string | null;
|
|
1921
|
+
x: number;
|
|
1922
|
+
y: number;
|
|
1923
|
+
xoffset: number;
|
|
1924
|
+
yoffset: number;
|
|
1925
|
+
width: number;
|
|
1926
|
+
height: number;
|
|
1927
|
+
xadvance: number;
|
|
1928
|
+
channel: number;
|
|
1847
1929
|
}> = [];
|
|
1848
1930
|
|
|
1849
1931
|
for (const line of lines) {
|
|
@@ -1878,7 +1960,8 @@ function _parseFnt(text: string): {
|
|
|
1878
1960
|
const chnl = parseInt(attrs.chnl, 10) || 0;
|
|
1879
1961
|
if (chnl !== 0 && chnl !== 15) hasChannel = true;
|
|
1880
1962
|
glyphs.push({
|
|
1881
|
-
charId,
|
|
1963
|
+
charId,
|
|
1964
|
+
img,
|
|
1882
1965
|
x: parseInt(attrs.x, 10) || 0,
|
|
1883
1966
|
y: parseInt(attrs.y, 10) || 0,
|
|
1884
1967
|
xoffset: parseInt(attrs.xoffset, 10) || 0,
|
|
@@ -1893,7 +1976,16 @@ function _parseFnt(text: string): {
|
|
|
1893
1976
|
}
|
|
1894
1977
|
}
|
|
1895
1978
|
|
|
1896
|
-
return {
|
|
1979
|
+
return {
|
|
1980
|
+
hasFace,
|
|
1981
|
+
colored,
|
|
1982
|
+
resizable: fontSize > 0 ? resizable : false,
|
|
1983
|
+
hasChannel,
|
|
1984
|
+
fontSize,
|
|
1985
|
+
xadvance: globalXadvance,
|
|
1986
|
+
lineHeight,
|
|
1987
|
+
glyphs,
|
|
1988
|
+
};
|
|
1897
1989
|
}
|
|
1898
1990
|
|
|
1899
1991
|
function isComponentResource(resource: PackageResource): resource is Component {
|
|
@@ -1946,6 +2038,6 @@ function addUiResourceRefsFromUnknown(target: Set<string>, value: unknown): void
|
|
|
1946
2038
|
}
|
|
1947
2039
|
}
|
|
1948
2040
|
|
|
1949
|
-
function isResolvedBuffer(value: Uint8Array |
|
|
2041
|
+
function isResolvedBuffer(value: Uint8Array | AtlasRasterResolvedBuffer): value is AtlasRasterResolvedBuffer {
|
|
1950
2042
|
return typeof value === 'object' && value !== null && 'data' in value && 'info' in value;
|
|
1951
2043
|
}
|