@openfairygui/functions 0.2.0-alpha.1 → 0.2.0-alpha.12
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 +32 -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 +98 -2896
- package/dist/index.d.cts +5 -274
- package/dist/index.d.ts +5 -274
- package/dist/index.js +86 -2888
- 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/uam-transaction.cjs +79 -0
- package/dist/uam-transaction.d.cts +42 -0
- package/dist/uam-transaction.d.ts +42 -0
- package/dist/uam-transaction.js +78 -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 +86 -53
- package/src/adapters/node/plugins.ts +82 -0
- package/src/adapters/node/publish.ts +129 -0
- package/src/adapters/web/publish.ts +404 -0
- package/src/atlas.ts +702 -254
- package/src/codegen.ts +106 -67
- package/src/index.ts +43 -3
- package/src/node.ts +4 -0
- package/src/plugins/types.ts +56 -0
- package/src/publish/contracts.ts +80 -0
- package/src/publish.ts +593 -170
- package/src/restore.ts +12 -5
- package/src/shared-types.ts +4 -8
- package/src/uam-transaction.ts +69 -1
- package/src/utils.ts +28 -0
- package/src/web.ts +11 -0
package/src/atlas.ts
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
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
|
-
import { createTransform } from './utils.js';
|
|
19
|
+
import { createTransform, parseTextureSetMode, type TextureSetMode } from './utils.js';
|
|
6
20
|
|
|
7
21
|
export interface AtlasOptions {
|
|
8
22
|
/**
|
|
9
|
-
*
|
|
23
|
+
* Limit atlas generation to specific package names.
|
|
24
|
+
* When omitted, all packages are processed.
|
|
25
|
+
*/
|
|
26
|
+
packages?: string[];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Raster backend, injected by the host adapter.
|
|
10
30
|
* Required for actual image compositing and trimImage.
|
|
11
31
|
*
|
|
12
32
|
* ```ts
|
|
@@ -14,7 +34,7 @@ export interface AtlasOptions {
|
|
|
14
34
|
* await doc.transform(atlas({ encoder: sharp }));
|
|
15
35
|
* ```
|
|
16
36
|
*/
|
|
17
|
-
encoder?:
|
|
37
|
+
encoder?: AtlasRasterBackend;
|
|
18
38
|
|
|
19
39
|
/** Maximum atlas texture size (width and height). Default: 2048. */
|
|
20
40
|
maxSize?: number;
|
|
@@ -39,7 +59,7 @@ export interface AtlasOptions {
|
|
|
39
59
|
|
|
40
60
|
/**
|
|
41
61
|
* Trim transparent pixels from image edges before packing.
|
|
42
|
-
* Requires
|
|
62
|
+
* Requires a raster backend. Stores offset/originalSize in Sprite nodes.
|
|
43
63
|
* Default: false.
|
|
44
64
|
*/
|
|
45
65
|
trimImage?: boolean;
|
|
@@ -92,10 +112,11 @@ export interface AtlasOptions {
|
|
|
92
112
|
* separate atlas pages/files per branch instead of mixing them with main.
|
|
93
113
|
*/
|
|
94
114
|
separatedAtlasForBranch?: boolean;
|
|
95
|
-
|
|
96
115
|
}
|
|
97
116
|
|
|
98
|
-
const ATLAS_DEFAULTS: Required<
|
|
117
|
+
const ATLAS_DEFAULTS: Required<
|
|
118
|
+
Omit<AtlasOptions, 'packages' | 'encoder' | 'basePath' | 'outputPath' | 'mkdir' | 'readFileRaw'>
|
|
119
|
+
> = {
|
|
99
120
|
maxSize: 2048,
|
|
100
121
|
fast: true,
|
|
101
122
|
allowRotation: true,
|
|
@@ -200,54 +221,20 @@ interface BranchAtlasGroup {
|
|
|
200
221
|
inputs: InputItem[];
|
|
201
222
|
}
|
|
202
223
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
interface AtlasCompositeInput {
|
|
218
|
-
input: Uint8Array;
|
|
219
|
-
left: number;
|
|
220
|
-
top: number;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
interface AtlasEncoderPipeline {
|
|
224
|
-
ensureAlpha(): AtlasEncoderPipeline;
|
|
225
|
-
raw(): AtlasEncoderPipeline;
|
|
226
|
-
extract(options: { left: number; top: number; width: number; height: number }): AtlasEncoderPipeline;
|
|
227
|
-
toBuffer(options: { resolveWithObject: true }): Promise<AtlasEncoderResolvedBuffer>;
|
|
228
|
-
toBuffer(options?: { resolveWithObject?: false }): Promise<Uint8Array>;
|
|
229
|
-
toBuffer(options?: { resolveWithObject?: boolean }): Promise<Uint8Array | AtlasEncoderResolvedBuffer>;
|
|
230
|
-
png(): AtlasEncoderPipeline;
|
|
231
|
-
metadata(): Promise<AtlasEncoderMetadata>;
|
|
232
|
-
rotate(angle: number): AtlasEncoderPipeline;
|
|
233
|
-
composite(inputs: AtlasCompositeInput[]): AtlasEncoderPipeline;
|
|
234
|
-
toFile(path: string): Promise<unknown>;
|
|
224
|
+
function getSelectedSkeletonDependencyImageIds(resources: PackageResource[]): Set<string> {
|
|
225
|
+
const imageIds = new Set<string>();
|
|
226
|
+
const resourcesById = new Map(resources.map((resource) => [resource.getId(), resource] as const));
|
|
227
|
+
for (const resource of resources) {
|
|
228
|
+
if (!isSkeletonResource(resource)) continue;
|
|
229
|
+
for (const requiredId of resource.getRequireIds()) {
|
|
230
|
+
if (!requiredId) continue;
|
|
231
|
+
const required = resourcesById.get(requiredId);
|
|
232
|
+
if (required && isImageResource(required)) imageIds.add(requiredId);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return imageIds;
|
|
235
236
|
}
|
|
236
237
|
|
|
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
|
-
|
|
249
|
-
type AtlasEncoder = (input: AtlasEncoderInput) => AtlasEncoderPipeline;
|
|
250
|
-
|
|
251
238
|
function resolveFontFileName(fontName: string): string {
|
|
252
239
|
return /\.fnt$/i.test(fontName) ? fontName : `${fontName}.fnt`;
|
|
253
240
|
}
|
|
@@ -282,7 +269,9 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
282
269
|
const imgMatch = line.match(/\bimg=(\w+)/);
|
|
283
270
|
if (imgMatch) await addResource(resourceMap.get(imgMatch[1] ?? ''));
|
|
284
271
|
}
|
|
285
|
-
} catch {
|
|
272
|
+
} catch {
|
|
273
|
+
/* ignore */
|
|
274
|
+
}
|
|
286
275
|
}
|
|
287
276
|
}
|
|
288
277
|
if (isComponentResource(resource)) {
|
|
@@ -371,7 +360,9 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
371
360
|
]) {
|
|
372
361
|
await addResourceByLocalUiUrl(ref);
|
|
373
362
|
}
|
|
374
|
-
for (const transition of (
|
|
363
|
+
for (const transition of (
|
|
364
|
+
component as Component & { listTransitions?(): TransitionWithAtlasRefs[] }
|
|
365
|
+
).listTransitions?.() ?? []) {
|
|
375
366
|
for (const item of transition.listItems?.() ?? []) {
|
|
376
367
|
const actionType = item.getActionType?.();
|
|
377
368
|
if (actionType !== TransitionActionType.Sound && actionType !== TransitionActionType.Icon) continue;
|
|
@@ -400,7 +391,7 @@ async function resolveEditorCompatibleResourceOrder(
|
|
|
400
391
|
*
|
|
401
392
|
* This transform performs MaxRects bin-packing on all ImageResource items
|
|
402
393
|
* within each package, creating Atlas and Sprite property nodes. When an
|
|
403
|
-
*
|
|
394
|
+
* a raster backend is provided, it also composites the actual PNG files.
|
|
404
395
|
*
|
|
405
396
|
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
406
397
|
* at image edges are trimmed before packing. The trimmed offset and original
|
|
@@ -423,21 +414,30 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
423
414
|
return createTransform('atlas', async (doc: Document): Promise<void> => {
|
|
424
415
|
const root = doc.getRoot();
|
|
425
416
|
const logger = doc.getLogger();
|
|
426
|
-
const encoder = options.encoder
|
|
417
|
+
const encoder = options.encoder;
|
|
427
418
|
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
419
|
+
const packageFilter = options.packages ? new Set(options.packages) : null;
|
|
428
420
|
|
|
429
421
|
for (const pkg of root.listPackages()) {
|
|
422
|
+
if (packageFilter && !packageFilter.has(pkg.getName())) continue;
|
|
430
423
|
// Respect publish-selected resources when publish() precomputes a merged branch view.
|
|
431
|
-
const selectedPublishIds = new Set(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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();
|
|
431
|
+
const skeletonDependencyImageIds = getSelectedSkeletonDependencyImageIds(allResources);
|
|
435
432
|
// Process resources in declaration order (matching editor behavior)
|
|
436
433
|
const orderedResources = await resolveEditorCompatibleResourceOrder(pkg, allResources, options);
|
|
437
434
|
const resourceOrder = new Map(orderedResources.map((resource, index) => [resource.getId(), index]));
|
|
438
435
|
const inputOrder = new Map(allResources.map((resource, index) => [resource.getId(), index]));
|
|
439
436
|
const orderedAllResources = sortResourcesByOrder(allResources, resourceOrder, inputOrder);
|
|
440
|
-
const hasPackable = allResources.some((resource) =>
|
|
437
|
+
const hasPackable = allResources.some((resource) => {
|
|
438
|
+
if (isImageResource(resource) && skeletonDependencyImageIds.has(resource.getId())) return false;
|
|
439
|
+
return isPackableResource(resource);
|
|
440
|
+
});
|
|
441
441
|
if (!hasPackable) continue;
|
|
442
442
|
|
|
443
443
|
// Collect packable items in declaration order
|
|
@@ -490,7 +490,9 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
490
490
|
addUiResourceRefsFromUnknown(referencedIds, gear.getDefaultValue?.());
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
|
-
for (const transition of (
|
|
493
|
+
for (const transition of (
|
|
494
|
+
component as Component & { listTransitions?(): TransitionWithAtlasRefs[] }
|
|
495
|
+
).listTransitions?.() ?? []) {
|
|
494
496
|
for (const item of transition.listItems?.() ?? []) {
|
|
495
497
|
addUiResourceRefsFromUnknown(referencedIds, item.getStartValue?.());
|
|
496
498
|
addUiResourceRefsFromUnknown(referencedIds, item.getEndValue?.());
|
|
@@ -522,7 +524,9 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
522
524
|
const match = line.match(/img=(\w+)/);
|
|
523
525
|
if (match) referencedIds.add(match[1]);
|
|
524
526
|
}
|
|
525
|
-
} catch {
|
|
527
|
+
} catch {
|
|
528
|
+
/* .fnt file not found — OK */
|
|
529
|
+
}
|
|
526
530
|
}
|
|
527
531
|
}
|
|
528
532
|
}
|
|
@@ -531,175 +535,124 @@ export function atlas(_options: AtlasOptions = {}): Transform {
|
|
|
531
535
|
if (isImageResource(res)) {
|
|
532
536
|
// Pack referenced images, plus explicitly exported standalone images.
|
|
533
537
|
const resId = res.getId();
|
|
534
|
-
if (
|
|
538
|
+
if (skeletonDependencyImageIds.has(resId)) continue;
|
|
539
|
+
if (
|
|
540
|
+
selectedPublishIds.size === 0 &&
|
|
541
|
+
!res.getExported() &&
|
|
542
|
+
referencedIds.size > 0 &&
|
|
543
|
+
!referencedIds.has(resId)
|
|
544
|
+
)
|
|
545
|
+
continue;
|
|
535
546
|
await _collectImage(res, pkg, inputs, encoder, options, doTrim, logger);
|
|
536
547
|
} else if (isMovieClipResource(res)) {
|
|
537
548
|
const resId = res.getId();
|
|
538
|
-
if (
|
|
549
|
+
if (
|
|
550
|
+
selectedPublishIds.size === 0 &&
|
|
551
|
+
!res.getExported() &&
|
|
552
|
+
referencedIds.size > 0 &&
|
|
553
|
+
!referencedIds.has(resId)
|
|
554
|
+
)
|
|
555
|
+
continue;
|
|
539
556
|
await _collectMovieClipFrames(doc, res, pkg, inputs, encoder, options, logger);
|
|
540
557
|
} else if (isFontResource(res)) {
|
|
541
558
|
const resId = res.getId();
|
|
542
|
-
if (
|
|
559
|
+
if (
|
|
560
|
+
selectedPublishIds.size === 0 &&
|
|
561
|
+
!res.getExported() &&
|
|
562
|
+
referencedIds.size > 0 &&
|
|
563
|
+
!referencedIds.has(resId)
|
|
564
|
+
)
|
|
565
|
+
continue;
|
|
543
566
|
await _collectFontTexture(doc, res, pkg, options);
|
|
544
567
|
}
|
|
545
568
|
}
|
|
546
569
|
|
|
547
570
|
if (inputs.length === 0) continue;
|
|
548
|
-
const branchGroups = buildBranchAtlasGroups(doc, inputs, options);
|
|
549
571
|
let totalPageCount = 0;
|
|
550
572
|
let usedDirectOutput = false;
|
|
573
|
+
const { autoInputs, fixedPageGroups, standaloneGroups, reservedPageIndexes } = groupStandaloneInputs(
|
|
574
|
+
doc,
|
|
575
|
+
inputs,
|
|
576
|
+
options,
|
|
577
|
+
);
|
|
578
|
+
const branchGroups = buildBranchAtlasGroups(doc, autoInputs, options);
|
|
579
|
+
const branchPageOffsets = new Map<number, number>();
|
|
551
580
|
|
|
552
581
|
for (const group of branchGroups) {
|
|
553
|
-
const directOutput =
|
|
582
|
+
const directOutput =
|
|
583
|
+
fixedPageGroups.length === 0 && standaloneGroups.length === 0
|
|
584
|
+
? resolveDirectImageOutput(group.inputs, options)
|
|
585
|
+
: null;
|
|
554
586
|
if (directOutput) {
|
|
555
|
-
await emitDirectImageOutput(
|
|
587
|
+
await emitDirectImageOutput(
|
|
588
|
+
doc,
|
|
589
|
+
pkg,
|
|
590
|
+
directOutput,
|
|
591
|
+
encoder,
|
|
592
|
+
options,
|
|
593
|
+
logger,
|
|
594
|
+
group.branchName,
|
|
595
|
+
group.branchOrdinal,
|
|
596
|
+
);
|
|
556
597
|
usedDirectOutput = true;
|
|
557
598
|
totalPageCount += 1;
|
|
558
599
|
continue;
|
|
559
600
|
}
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
|
|
601
|
+
const pageStart = reserveAutoPageStart(branchPageOffsets, group.branchOrdinal, reservedPageIndexes);
|
|
602
|
+
const emittedPageCount = await emitPagedAtlasGroup(doc, pkg, allResources, group.inputs, {
|
|
603
|
+
branchName: group.branchName,
|
|
604
|
+
branchOrdinal: group.branchOrdinal,
|
|
605
|
+
pageStart,
|
|
606
|
+
fileNameAt: (pageIndex) => resolveAtlasOutputFileName(pkg, pageIndex, group.branchName),
|
|
607
|
+
options,
|
|
608
|
+
encoder,
|
|
609
|
+
logger,
|
|
563
610
|
});
|
|
611
|
+
totalPageCount += emittedPageCount;
|
|
612
|
+
branchPageOffsets.set(group.branchOrdinal, pageStart + emittedPageCount);
|
|
613
|
+
}
|
|
564
614
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
fast: options.fast,
|
|
576
|
-
edgePadding: false,
|
|
577
|
-
duplicatePadding: hasDuplicatePadding,
|
|
578
|
-
multiPage: options.multiPage,
|
|
579
|
-
preserveInputOrderOnTie: options.preserveInputOrderOnTie,
|
|
615
|
+
for (const group of fixedPageGroups) {
|
|
616
|
+
const emittedPageCount = await emitPagedAtlasGroup(doc, pkg, allResources, group.inputs, {
|
|
617
|
+
branchName: group.branchName,
|
|
618
|
+
branchOrdinal: group.branchOrdinal,
|
|
619
|
+
pageStart: group.pageIndex,
|
|
620
|
+
forceSinglePage: true,
|
|
621
|
+
fileNameAt: () => resolveAtlasOutputFileName(pkg, group.pageIndex, group.branchName),
|
|
622
|
+
options,
|
|
623
|
+
encoder,
|
|
624
|
+
logger,
|
|
580
625
|
});
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
totalPageCount += pages.length;
|
|
584
|
-
|
|
585
|
-
for (let p = 0; p < pages.length; p++) {
|
|
586
|
-
const page = pages[p];
|
|
587
|
-
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(group.branchOrdinal, p)}`);
|
|
588
|
-
atlasNode.setIndex(resolveAtlasIndex(group.branchOrdinal, p));
|
|
589
|
-
atlasNode.setFile(resolveAtlasOutputFileName(pkg, p, group.branchName));
|
|
590
|
-
atlasNode.setWidth(page.width);
|
|
591
|
-
atlasNode.setHeight(page.height);
|
|
592
|
-
pkg.addAtlas(atlasNode);
|
|
593
|
-
|
|
594
|
-
for (const pr of page.outputRects) {
|
|
595
|
-
const input = group.inputs[pr.index];
|
|
596
|
-
if (!input) continue;
|
|
597
|
-
const packedSize = resolvePackedRectSize(input, pr.width, pr.height, pr.rotated);
|
|
598
|
-
const rotated = pr.rotated;
|
|
599
|
-
const sprite = doc.createSprite();
|
|
600
|
-
sprite.setItemId(input.id);
|
|
601
|
-
sprite.setRectX(pr.x);
|
|
602
|
-
sprite.setRectY(pr.y);
|
|
603
|
-
sprite.setRectWidth(packedSize.width);
|
|
604
|
-
sprite.setRectHeight(packedSize.height);
|
|
605
|
-
sprite.setRotated(rotated);
|
|
606
|
-
sprite.setOffsetX(input.offsetX);
|
|
607
|
-
sprite.setOffsetY(input.offsetY);
|
|
608
|
-
sprite.setOriginalWidth(input.originalWidth);
|
|
609
|
-
sprite.setOriginalHeight(input.originalHeight);
|
|
610
|
-
sprite.setAtlas(atlasNode);
|
|
611
|
-
atlasNode.addSprite(sprite);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
for (const res of allResources) {
|
|
615
|
-
if (!isFontResource(res)) continue;
|
|
616
|
-
const fextras = res.getExtras() as FontResourceExtras;
|
|
617
|
-
const alias = fextras?._fontSpriteAlias;
|
|
618
|
-
if (!alias) continue;
|
|
619
|
-
const imgSprite = page.outputRects.find((result) => group.inputs[result.index]?.id === alias.textureId);
|
|
620
|
-
if (!imgSprite) continue;
|
|
621
|
-
const imgInput = group.inputs[imgSprite.index];
|
|
622
|
-
const fontSprite = doc.createSprite();
|
|
623
|
-
fontSprite.setItemId(alias.fontId);
|
|
624
|
-
fontSprite.setRectX(imgSprite.x);
|
|
625
|
-
fontSprite.setRectY(imgSprite.y);
|
|
626
|
-
fontSprite.setRectWidth(imgSprite.width);
|
|
627
|
-
fontSprite.setRectHeight(imgSprite.height);
|
|
628
|
-
fontSprite.setRotated(imgSprite.rotated);
|
|
629
|
-
if (imgInput) {
|
|
630
|
-
fontSprite.setOffsetX(imgInput.offsetX);
|
|
631
|
-
fontSprite.setOffsetY(imgInput.offsetY);
|
|
632
|
-
fontSprite.setOriginalWidth(imgInput.originalWidth);
|
|
633
|
-
fontSprite.setOriginalHeight(imgInput.originalHeight);
|
|
634
|
-
}
|
|
635
|
-
fontSprite.setAtlas(atlasNode);
|
|
636
|
-
atlasNode.addSprite(fontSprite);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
626
|
+
totalPageCount += emittedPageCount;
|
|
627
|
+
}
|
|
639
628
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const compositeInputs: Array<{ input: Uint8Array; left: number; top: number }> = [];
|
|
647
|
-
|
|
648
|
-
for (const pr of page.outputRects) {
|
|
649
|
-
const input = group.inputs[pr.index];
|
|
650
|
-
if (!input) continue;
|
|
651
|
-
if (pr.width <= 0 || pr.height <= 0 || input.width <= 0 || input.height <= 0) continue;
|
|
652
|
-
try {
|
|
653
|
-
let imgBuffer: Uint8Array;
|
|
654
|
-
|
|
655
|
-
if (input.trimBuffer) {
|
|
656
|
-
imgBuffer = input.trimBuffer;
|
|
657
|
-
if (imgBuffer.length === 0) continue;
|
|
658
|
-
} else {
|
|
659
|
-
if (!isImageResource(input.resource)) {
|
|
660
|
-
logger.warn(`atlas: Non-image input "${input.id}" is missing inline buffer, skipping compositing.`);
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
const filePath = _resolveImagePath(input.resource, pkg, options.basePath!);
|
|
664
|
-
imgBuffer = await encoder(filePath).toBuffer();
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
if (pr.rotated) imgBuffer = await encoder(imgBuffer).rotate(270).toBuffer();
|
|
668
|
-
|
|
669
|
-
compositeInputs.push({
|
|
670
|
-
input: imgBuffer,
|
|
671
|
-
left: pr.x,
|
|
672
|
-
top: pr.y,
|
|
673
|
-
});
|
|
674
|
-
} catch {
|
|
675
|
-
logger.warn(`atlas: Could not read image "${input.id}" for compositing.`);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
629
|
+
const standalonePageOffsets = new Map(branchPageOffsets);
|
|
630
|
+
for (const group of fixedPageGroups) {
|
|
631
|
+
const nextPageIndex = group.pageIndex + 1;
|
|
632
|
+
const current = standalonePageOffsets.get(group.branchOrdinal) ?? 0;
|
|
633
|
+
if (nextPageIndex > current) standalonePageOffsets.set(group.branchOrdinal, nextPageIndex);
|
|
634
|
+
}
|
|
678
635
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
.png()
|
|
692
|
-
.toFile(outputFile);
|
|
693
|
-
|
|
694
|
-
logger.info(`atlas: Generated ${atlasFileName} (${page.width}x${page.height}, ${page.outputRects.length} sprites)`);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
636
|
+
for (const group of standaloneGroups) {
|
|
637
|
+
const emittedPageCount = await emitStandaloneAtlasGroup(doc, pkg, group, {
|
|
638
|
+
atlasIndexStart: standalonePageOffsets.get(group.branchOrdinal) ?? 0,
|
|
639
|
+
options,
|
|
640
|
+
encoder,
|
|
641
|
+
logger,
|
|
642
|
+
});
|
|
643
|
+
totalPageCount += emittedPageCount;
|
|
644
|
+
standalonePageOffsets.set(
|
|
645
|
+
group.branchOrdinal,
|
|
646
|
+
(standalonePageOffsets.get(group.branchOrdinal) ?? 0) + emittedPageCount,
|
|
647
|
+
);
|
|
697
648
|
}
|
|
698
649
|
|
|
699
650
|
if (usedDirectOutput) {
|
|
700
651
|
logger.info(`atlas: Direct output for single image package "${pkg.getName()}".`);
|
|
701
652
|
}
|
|
702
|
-
logger.info(
|
|
653
|
+
logger.info(
|
|
654
|
+
`atlas: Packed ${inputs.length} images into ${totalPageCount} atlas(es) for package "${pkg.getName()}".`,
|
|
655
|
+
);
|
|
703
656
|
}
|
|
704
657
|
});
|
|
705
658
|
}
|
|
@@ -709,14 +662,17 @@ function buildBranchAtlasGroups(doc: Document, inputs: InputItem[], options: Atl
|
|
|
709
662
|
return [{ branchName: '', branchOrdinal: 0, inputs }];
|
|
710
663
|
}
|
|
711
664
|
|
|
712
|
-
const discoveredBranchNames = [
|
|
713
|
-
.map((input) => getInputBranchName(input))
|
|
714
|
-
|
|
665
|
+
const discoveredBranchNames = [
|
|
666
|
+
...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName)),
|
|
667
|
+
];
|
|
715
668
|
if (discoveredBranchNames.length === 0) {
|
|
716
669
|
return [{ branchName: '', branchOrdinal: 0, inputs }];
|
|
717
670
|
}
|
|
718
671
|
|
|
719
|
-
const orderedBranchNames = doc
|
|
672
|
+
const orderedBranchNames = doc
|
|
673
|
+
.getRoot()
|
|
674
|
+
.listBranches()
|
|
675
|
+
.filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
720
676
|
for (const branchName of discoveredBranchNames) {
|
|
721
677
|
if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
722
678
|
}
|
|
@@ -747,6 +703,263 @@ function buildBranchAtlasGroups(doc: Document, inputs: InputItem[], options: Atl
|
|
|
747
703
|
}));
|
|
748
704
|
}
|
|
749
705
|
|
|
706
|
+
function reserveAutoPageStart(
|
|
707
|
+
branchPageOffsets: Map<number, number>,
|
|
708
|
+
branchOrdinal: number,
|
|
709
|
+
reservedPageIndexes: Set<number>,
|
|
710
|
+
): number {
|
|
711
|
+
let pageIndex = branchPageOffsets.get(branchOrdinal) ?? 0;
|
|
712
|
+
while (branchOrdinal === 0 && reservedPageIndexes.has(pageIndex)) {
|
|
713
|
+
pageIndex += 1;
|
|
714
|
+
}
|
|
715
|
+
return pageIndex;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
async function emitPagedAtlasGroup(
|
|
719
|
+
doc: Document,
|
|
720
|
+
pkg: Package,
|
|
721
|
+
allResources: PackageResource[],
|
|
722
|
+
inputs: InputItem[],
|
|
723
|
+
context: {
|
|
724
|
+
branchName: string;
|
|
725
|
+
branchOrdinal: number;
|
|
726
|
+
pageStart: number;
|
|
727
|
+
fileNameAt: (pageIndex: number) => string;
|
|
728
|
+
options: AtlasOptions;
|
|
729
|
+
encoder: AtlasRasterBackend | undefined;
|
|
730
|
+
logger: ILogger;
|
|
731
|
+
forceSinglePage?: boolean;
|
|
732
|
+
},
|
|
733
|
+
): Promise<number> {
|
|
734
|
+
if (inputs.length === 0) return 0;
|
|
735
|
+
const pages = packAtlasPages(inputs, context.options, context.forceSinglePage === true);
|
|
736
|
+
if (pages.length === 0) return 0;
|
|
737
|
+
|
|
738
|
+
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
739
|
+
const page = pages[pageOffset];
|
|
740
|
+
const pageIndex = context.pageStart + pageOffset;
|
|
741
|
+
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(context.branchOrdinal, pageIndex)}`);
|
|
742
|
+
atlasNode.setIndex(resolveAtlasIndex(context.branchOrdinal, pageIndex));
|
|
743
|
+
atlasNode.setFile(context.fileNameAt(pageIndex));
|
|
744
|
+
atlasNode.setWidth(page.width);
|
|
745
|
+
atlasNode.setHeight(page.height);
|
|
746
|
+
pkg.addAtlas(atlasNode);
|
|
747
|
+
|
|
748
|
+
attachSpritesToAtlas(doc, allResources, inputs, page.outputRects, atlasNode);
|
|
749
|
+
await writeAtlasPageImage(
|
|
750
|
+
pkg,
|
|
751
|
+
inputs,
|
|
752
|
+
page,
|
|
753
|
+
atlasNode.getFile(),
|
|
754
|
+
context.encoder,
|
|
755
|
+
context.options,
|
|
756
|
+
context.logger,
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return pages.length;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
async function emitStandaloneAtlasGroup(
|
|
764
|
+
doc: Document,
|
|
765
|
+
pkg: Package,
|
|
766
|
+
group: StandaloneAtlasGroup,
|
|
767
|
+
context: {
|
|
768
|
+
atlasIndexStart: number;
|
|
769
|
+
options: AtlasOptions;
|
|
770
|
+
encoder: AtlasRasterBackend | undefined;
|
|
771
|
+
logger: ILogger;
|
|
772
|
+
},
|
|
773
|
+
): Promise<number> {
|
|
774
|
+
if (group.inputs.length === 0) return 0;
|
|
775
|
+
const pages = packAtlasPages(
|
|
776
|
+
group.inputs,
|
|
777
|
+
context.options,
|
|
778
|
+
true,
|
|
779
|
+
group.sizeMode === 'npot'
|
|
780
|
+
? { powerOfTwo: false, multipleOfFour: false, square: false }
|
|
781
|
+
: group.sizeMode === 'multipleOf4'
|
|
782
|
+
? { powerOfTwo: false, multipleOfFour: true, square: false }
|
|
783
|
+
: undefined,
|
|
784
|
+
);
|
|
785
|
+
if (pages.length === 0) return 0;
|
|
786
|
+
|
|
787
|
+
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
788
|
+
const page = pages[pageOffset];
|
|
789
|
+
const baseFileName = resolveStandaloneAtlasOutputFileName(pkg, group.resource, group.branchName);
|
|
790
|
+
const atlasFileName = pages.length <= 1 ? baseFileName : insertFileNameSuffix(baseFileName, `_${pageOffset}`);
|
|
791
|
+
const atlasIndex = context.atlasIndexStart + pageOffset;
|
|
792
|
+
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(group.branchOrdinal, atlasIndex)}`);
|
|
793
|
+
atlasNode.setIndex(resolveAtlasIndex(group.branchOrdinal, atlasIndex));
|
|
794
|
+
atlasNode.setFile(atlasFileName);
|
|
795
|
+
const standaloneSize = resolveStandaloneAtlasSize(page.width, page.height, group.sizeMode, context.options);
|
|
796
|
+
atlasNode.setWidth(standaloneSize.width);
|
|
797
|
+
atlasNode.setHeight(standaloneSize.height);
|
|
798
|
+
pkg.addAtlas(atlasNode);
|
|
799
|
+
|
|
800
|
+
attachSpritesToAtlas(doc, [], group.inputs, page.outputRects, atlasNode);
|
|
801
|
+
await writeAtlasPageImage(
|
|
802
|
+
pkg,
|
|
803
|
+
group.inputs,
|
|
804
|
+
{ ...page, width: standaloneSize.width, height: standaloneSize.height },
|
|
805
|
+
atlasFileName,
|
|
806
|
+
context.encoder,
|
|
807
|
+
context.options,
|
|
808
|
+
context.logger,
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return pages.length;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function packAtlasPages(
|
|
816
|
+
inputs: InputItem[],
|
|
817
|
+
options: AtlasOptions,
|
|
818
|
+
forceSinglePage: boolean,
|
|
819
|
+
sizeOverrides?: {
|
|
820
|
+
powerOfTwo: boolean;
|
|
821
|
+
multipleOfFour: boolean;
|
|
822
|
+
square: boolean;
|
|
823
|
+
},
|
|
824
|
+
): ReturnType<MaxRectsPackerCompat['pack']> {
|
|
825
|
+
const hasDuplicatePadding = inputs.some((input) => {
|
|
826
|
+
return isImageResource(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
827
|
+
});
|
|
828
|
+
const packer = new MaxRectsPackerCompat({
|
|
829
|
+
pot: sizeOverrides?.powerOfTwo ?? options.powerOfTwo,
|
|
830
|
+
mof: sizeOverrides?.multipleOfFour ?? !options.powerOfTwo,
|
|
831
|
+
padding: options.padding,
|
|
832
|
+
rotation: options.allowRotation,
|
|
833
|
+
minWidth: 16,
|
|
834
|
+
minHeight: 16,
|
|
835
|
+
maxWidth: options.maxSize,
|
|
836
|
+
maxHeight: options.maxSize,
|
|
837
|
+
square: sizeOverrides?.square ?? options.square,
|
|
838
|
+
fast: options.fast,
|
|
839
|
+
edgePadding: false,
|
|
840
|
+
duplicatePadding: hasDuplicatePadding,
|
|
841
|
+
multiPage: forceSinglePage ? false : options.multiPage,
|
|
842
|
+
preserveInputOrderOnTie: options.preserveInputOrderOnTie,
|
|
843
|
+
});
|
|
844
|
+
return packer.pack(inputs.map((input, index) => inputToCompatRect(input, index))) ?? [];
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function attachSpritesToAtlas(
|
|
848
|
+
doc: Document,
|
|
849
|
+
allResources: PackageResource[],
|
|
850
|
+
inputs: InputItem[],
|
|
851
|
+
outputRects: Array<{ index: number; x: number; y: number; width: number; height: number; rotated: boolean }>,
|
|
852
|
+
atlasNode: ReturnType<Document['createAtlas']>,
|
|
853
|
+
): void {
|
|
854
|
+
for (const packedRect of outputRects) {
|
|
855
|
+
const input = inputs[packedRect.index];
|
|
856
|
+
if (!input) continue;
|
|
857
|
+
const packedSize = resolvePackedRectSize(input, packedRect.width, packedRect.height, packedRect.rotated);
|
|
858
|
+
const sprite = doc.createSprite();
|
|
859
|
+
sprite.setItemId(input.id);
|
|
860
|
+
sprite.setRectX(packedRect.x);
|
|
861
|
+
sprite.setRectY(packedRect.y);
|
|
862
|
+
sprite.setRectWidth(packedSize.width);
|
|
863
|
+
sprite.setRectHeight(packedSize.height);
|
|
864
|
+
sprite.setRotated(packedRect.rotated);
|
|
865
|
+
sprite.setOffsetX(input.offsetX);
|
|
866
|
+
sprite.setOffsetY(input.offsetY);
|
|
867
|
+
sprite.setOriginalWidth(input.originalWidth);
|
|
868
|
+
sprite.setOriginalHeight(input.originalHeight);
|
|
869
|
+
sprite.setAtlas(atlasNode);
|
|
870
|
+
atlasNode.addSprite(sprite);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
for (const resource of allResources) {
|
|
874
|
+
if (!isFontResource(resource)) continue;
|
|
875
|
+
const extras = resource.getExtras() as FontResourceExtras;
|
|
876
|
+
const alias = extras?._fontSpriteAlias;
|
|
877
|
+
if (!alias) continue;
|
|
878
|
+
const imageSprite = outputRects.find((result) => inputs[result.index]?.id === alias.textureId);
|
|
879
|
+
if (!imageSprite) continue;
|
|
880
|
+
const imageInput = inputs[imageSprite.index];
|
|
881
|
+
const fontSprite = doc.createSprite();
|
|
882
|
+
fontSprite.setItemId(alias.fontId);
|
|
883
|
+
fontSprite.setRectX(imageSprite.x);
|
|
884
|
+
fontSprite.setRectY(imageSprite.y);
|
|
885
|
+
fontSprite.setRectWidth(imageSprite.width);
|
|
886
|
+
fontSprite.setRectHeight(imageSprite.height);
|
|
887
|
+
fontSprite.setRotated(imageSprite.rotated);
|
|
888
|
+
if (imageInput) {
|
|
889
|
+
fontSprite.setOffsetX(imageInput.offsetX);
|
|
890
|
+
fontSprite.setOffsetY(imageInput.offsetY);
|
|
891
|
+
fontSprite.setOriginalWidth(imageInput.originalWidth);
|
|
892
|
+
fontSprite.setOriginalHeight(imageInput.originalHeight);
|
|
893
|
+
}
|
|
894
|
+
fontSprite.setAtlas(atlasNode);
|
|
895
|
+
atlasNode.addSprite(fontSprite);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async function writeAtlasPageImage(
|
|
900
|
+
pkg: Package,
|
|
901
|
+
inputs: InputItem[],
|
|
902
|
+
page: {
|
|
903
|
+
width: number;
|
|
904
|
+
height: number;
|
|
905
|
+
outputRects: Array<{ index: number; x: number; y: number; width: number; height: number; rotated: boolean }>;
|
|
906
|
+
},
|
|
907
|
+
atlasFileName: string,
|
|
908
|
+
encoder: AtlasRasterBackend | undefined,
|
|
909
|
+
options: AtlasOptions,
|
|
910
|
+
logger: ILogger,
|
|
911
|
+
): Promise<void> {
|
|
912
|
+
if (!encoder || !options.outputPath) return;
|
|
913
|
+
if (options.mkdir) {
|
|
914
|
+
await options.mkdir(options.outputPath);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const compositeInputs: Array<{ input: Uint8Array; left: number; top: number }> = [];
|
|
918
|
+
for (const packedRect of page.outputRects) {
|
|
919
|
+
const input = inputs[packedRect.index];
|
|
920
|
+
if (!input) continue;
|
|
921
|
+
if (packedRect.width <= 0 || packedRect.height <= 0 || input.width <= 0 || input.height <= 0) continue;
|
|
922
|
+
try {
|
|
923
|
+
let imageBuffer: Uint8Array;
|
|
924
|
+
if (input.trimBuffer) {
|
|
925
|
+
imageBuffer = input.trimBuffer;
|
|
926
|
+
if (imageBuffer.length === 0) continue;
|
|
927
|
+
} else if (input.rasterizedBuffer) {
|
|
928
|
+
imageBuffer = input.rasterizedBuffer;
|
|
929
|
+
} else {
|
|
930
|
+
if (!isImageResource(input.resource)) {
|
|
931
|
+
logger.warn(`atlas: Non-image input "${input.id}" is missing inline buffer, skipping compositing.`);
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
const filePath = _resolveImagePath(input.resource, pkg, options.basePath!);
|
|
935
|
+
imageBuffer = await encoder(filePath).toBuffer();
|
|
936
|
+
}
|
|
937
|
+
if (packedRect.rotated) imageBuffer = await encoder(imageBuffer).rotate(270).toBuffer();
|
|
938
|
+
compositeInputs.push({
|
|
939
|
+
input: imageBuffer,
|
|
940
|
+
left: packedRect.x,
|
|
941
|
+
top: packedRect.y,
|
|
942
|
+
});
|
|
943
|
+
} catch {
|
|
944
|
+
logger.warn(`atlas: Could not read image "${input.id}" for compositing.`);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const outputFile = `${options.outputPath}/${atlasFileName}`;
|
|
949
|
+
await encoder({
|
|
950
|
+
create: {
|
|
951
|
+
width: page.width,
|
|
952
|
+
height: page.height,
|
|
953
|
+
channels: 4 as const,
|
|
954
|
+
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
|
955
|
+
},
|
|
956
|
+
})
|
|
957
|
+
.composite(compositeInputs)
|
|
958
|
+
.toFile(outputFile);
|
|
959
|
+
|
|
960
|
+
logger.info(`atlas: Generated ${atlasFileName} (${page.width}x${page.height}, ${page.outputRects.length} sprites)`);
|
|
961
|
+
}
|
|
962
|
+
|
|
750
963
|
function inputToCompatRect(input: InputItem, index: number): CompatNodeRect {
|
|
751
964
|
const duplicatePadding = isImageResource(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
752
965
|
return {
|
|
@@ -764,7 +977,12 @@ function inputToCompatRect(input: InputItem, index: number): CompatNodeRect {
|
|
|
764
977
|
};
|
|
765
978
|
}
|
|
766
979
|
|
|
767
|
-
function resolvePackedRectSize(
|
|
980
|
+
function resolvePackedRectSize(
|
|
981
|
+
input: InputItem,
|
|
982
|
+
width: number,
|
|
983
|
+
height: number,
|
|
984
|
+
rectRotated: boolean,
|
|
985
|
+
): { width: number; height: number } {
|
|
768
986
|
if (!rectRotated) return { width, height };
|
|
769
987
|
return {
|
|
770
988
|
width: input.height,
|
|
@@ -784,7 +1002,11 @@ function resolveDirectImageOutput(inputs: InputItem[], options: AtlasOptions): I
|
|
|
784
1002
|
return input;
|
|
785
1003
|
}
|
|
786
1004
|
|
|
787
|
-
function resolveDirectOutputAtlasSize(
|
|
1005
|
+
function resolveDirectOutputAtlasSize(
|
|
1006
|
+
width: number,
|
|
1007
|
+
height: number,
|
|
1008
|
+
options: AtlasOptions,
|
|
1009
|
+
): { width: number; height: number } {
|
|
788
1010
|
let resolvedWidth = width;
|
|
789
1011
|
let resolvedHeight = height;
|
|
790
1012
|
if (options.square) {
|
|
@@ -803,7 +1025,7 @@ async function emitDirectImageOutput(
|
|
|
803
1025
|
doc: Document,
|
|
804
1026
|
pkg: Package,
|
|
805
1027
|
input: InputItem,
|
|
806
|
-
encoder:
|
|
1028
|
+
encoder: AtlasRasterBackend | undefined,
|
|
807
1029
|
options: AtlasOptions,
|
|
808
1030
|
logger: ILogger,
|
|
809
1031
|
branchName: string = '',
|
|
@@ -876,16 +1098,63 @@ function resolveAtlasOutputFileName(pkg: Package, pageIndex: number, branchName:
|
|
|
876
1098
|
return `${pkg.getPublishName() || pkg.getName()}_atlas${pageIndex}${suffix}.png`;
|
|
877
1099
|
}
|
|
878
1100
|
|
|
1101
|
+
function resolveStandaloneAtlasOutputFileName(pkg: Package, resource: PackInputResource, branchName: string): string {
|
|
1102
|
+
const baseName = `${pkg.getPublishName() || pkg.getName()}_atlas_${getPublishedItemId(resource)}`;
|
|
1103
|
+
const suffix = branchName ? `_${branchName}` : '';
|
|
1104
|
+
if (isImageResource(resource)) {
|
|
1105
|
+
const ext = extname(resolveImageFileName(resource)) || '.png';
|
|
1106
|
+
return `${baseName}${suffix}${ext}`;
|
|
1107
|
+
}
|
|
1108
|
+
return `${baseName}${suffix}.png`;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function resolveStandaloneAtlasSize(
|
|
1112
|
+
width: number,
|
|
1113
|
+
height: number,
|
|
1114
|
+
sizeMode: StandaloneAtlasGroup['sizeMode'],
|
|
1115
|
+
options: AtlasOptions,
|
|
1116
|
+
): { width: number; height: number } {
|
|
1117
|
+
if (sizeMode === 'npot') {
|
|
1118
|
+
return { width, height };
|
|
1119
|
+
}
|
|
1120
|
+
if (sizeMode === 'multipleOf4') {
|
|
1121
|
+
return {
|
|
1122
|
+
width: roundUpToMultiple(width, 4),
|
|
1123
|
+
height: roundUpToMultiple(height, 4),
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
return resolveDirectOutputAtlasSize(width, height, options);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
879
1129
|
function resolveImageFileName(resource: ImageResource): string {
|
|
880
1130
|
const extras = resource.getExtras() as ImageResourceExtras;
|
|
881
1131
|
return resource.getFileName() || extras._fileName || resource.getName();
|
|
882
1132
|
}
|
|
883
1133
|
|
|
1134
|
+
function extname(fileName: string): string {
|
|
1135
|
+
const normalized = fileName.replace(/\\/g, '/');
|
|
1136
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
1137
|
+
const lastDot = normalized.lastIndexOf('.');
|
|
1138
|
+
if (lastDot <= lastSlash) return '';
|
|
1139
|
+
return normalized.slice(lastDot);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function insertFileNameSuffix(fileName: string, suffix: string): string {
|
|
1143
|
+
const extension = extname(fileName);
|
|
1144
|
+
if (!extension) return `${fileName}${suffix}`;
|
|
1145
|
+
return `${fileName.slice(0, -extension.length)}${suffix}${extension}`;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
884
1148
|
function nextPow2(value: number): number {
|
|
885
1149
|
if (value <= 1) return 1;
|
|
886
1150
|
return 2 ** Math.ceil(Math.log2(value));
|
|
887
1151
|
}
|
|
888
1152
|
|
|
1153
|
+
function roundUpToMultiple(value: number, base: number): number {
|
|
1154
|
+
if (value <= 0) return 0;
|
|
1155
|
+
return Math.ceil(value / base) * base;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
889
1158
|
function sortResourcesByOrder(
|
|
890
1159
|
resources: PackageResource[],
|
|
891
1160
|
orderMap: Map<string, number>,
|
|
@@ -895,11 +1164,23 @@ function sortResourcesByOrder(
|
|
|
895
1164
|
ordered.sort((left, right) => {
|
|
896
1165
|
const leftId = left.getId();
|
|
897
1166
|
const rightId = right.getId();
|
|
898
|
-
const leftOrder =
|
|
899
|
-
|
|
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;
|
|
900
1175
|
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
901
|
-
const leftInputOrder =
|
|
902
|
-
|
|
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;
|
|
903
1184
|
if (leftInputOrder !== rightInputOrder) return leftInputOrder - rightInputOrder;
|
|
904
1185
|
return (leftId ?? '').localeCompare(rightId ?? '');
|
|
905
1186
|
});
|
|
@@ -929,22 +1210,117 @@ interface ExtractedJtaData {
|
|
|
929
1210
|
meta?: ExtractedJtaMeta;
|
|
930
1211
|
}
|
|
931
1212
|
|
|
1213
|
+
function getResourceTextureSetMode(resource: PackInputResource): TextureSetMode {
|
|
1214
|
+
if (isImageResource(resource)) {
|
|
1215
|
+
return parseTextureSetMode(resource.getTextureSetMode?.());
|
|
1216
|
+
}
|
|
1217
|
+
return parseTextureSetMode(resource.getTextureSetMode?.());
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function groupStandaloneInputs(
|
|
1221
|
+
doc: Document,
|
|
1222
|
+
inputs: InputItem[],
|
|
1223
|
+
options: AtlasOptions,
|
|
1224
|
+
): {
|
|
1225
|
+
autoInputs: InputItem[];
|
|
1226
|
+
fixedPageGroups: PagedAtlasGroup[];
|
|
1227
|
+
standaloneGroups: StandaloneAtlasGroup[];
|
|
1228
|
+
reservedPageIndexes: Set<number>;
|
|
1229
|
+
} {
|
|
1230
|
+
const autoInputs: InputItem[] = [];
|
|
1231
|
+
const fixedInputsByPage = new Map<string, PagedAtlasGroup>();
|
|
1232
|
+
const standaloneGroups = new Map<string, StandaloneAtlasGroup>();
|
|
1233
|
+
const reservedPageIndexes = new Set<number>();
|
|
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));
|
|
1241
|
+
for (const branchName of discoveredBranchNames) {
|
|
1242
|
+
if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
1243
|
+
}
|
|
1244
|
+
const branchOrdinalByName = new Map<string, number>();
|
|
1245
|
+
branchOrdinalByName.set('', 0);
|
|
1246
|
+
if (options.separatedAtlasForBranch) {
|
|
1247
|
+
let ordinal = 1;
|
|
1248
|
+
for (const branchName of orderedBranchNames) {
|
|
1249
|
+
branchOrdinalByName.set(branchName, ordinal++);
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
for (const branchName of orderedBranchNames) {
|
|
1253
|
+
branchOrdinalByName.set(branchName, 0);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
for (const input of inputs) {
|
|
1258
|
+
const branchName = getInputBranchName(input);
|
|
1259
|
+
const branchOrdinal = branchOrdinalByName.get(branchName) ?? 0;
|
|
1260
|
+
const mode = getResourceTextureSetMode(input.resource);
|
|
1261
|
+
if (mode.kind === 'standalone') {
|
|
1262
|
+
const resourceId = getPublishedItemId(input.resource);
|
|
1263
|
+
const key = `${branchName}\u0000${resourceId}`;
|
|
1264
|
+
const existing = standaloneGroups.get(key);
|
|
1265
|
+
if (existing) {
|
|
1266
|
+
existing.inputs.push(input);
|
|
1267
|
+
} else {
|
|
1268
|
+
standaloneGroups.set(key, {
|
|
1269
|
+
resource: input.resource,
|
|
1270
|
+
branchName,
|
|
1271
|
+
branchOrdinal,
|
|
1272
|
+
sizeMode: mode.sizeMode,
|
|
1273
|
+
inputs: [input],
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
if (mode.kind === 'page') {
|
|
1279
|
+
reservedPageIndexes.add(mode.pageIndex);
|
|
1280
|
+
const key = `${branchName}\u0000${mode.pageIndex}`;
|
|
1281
|
+
const existing = fixedInputsByPage.get(key);
|
|
1282
|
+
if (existing) {
|
|
1283
|
+
existing.inputs.push(input);
|
|
1284
|
+
} else {
|
|
1285
|
+
fixedInputsByPage.set(key, {
|
|
1286
|
+
pageIndex: mode.pageIndex,
|
|
1287
|
+
branchName,
|
|
1288
|
+
branchOrdinal,
|
|
1289
|
+
inputs: [input],
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
autoInputs.push(input);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
return {
|
|
1298
|
+
autoInputs,
|
|
1299
|
+
fixedPageGroups: [...fixedInputsByPage.values()].sort(
|
|
1300
|
+
(left, right) => left.branchOrdinal - right.branchOrdinal || left.pageIndex - right.pageIndex,
|
|
1301
|
+
),
|
|
1302
|
+
standaloneGroups: [...standaloneGroups.values()].sort(
|
|
1303
|
+
(left, right) =>
|
|
1304
|
+
left.branchOrdinal - right.branchOrdinal ||
|
|
1305
|
+
getPublishedItemId(left.resource).localeCompare(getPublishedItemId(right.resource)),
|
|
1306
|
+
),
|
|
1307
|
+
reservedPageIndexes,
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
|
|
932
1311
|
/**
|
|
933
|
-
* Trim transparent edges from an image using
|
|
1312
|
+
* Trim transparent edges from an image using the host raster backend.
|
|
934
1313
|
* Returns the trimmed buffer, dimensions, and offsets.
|
|
935
1314
|
* Falls back to the original image if trim fails (e.g. no alpha channel, no transparent edges).
|
|
936
1315
|
*/
|
|
937
1316
|
async function _trimImage(
|
|
938
|
-
encoder:
|
|
939
|
-
|
|
1317
|
+
encoder: AtlasRasterBackend,
|
|
1318
|
+
input: AtlasRasterInput,
|
|
940
1319
|
originalWidth: number,
|
|
941
1320
|
originalHeight: number,
|
|
942
1321
|
): Promise<TrimInfo> {
|
|
943
1322
|
try {
|
|
944
|
-
const trimResult = await encoder(
|
|
945
|
-
.ensureAlpha()
|
|
946
|
-
.raw()
|
|
947
|
-
.toBuffer({ resolveWithObject: true });
|
|
1323
|
+
const trimResult = await encoder(input).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
948
1324
|
if (!isResolvedBuffer(trimResult)) {
|
|
949
1325
|
throw new Error('atlas: encoder raw alpha trim did not return resolved metadata.');
|
|
950
1326
|
}
|
|
@@ -982,7 +1358,7 @@ async function _trimImage(
|
|
|
982
1358
|
|
|
983
1359
|
const trimmedWidth = maxX - minX + 1;
|
|
984
1360
|
const trimmedHeight = maxY - minY + 1;
|
|
985
|
-
const buffer = await encoder(
|
|
1361
|
+
const buffer = await encoder(input)
|
|
986
1362
|
.extract({
|
|
987
1363
|
left: minX,
|
|
988
1364
|
top: minY,
|
|
@@ -1002,7 +1378,7 @@ async function _trimImage(
|
|
|
1002
1378
|
};
|
|
1003
1379
|
} catch {
|
|
1004
1380
|
// Trim failed (e.g. JPEG without alpha, nothing to trim) — return original
|
|
1005
|
-
const buf = await encoder(
|
|
1381
|
+
const buf = await encoder(input).png().toBuffer();
|
|
1006
1382
|
return {
|
|
1007
1383
|
buffer: buf,
|
|
1008
1384
|
width: originalWidth,
|
|
@@ -1032,26 +1408,50 @@ function _resolveImagePath(resource: ImageResource, pkg: Package, basePath: stri
|
|
|
1032
1408
|
}
|
|
1033
1409
|
|
|
1034
1410
|
type InputItem = {
|
|
1035
|
-
id: string;
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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;
|
|
1039
1421
|
sourceKind: 'image' | 'movieclip-frame';
|
|
1040
1422
|
};
|
|
1041
1423
|
|
|
1424
|
+
interface StandaloneAtlasGroup {
|
|
1425
|
+
resource: PackInputResource;
|
|
1426
|
+
branchName: string;
|
|
1427
|
+
branchOrdinal: number;
|
|
1428
|
+
sizeMode: 'default' | 'npot' | 'multipleOf4';
|
|
1429
|
+
inputs: InputItem[];
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
interface PagedAtlasGroup {
|
|
1433
|
+
pageIndex: number;
|
|
1434
|
+
branchName: string;
|
|
1435
|
+
branchOrdinal: number;
|
|
1436
|
+
inputs: InputItem[];
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1042
1439
|
/** Collect a single ImageResource into the inputs array. */
|
|
1043
1440
|
async function _collectImage(
|
|
1044
1441
|
resource: ImageResource,
|
|
1045
1442
|
pkg: Package,
|
|
1046
1443
|
inputs: InputItem[],
|
|
1047
|
-
encoder:
|
|
1444
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1048
1445
|
options: AtlasOptions,
|
|
1049
1446
|
doTrim: boolean,
|
|
1050
1447
|
logger: ILogger,
|
|
1051
1448
|
): Promise<void> {
|
|
1052
1449
|
let origW = resource.getWidth() ?? 0;
|
|
1053
1450
|
let origH = resource.getHeight() ?? 0;
|
|
1451
|
+
const declaredWidth = origW;
|
|
1452
|
+
const declaredHeight = origH;
|
|
1054
1453
|
let sourceHasAlpha = false;
|
|
1454
|
+
let rasterizedBuffer: Uint8Array | undefined;
|
|
1055
1455
|
|
|
1056
1456
|
if (encoder && options.basePath) {
|
|
1057
1457
|
const filePath = _resolveImagePath(resource, pkg, options.basePath);
|
|
@@ -1064,6 +1464,13 @@ async function _collectImage(
|
|
|
1064
1464
|
resource.setHeight(origH);
|
|
1065
1465
|
}
|
|
1066
1466
|
sourceHasAlpha = metadata.hasAlpha === true || metadata.channels === 4;
|
|
1467
|
+
if (/\.svg$/i.test(resolveImageFileName(resource)) && declaredWidth > 0 && declaredHeight > 0) {
|
|
1468
|
+
rasterizedBuffer = await encoder(filePath)
|
|
1469
|
+
.resize({ width: declaredWidth, height: declaredHeight, fit: 'fill' })
|
|
1470
|
+
.png()
|
|
1471
|
+
.toBuffer();
|
|
1472
|
+
sourceHasAlpha = true;
|
|
1473
|
+
}
|
|
1067
1474
|
} catch {
|
|
1068
1475
|
if (origW === 0 || origH === 0) {
|
|
1069
1476
|
logger.warn(`atlas: Could not read image "${filePath}", skipping.`);
|
|
@@ -1074,13 +1481,16 @@ async function _collectImage(
|
|
|
1074
1481
|
|
|
1075
1482
|
if (origW <= 0 || origH <= 0) return;
|
|
1076
1483
|
|
|
1077
|
-
let packW = origW,
|
|
1484
|
+
let packW = origW,
|
|
1485
|
+
packH = origH,
|
|
1486
|
+
offX = 0,
|
|
1487
|
+
offY = 0;
|
|
1078
1488
|
let trimBuf: Uint8Array | undefined;
|
|
1079
1489
|
|
|
1080
1490
|
if (doTrim && sourceHasAlpha && options.basePath && encoder) {
|
|
1081
1491
|
const filePath = _resolveImagePath(resource, pkg, options.basePath);
|
|
1082
1492
|
try {
|
|
1083
|
-
const trimResult = await _trimImage(encoder, filePath, origW, origH);
|
|
1493
|
+
const trimResult = await _trimImage(encoder, rasterizedBuffer ?? filePath, origW, origH);
|
|
1084
1494
|
packW = trimResult.width;
|
|
1085
1495
|
packH = trimResult.height;
|
|
1086
1496
|
offX = trimResult.offsetX;
|
|
@@ -1092,11 +1502,16 @@ async function _collectImage(
|
|
|
1092
1502
|
}
|
|
1093
1503
|
|
|
1094
1504
|
inputs.push({
|
|
1095
|
-
id: getPublishedItemId(resource),
|
|
1096
|
-
|
|
1097
|
-
|
|
1505
|
+
id: getPublishedItemId(resource),
|
|
1506
|
+
width: packW,
|
|
1507
|
+
height: packH,
|
|
1508
|
+
originalWidth: origW,
|
|
1509
|
+
originalHeight: origH,
|
|
1510
|
+
offsetX: offX,
|
|
1511
|
+
offsetY: offY,
|
|
1098
1512
|
resource,
|
|
1099
1513
|
trimBuffer: trimBuf,
|
|
1514
|
+
rasterizedBuffer,
|
|
1100
1515
|
sourceKind: 'image',
|
|
1101
1516
|
});
|
|
1102
1517
|
}
|
|
@@ -1107,7 +1522,7 @@ async function _collectMovieClipFrames(
|
|
|
1107
1522
|
resource: MovieClipResource,
|
|
1108
1523
|
pkg: Package,
|
|
1109
1524
|
inputs: InputItem[],
|
|
1110
|
-
encoder:
|
|
1525
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1111
1526
|
options: AtlasOptions,
|
|
1112
1527
|
logger: ILogger,
|
|
1113
1528
|
): Promise<void> {
|
|
@@ -1197,7 +1612,7 @@ async function _createMovieClipFrameInput(
|
|
|
1197
1612
|
buffer: Uint8Array,
|
|
1198
1613
|
itemId: string,
|
|
1199
1614
|
resource: MovieClipResource,
|
|
1200
|
-
encoder:
|
|
1615
|
+
encoder: AtlasRasterBackend | undefined,
|
|
1201
1616
|
): Promise<InputItem | null> {
|
|
1202
1617
|
if (!encoder || buffer.length === 0) return null;
|
|
1203
1618
|
try {
|
|
@@ -1270,10 +1685,7 @@ function _findPngEnd(data: Uint8Array, start: number): number {
|
|
|
1270
1685
|
pos += 8;
|
|
1271
1686
|
if (pos + length + 4 > data.length) return -1;
|
|
1272
1687
|
const isIEND =
|
|
1273
|
-
data[pos - 4] === 0x49 &&
|
|
1274
|
-
data[pos - 3] === 0x45 &&
|
|
1275
|
-
data[pos - 2] === 0x4e &&
|
|
1276
|
-
data[pos - 1] === 0x44;
|
|
1688
|
+
data[pos - 4] === 0x49 && data[pos - 3] === 0x45 && data[pos - 2] === 0x4e && data[pos - 1] === 0x44;
|
|
1277
1689
|
pos += length + 4;
|
|
1278
1690
|
if (isIEND) return pos;
|
|
1279
1691
|
}
|
|
@@ -1404,7 +1816,7 @@ function _readUint16BE(data: Uint8Array, offset: number): number {
|
|
|
1404
1816
|
function _readUint32BE(data: Uint8Array, offset: number): number {
|
|
1405
1817
|
if (offset + 3 >= data.length) return 0;
|
|
1406
1818
|
return (
|
|
1407
|
-
|
|
1819
|
+
data[offset] * 0x1000000 +
|
|
1408
1820
|
((data[offset + 1] ?? 0) << 16) +
|
|
1409
1821
|
((data[offset + 2] ?? 0) << 8) +
|
|
1410
1822
|
(data[offset + 3] ?? 0)
|
|
@@ -1467,27 +1879,53 @@ async function _collectFontTexture(
|
|
|
1467
1879
|
.setChannel(item.channel);
|
|
1468
1880
|
fontRes.addGlyph(glyph);
|
|
1469
1881
|
}
|
|
1470
|
-
} catch {
|
|
1882
|
+
} catch {
|
|
1883
|
+
/* .fnt not found */
|
|
1884
|
+
}
|
|
1471
1885
|
}
|
|
1472
1886
|
}
|
|
1473
1887
|
|
|
1474
1888
|
/** Parse a BMFont .fnt text file into structured data for binary encoding. */
|
|
1475
1889
|
function _parseFnt(text: string): {
|
|
1476
|
-
hasFace: boolean;
|
|
1477
|
-
|
|
1890
|
+
hasFace: boolean;
|
|
1891
|
+
colored: boolean;
|
|
1892
|
+
resizable: boolean;
|
|
1893
|
+
hasChannel: boolean;
|
|
1894
|
+
fontSize: number;
|
|
1895
|
+
xadvance: number;
|
|
1896
|
+
lineHeight: number;
|
|
1478
1897
|
glyphs: Array<{
|
|
1479
|
-
charId: number;
|
|
1480
|
-
|
|
1481
|
-
|
|
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;
|
|
1482
1908
|
}>;
|
|
1483
1909
|
} {
|
|
1484
1910
|
const lines = text.split(/\r?\n/);
|
|
1485
|
-
let hasFace = false,
|
|
1486
|
-
|
|
1911
|
+
let hasFace = false,
|
|
1912
|
+
colored = false,
|
|
1913
|
+
resizable = false,
|
|
1914
|
+
hasChannel = false;
|
|
1915
|
+
let fontSize = 0,
|
|
1916
|
+
globalXadvance = 0,
|
|
1917
|
+
lineHeight = 0;
|
|
1487
1918
|
const glyphs: Array<{
|
|
1488
|
-
charId: number;
|
|
1489
|
-
|
|
1490
|
-
|
|
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;
|
|
1491
1929
|
}> = [];
|
|
1492
1930
|
|
|
1493
1931
|
for (const line of lines) {
|
|
@@ -1522,7 +1960,8 @@ function _parseFnt(text: string): {
|
|
|
1522
1960
|
const chnl = parseInt(attrs.chnl, 10) || 0;
|
|
1523
1961
|
if (chnl !== 0 && chnl !== 15) hasChannel = true;
|
|
1524
1962
|
glyphs.push({
|
|
1525
|
-
charId,
|
|
1963
|
+
charId,
|
|
1964
|
+
img,
|
|
1526
1965
|
x: parseInt(attrs.x, 10) || 0,
|
|
1527
1966
|
y: parseInt(attrs.y, 10) || 0,
|
|
1528
1967
|
xoffset: parseInt(attrs.xoffset, 10) || 0,
|
|
@@ -1537,7 +1976,16 @@ function _parseFnt(text: string): {
|
|
|
1537
1976
|
}
|
|
1538
1977
|
}
|
|
1539
1978
|
|
|
1540
|
-
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
|
+
};
|
|
1541
1989
|
}
|
|
1542
1990
|
|
|
1543
1991
|
function isComponentResource(resource: PackageResource): resource is Component {
|
|
@@ -1590,6 +2038,6 @@ function addUiResourceRefsFromUnknown(target: Set<string>, value: unknown): void
|
|
|
1590
2038
|
}
|
|
1591
2039
|
}
|
|
1592
2040
|
|
|
1593
|
-
function isResolvedBuffer(value: Uint8Array |
|
|
2041
|
+
function isResolvedBuffer(value: Uint8Array | AtlasRasterResolvedBuffer): value is AtlasRasterResolvedBuffer {
|
|
1594
2042
|
return typeof value === 'object' && value !== null && 'data' in value && 'info' in value;
|
|
1595
2043
|
}
|