@openfairygui/functions 0.2.0-alpha.19 → 0.2.0-alpha.20
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 +7 -2
- package/dist/index.cjs +3 -920
- package/dist/index.d.cts +2 -46
- package/dist/index.d.ts +3 -47
- package/dist/index.js +2 -919
- package/dist/node.cjs +138 -10
- package/dist/node.d.cts +7 -2
- package/dist/node.d.ts +8 -3
- package/dist/node.js +138 -11
- package/dist/{publish-DsPK0SJ1.js → publish-BJ_eelME.js} +1973 -2070
- package/dist/{publish-D7268_u4.cjs → publish-xFWT9Slz.cjs} +1973 -2070
- package/dist/restore-BW2xacB3.cjs +936 -0
- package/dist/{codegen-B8ZM1F4j.d.cts → restore-BeWaJNjR.d.cts} +49 -3
- package/dist/restore-Clk62n0O.js +931 -0
- package/dist/{codegen-CfbDHuFt.d.ts → restore-Dh0-Nvms.d.ts} +50 -4
- package/dist/web.cjs +4 -2
- package/dist/web.d.ts +1 -1
- package/dist/web.js +4 -2
- package/package.json +5 -2
- package/src/adapters/node/restore.ts +187 -0
- package/src/adapters/web/publish.ts +1 -247
- package/src/adapters/web/raster.ts +251 -0
- package/src/atlas/font.ts +95 -0
- package/src/atlas/inputs.ts +515 -0
- package/src/atlas/jta.ts +211 -0
- package/src/atlas/packing.ts +767 -0
- package/src/atlas.ts +23 -1639
- package/src/node.ts +4 -0
- package/src/publish/external-resources.ts +117 -0
- package/src/publish/options.ts +180 -0
- package/src/publish/package-context.ts +608 -0
- package/src/publish/resource-references.ts +210 -0
- package/src/publish.ts +21 -1130
- package/src/restore-internals/font.ts +100 -0
- package/src/restore-internals/movie-clip.ts +104 -0
- package/src/restore-internals/output-transaction.ts +164 -0
- package/src/restore.ts +14 -329
- /package/dist/{atlas-CDn6TirX.d.ts → atlas-C6tbl7nn.d.ts} +0 -0
|
@@ -42,1962 +42,2022 @@ function parseTextureSetMode(value) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
//#endregion
|
|
45
|
-
//#region src/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
var MaxRectsCompat = class MaxRectsCompat {
|
|
60
|
-
static helperRect = createNodeRect();
|
|
61
|
-
binWidth = 0;
|
|
62
|
-
binHeight = 0;
|
|
63
|
-
allowRotations = false;
|
|
64
|
-
usedRectangles = [];
|
|
65
|
-
freeRectangles = [];
|
|
66
|
-
init(width, height, allowRotations = false) {
|
|
67
|
-
this.binWidth = width;
|
|
68
|
-
this.binHeight = height;
|
|
69
|
-
this.allowRotations = allowRotations;
|
|
70
|
-
this.usedRectangles.length = 0;
|
|
71
|
-
this.freeRectangles.length = 0;
|
|
72
|
-
this.freeRectangles.push({
|
|
73
|
-
...createNodeRect(),
|
|
74
|
-
x: 0,
|
|
75
|
-
y: 0,
|
|
76
|
-
width,
|
|
77
|
-
height
|
|
78
|
-
});
|
|
45
|
+
//#region src/publish/resource-references.ts
|
|
46
|
+
function addReference(target, ownerPackageId, packageId, resourceId) {
|
|
47
|
+
if (!packageId || !resourceId) return;
|
|
48
|
+
if (packageId === ownerPackageId) target.localResourceIds.add(resourceId);
|
|
49
|
+
else target.packageIds.add(packageId);
|
|
50
|
+
}
|
|
51
|
+
function addUiReference(target, ownerPackageId, value) {
|
|
52
|
+
if (!value?.startsWith("ui://")) return;
|
|
53
|
+
const reference = value.slice(5);
|
|
54
|
+
const slashIndex = reference.indexOf("/");
|
|
55
|
+
if (slashIndex >= 0) {
|
|
56
|
+
addReference(target, ownerPackageId, reference.slice(0, slashIndex), reference.slice(slashIndex + 1));
|
|
57
|
+
return;
|
|
79
58
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
59
|
+
if (reference.length > 8) addReference(target, ownerPackageId, reference.slice(0, 8), reference.slice(8));
|
|
60
|
+
}
|
|
61
|
+
function addTextReferences(target, ownerPackageId, value) {
|
|
62
|
+
if (!value) return;
|
|
63
|
+
for (const match of value.matchAll(/ui:\/\/([0-9a-z]{8})\/?([0-9a-z]+)/giu)) addReference(target, ownerPackageId, match[1] ?? "", match[2] ?? "");
|
|
64
|
+
}
|
|
65
|
+
function addUnknownReferences(target, ownerPackageId, value) {
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
for (const entry of value) addUnknownReferences(target, ownerPackageId, entry);
|
|
68
|
+
return;
|
|
86
69
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let bestIndex = -1;
|
|
91
|
-
const bestNode = createNodeRect();
|
|
92
|
-
bestNode.score1 = MAX_SCORE;
|
|
93
|
-
bestNode.score2 = MAX_SCORE;
|
|
94
|
-
for (let index = 0; index < remaining.length; index += 1) {
|
|
95
|
-
const candidate = this.scoreRect(remaining[index], method);
|
|
96
|
-
if (candidate.score1 < bestNode.score1 || candidate.score1 === bestNode.score1 && candidate.score2 < bestNode.score2) {
|
|
97
|
-
copyNodeRect(bestNode, candidate);
|
|
98
|
-
bestIndex = index;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (bestIndex === -1) break;
|
|
102
|
-
this.placeRect(bestNode);
|
|
103
|
-
remaining.splice(bestIndex, 1);
|
|
104
|
-
}
|
|
105
|
-
const result = this.getResult();
|
|
106
|
-
result.remainingRects = remaining;
|
|
107
|
-
return result;
|
|
70
|
+
if (typeof value === "string") {
|
|
71
|
+
addUiReference(target, ownerPackageId, value);
|
|
72
|
+
addTextReferences(target, ownerPackageId, value);
|
|
108
73
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for (const
|
|
113
|
-
|
|
114
|
-
height = Math.max(height, rect.y + rect.height);
|
|
115
|
-
}
|
|
116
|
-
return {
|
|
117
|
-
outputRects: this.usedRectangles.map(cloneNodeRect),
|
|
118
|
-
remainingRects: [],
|
|
119
|
-
occupancy: this.getOccupancy(),
|
|
120
|
-
width,
|
|
121
|
-
height
|
|
122
|
-
};
|
|
74
|
+
}
|
|
75
|
+
function addFontReferences(target, ownerPackageId, value) {
|
|
76
|
+
if (Array.isArray(value)) {
|
|
77
|
+
for (const entry of value) addUiReference(target, ownerPackageId, entry);
|
|
78
|
+
return;
|
|
123
79
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
80
|
+
addUiReference(target, ownerPackageId, value);
|
|
81
|
+
}
|
|
82
|
+
function collectComponentReferences(target, ownerPackageId, component) {
|
|
83
|
+
const referenceComponent = component;
|
|
84
|
+
for (const child of referenceComponent.listChildren()) {
|
|
85
|
+
const sourceId = child.getSrc?.();
|
|
86
|
+
if (sourceId) if (sourceId.startsWith("ui://")) addUiReference(target, ownerPackageId, sourceId);
|
|
87
|
+
else target.localResourceIds.add(sourceId);
|
|
88
|
+
const sourcePackageId = child.getPackageId?.()?.trim();
|
|
89
|
+
if (sourcePackageId && sourcePackageId !== ownerPackageId) target.packageIds.add(sourcePackageId);
|
|
90
|
+
addFontReferences(target, ownerPackageId, child.getFont?.());
|
|
91
|
+
addTextReferences(target, ownerPackageId, child.getText?.());
|
|
92
|
+
for (const reference of [
|
|
93
|
+
child.getUrl?.(),
|
|
94
|
+
child.getDefaultItem?.(),
|
|
95
|
+
child.getIcon?.(),
|
|
96
|
+
child.getSelectedIcon?.(),
|
|
97
|
+
child.getDropdown?.(),
|
|
98
|
+
child.getSound?.(),
|
|
99
|
+
child.getInstanceSound?.(),
|
|
100
|
+
child.getInstanceIcon?.(),
|
|
101
|
+
child.getInstanceSelectedIcon?.(),
|
|
102
|
+
child.getVtScrollBarRes?.(),
|
|
103
|
+
child.getHzScrollBarRes?.(),
|
|
104
|
+
child.getHeaderRes?.(),
|
|
105
|
+
child.getFooterRes?.()
|
|
106
|
+
]) addUiReference(target, ownerPackageId, reference);
|
|
107
|
+
for (const item of child.getInstanceComboItems?.() ?? []) addUiReference(target, ownerPackageId, item.icon);
|
|
108
|
+
for (const item of child.getListItems?.() ?? []) {
|
|
109
|
+
addUiReference(target, ownerPackageId, item.icon);
|
|
110
|
+
addUiReference(target, ownerPackageId, item.url);
|
|
111
|
+
}
|
|
112
|
+
for (const gear of child.listGears?.() ?? []) {
|
|
113
|
+
addUnknownReferences(target, ownerPackageId, gear.getValues?.());
|
|
114
|
+
addUnknownReferences(target, ownerPackageId, gear.getDefaultValue?.());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
addFontReferences(target, ownerPackageId, referenceComponent.getFont?.());
|
|
118
|
+
for (const reference of [
|
|
119
|
+
referenceComponent.getDropdown?.(),
|
|
120
|
+
referenceComponent.getHeaderRes?.(),
|
|
121
|
+
referenceComponent.getFooterRes?.(),
|
|
122
|
+
referenceComponent.getVtScrollBarRes?.(),
|
|
123
|
+
referenceComponent.getHzScrollBarRes?.(),
|
|
124
|
+
referenceComponent.getSound?.()
|
|
125
|
+
]) addUiReference(target, ownerPackageId, reference);
|
|
126
|
+
for (const transition of referenceComponent.listTransitions?.() ?? []) for (const item of transition.listItems?.() ?? []) {
|
|
127
|
+
addUnknownReferences(target, ownerPackageId, item.getStartValue?.());
|
|
128
|
+
addUnknownReferences(target, ownerPackageId, item.getEndValue?.());
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Enumerates package-local resource IDs and external package IDs referenced by
|
|
133
|
+
* component content. Callers retain policy decisions such as atlas selection
|
|
134
|
+
* and dependency ordering.
|
|
135
|
+
*/
|
|
136
|
+
function collectPackageResourceReferences(pkg) {
|
|
137
|
+
const references = {
|
|
138
|
+
localResourceIds: /* @__PURE__ */ new Set(),
|
|
139
|
+
packageIds: /* @__PURE__ */ new Set()
|
|
140
|
+
};
|
|
141
|
+
for (const resource of pkg.listResources()) if (resource.propertyType === "Component") collectComponentReferences(references, pkg.getId(), resource);
|
|
142
|
+
return references;
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/atlas/font.ts
|
|
146
|
+
/** Parse a BMFont .fnt text file into structured data for binary encoding. */
|
|
147
|
+
function parseFnt(text) {
|
|
148
|
+
const lines = text.split(/\r?\n/);
|
|
149
|
+
let hasFace = false;
|
|
150
|
+
let colored = false;
|
|
151
|
+
let resizable = false;
|
|
152
|
+
let hasChannel = false;
|
|
153
|
+
let fontSize = 0;
|
|
154
|
+
let globalXadvance = 0;
|
|
155
|
+
let lineHeight = 0;
|
|
156
|
+
const glyphs = [];
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const trimmed = line.trim();
|
|
159
|
+
if (!trimmed) continue;
|
|
160
|
+
const parts = trimmed.split(/\s+/);
|
|
161
|
+
const attrs = {};
|
|
162
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
163
|
+
const entry = parts[index]?.split("=") ?? [];
|
|
164
|
+
if (entry.length === 2 && entry[0]) attrs[entry[0]] = entry[1] ?? "";
|
|
133
165
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
switch (method) {
|
|
142
|
-
case MAX_RECTS_METHOD.BestShortSideFit:
|
|
143
|
-
newNode = this.findPositionForNewNodeBestShortSideFit(rect.width, rect.height, allowRotation(rect));
|
|
144
|
-
break;
|
|
145
|
-
case MAX_RECTS_METHOD.BestLongSideFit:
|
|
146
|
-
newNode = this.findPositionForNewNodeBestLongSideFit(rect.width, rect.height, allowRotation(rect));
|
|
147
|
-
break;
|
|
148
|
-
case MAX_RECTS_METHOD.BestAreaFit:
|
|
149
|
-
newNode = this.findPositionForNewNodeBestAreaFit(rect.width, rect.height, allowRotation(rect));
|
|
150
|
-
break;
|
|
151
|
-
case MAX_RECTS_METHOD.BottomLeftRule:
|
|
152
|
-
newNode = this.findPositionForNewNodeBottomLeft(rect.width, rect.height, allowRotation(rect));
|
|
166
|
+
switch (parts[0]) {
|
|
167
|
+
case "info":
|
|
168
|
+
hasFace = attrs.face != null;
|
|
169
|
+
colored = hasFace;
|
|
170
|
+
if (attrs.colored !== void 0) colored = attrs.colored === "true";
|
|
171
|
+
fontSize = parseInt(attrs.size ?? "", 10) || 0;
|
|
172
|
+
resizable = attrs.resizable === "true";
|
|
153
173
|
break;
|
|
154
|
-
case
|
|
155
|
-
|
|
156
|
-
|
|
174
|
+
case "common":
|
|
175
|
+
lineHeight = parseInt(attrs.lineHeight ?? "", 10) || 0;
|
|
176
|
+
globalXadvance = parseInt(attrs.xadvance ?? "", 10) || 0;
|
|
177
|
+
if (fontSize === 0) fontSize = lineHeight;
|
|
178
|
+
else if (lineHeight === 0) lineHeight = fontSize;
|
|
157
179
|
break;
|
|
158
|
-
|
|
159
|
-
|
|
180
|
+
case "char": {
|
|
181
|
+
const charId = parseInt(attrs.id ?? "", 10) || 0;
|
|
182
|
+
if (charId === 0) continue;
|
|
183
|
+
const img = attrs.img || null;
|
|
184
|
+
if (!hasFace && !img) continue;
|
|
185
|
+
const channel = parseInt(attrs.chnl ?? "", 10) || 0;
|
|
186
|
+
if (channel !== 0 && channel !== 15) hasChannel = true;
|
|
187
|
+
glyphs.push({
|
|
188
|
+
charId,
|
|
189
|
+
img,
|
|
190
|
+
x: parseInt(attrs.x ?? "", 10) || 0,
|
|
191
|
+
y: parseInt(attrs.y ?? "", 10) || 0,
|
|
192
|
+
xoffset: parseInt(attrs.xoffset ?? "", 10) || 0,
|
|
193
|
+
yoffset: parseInt(attrs.yoffset ?? "", 10) || 0,
|
|
194
|
+
width: parseInt(attrs.width ?? "", 10) || 0,
|
|
195
|
+
height: parseInt(attrs.height ?? "", 10) || 0,
|
|
196
|
+
xadvance: parseInt(attrs.xadvance ?? "", 10) || 0,
|
|
197
|
+
channel
|
|
198
|
+
});
|
|
160
199
|
break;
|
|
161
|
-
}
|
|
162
|
-
if (newNode.height === 0) {
|
|
163
|
-
newNode.score1 = MAX_SCORE;
|
|
164
|
-
newNode.score2 = MAX_SCORE;
|
|
165
|
-
}
|
|
166
|
-
newNode.index = rect.index;
|
|
167
|
-
newNode.subIndex = rect.subIndex;
|
|
168
|
-
newNode.flags = rect.flags;
|
|
169
|
-
newNode.sourceKind = rect.sourceKind;
|
|
170
|
-
return cloneNodeRect(newNode);
|
|
171
|
-
}
|
|
172
|
-
findPositionForNewNodeBottomLeft(width, height, allowRectRotation) {
|
|
173
|
-
const bestNode = MaxRectsCompat.helperRect;
|
|
174
|
-
bestNode.score1 = MAX_SCORE;
|
|
175
|
-
bestNode.score2 = 0;
|
|
176
|
-
for (const freeRect of this.freeRectangles) {
|
|
177
|
-
if (freeRect.width >= width && freeRect.height >= height) {
|
|
178
|
-
const topSideY = freeRect.y + height;
|
|
179
|
-
if (topSideY < bestNode.score1 || topSideY === bestNode.score1 && freeRect.x < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, topSideY, freeRect.x);
|
|
180
|
-
}
|
|
181
|
-
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
182
|
-
const topSideY = freeRect.y + width;
|
|
183
|
-
if (topSideY < bestNode.score1 || topSideY === bestNode.score1 && freeRect.x < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, topSideY, freeRect.x);
|
|
184
200
|
}
|
|
185
201
|
}
|
|
186
|
-
return bestNode;
|
|
187
202
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
203
|
+
return {
|
|
204
|
+
hasFace,
|
|
205
|
+
colored,
|
|
206
|
+
resizable: fontSize > 0 ? resizable : false,
|
|
207
|
+
hasChannel,
|
|
208
|
+
fontSize,
|
|
209
|
+
xadvance: globalXadvance,
|
|
210
|
+
lineHeight,
|
|
211
|
+
glyphs
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/atlas/jta.ts
|
|
216
|
+
const PNG_SIGNATURE = new Uint8Array([
|
|
217
|
+
137,
|
|
218
|
+
80,
|
|
219
|
+
78,
|
|
220
|
+
71,
|
|
221
|
+
13,
|
|
222
|
+
10,
|
|
223
|
+
26,
|
|
224
|
+
10
|
|
225
|
+
]);
|
|
226
|
+
function extractJtaFrames(data) {
|
|
227
|
+
const frames = [];
|
|
228
|
+
let offset = 0;
|
|
229
|
+
let firstPngOffset = -1;
|
|
230
|
+
while (offset < data.length) {
|
|
231
|
+
const signatureIndex = findPngSignature(data, offset);
|
|
232
|
+
if (signatureIndex === -1) break;
|
|
233
|
+
if (firstPngOffset === -1) firstPngOffset = signatureIndex;
|
|
234
|
+
const end = findPngEnd(data, signatureIndex);
|
|
235
|
+
if (end === -1) break;
|
|
236
|
+
frames.push(data.subarray(signatureIndex, end));
|
|
237
|
+
offset = end;
|
|
209
238
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
223
|
-
const leftoverHoriz = Math.abs(freeRect.width - height);
|
|
224
|
-
const leftoverVert = Math.abs(freeRect.height - width);
|
|
225
|
-
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
226
|
-
const longSideFit = Math.max(leftoverHoriz, leftoverVert);
|
|
227
|
-
if (longSideFit < bestNode.score2 || longSideFit === bestNode.score2 && shortSideFit < bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, shortSideFit, longSideFit);
|
|
228
|
-
}
|
|
239
|
+
if (firstPngOffset === -1 || frames.length === 0) return { frames: [] };
|
|
240
|
+
return {
|
|
241
|
+
frames,
|
|
242
|
+
meta: parseJtaHeader(data, firstPngOffset, frames.length)
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function findPngSignature(data, fromIndex) {
|
|
246
|
+
for (let index = fromIndex; index <= data.length - PNG_SIGNATURE.length; index += 1) {
|
|
247
|
+
let matched = true;
|
|
248
|
+
for (let signatureIndex = 0; signatureIndex < PNG_SIGNATURE.length; signatureIndex += 1) if (data[index + signatureIndex] !== PNG_SIGNATURE[signatureIndex]) {
|
|
249
|
+
matched = false;
|
|
250
|
+
break;
|
|
229
251
|
}
|
|
230
|
-
return
|
|
252
|
+
if (matched) return index;
|
|
231
253
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
245
|
-
const leftoverHoriz = Math.abs(freeRect.width - height);
|
|
246
|
-
const leftoverVert = Math.abs(freeRect.height - width);
|
|
247
|
-
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
248
|
-
if (areaFit < bestNode.score1 || areaFit === bestNode.score1 && shortSideFit < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, areaFit, shortSideFit);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return bestNode;
|
|
252
|
-
}
|
|
253
|
-
findPositionForNewNodeContactPoint(width, height, allowRectRotation) {
|
|
254
|
-
const bestNode = MaxRectsCompat.helperRect;
|
|
255
|
-
bestNode.score1 = -1;
|
|
256
|
-
bestNode.score2 = 0;
|
|
257
|
-
for (const freeRect of this.freeRectangles) {
|
|
258
|
-
if (freeRect.width >= width && freeRect.height >= height) {
|
|
259
|
-
const score = this.contactPointScoreNode(freeRect.x, freeRect.y, width, height);
|
|
260
|
-
if (score > bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, score, bestNode.score2);
|
|
261
|
-
}
|
|
262
|
-
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
263
|
-
const score = this.contactPointScoreNode(freeRect.x, freeRect.y, height, width);
|
|
264
|
-
if (score > bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, score, bestNode.score2);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return bestNode;
|
|
268
|
-
}
|
|
269
|
-
contactPointScoreNode(x, y, width, height) {
|
|
270
|
-
let score = 0;
|
|
271
|
-
if (x === 0 || x + width === this.binWidth) score += height;
|
|
272
|
-
if (y === 0 || y + height === this.binHeight) score += width;
|
|
273
|
-
for (const rect of this.usedRectangles) {
|
|
274
|
-
if (rect.x === x + width || rect.x + rect.width === x) score += commonIntervalLength(rect.y, rect.y + rect.height, y, y + height);
|
|
275
|
-
if (rect.y === y + height || rect.y + rect.height === y) score += commonIntervalLength(rect.x, rect.x + rect.width, x, x + width);
|
|
276
|
-
}
|
|
277
|
-
return score;
|
|
278
|
-
}
|
|
279
|
-
splitFreeNode(freeNode, usedNode) {
|
|
280
|
-
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x || usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y) return false;
|
|
281
|
-
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
|
|
282
|
-
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
|
|
283
|
-
const newNode = cloneNodeRect(freeNode);
|
|
284
|
-
newNode.height = usedNode.y - newNode.y;
|
|
285
|
-
this.freeRectangles.push(newNode);
|
|
286
|
-
}
|
|
287
|
-
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
|
|
288
|
-
const newNode = cloneNodeRect(freeNode);
|
|
289
|
-
newNode.y = usedNode.y + usedNode.height;
|
|
290
|
-
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
|
|
291
|
-
this.freeRectangles.push(newNode);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
|
|
295
|
-
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
|
|
296
|
-
const newNode = cloneNodeRect(freeNode);
|
|
297
|
-
newNode.width = usedNode.x - newNode.x;
|
|
298
|
-
this.freeRectangles.push(newNode);
|
|
299
|
-
}
|
|
300
|
-
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
|
|
301
|
-
const newNode = cloneNodeRect(freeNode);
|
|
302
|
-
newNode.x = usedNode.x + usedNode.width;
|
|
303
|
-
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
|
|
304
|
-
this.freeRectangles.push(newNode);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return true;
|
|
254
|
+
return -1;
|
|
255
|
+
}
|
|
256
|
+
function findPngEnd(data, start) {
|
|
257
|
+
let position = start + PNG_SIGNATURE.length;
|
|
258
|
+
while (position + 8 <= data.length) {
|
|
259
|
+
const length = readUint32BE(data, position);
|
|
260
|
+
position += 8;
|
|
261
|
+
if (position + length + 4 > data.length) return -1;
|
|
262
|
+
const isEnd = data[position - 4] === 73 && data[position - 3] === 69 && data[position - 2] === 78 && data[position - 1] === 68;
|
|
263
|
+
position += length + 4;
|
|
264
|
+
if (isEnd) return position;
|
|
308
265
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
266
|
+
return -1;
|
|
267
|
+
}
|
|
268
|
+
function parseJtaHeader(data, firstPngOffset, frameCount) {
|
|
269
|
+
if (data.length < 10) return void 0;
|
|
270
|
+
const state = { offset: 0 };
|
|
271
|
+
const end = Math.min(firstPngOffset, data.length);
|
|
272
|
+
if (!readUtfBE(data, state, end)) return void 0;
|
|
273
|
+
const version = readInt32BEAt(data, state, end);
|
|
274
|
+
if (version == null) return void 0;
|
|
275
|
+
const fpsRaw = readInt8At(data, state, end);
|
|
276
|
+
if (fpsRaw == null) return void 0;
|
|
277
|
+
const fps = fpsRaw > 0 ? fpsRaw : 24;
|
|
278
|
+
if (state.offset + 3 > end) return void 0;
|
|
279
|
+
state.offset += 3;
|
|
280
|
+
if (version < 102) return void 0;
|
|
281
|
+
readUint16BEAt(data, state, end);
|
|
282
|
+
readUint16BEAt(data, state, end);
|
|
283
|
+
const width = readUint16BEAt(data, state, end);
|
|
284
|
+
const height = readUint16BEAt(data, state, end);
|
|
285
|
+
if (width == null || height == null) return void 0;
|
|
286
|
+
const speedRaw = readUint8At(data, state, end);
|
|
287
|
+
const repeatDelayRaw = readUint8At(data, state, end);
|
|
288
|
+
const swingRaw = readInt8At(data, state, end);
|
|
289
|
+
const frameTableCount = readInt16BEAt(data, state, end);
|
|
290
|
+
if (speedRaw == null || repeatDelayRaw == null || swingRaw == null || frameTableCount == null) return void 0;
|
|
291
|
+
const frames = [];
|
|
292
|
+
for (let index = 0; index < frameTableCount; index += 1) {
|
|
293
|
+
const delayRaw = readInt16BEAt(data, state, end);
|
|
294
|
+
const offsetX = readInt16BEAt(data, state, end);
|
|
295
|
+
const offsetY = readInt16BEAt(data, state, end);
|
|
296
|
+
const frameWidth = readInt16BEAt(data, state, end);
|
|
297
|
+
const frameHeight = readInt16BEAt(data, state, end);
|
|
298
|
+
const textureIndex = readInt16BEAt(data, state, end);
|
|
299
|
+
if (delayRaw == null || offsetX == null || offsetY == null || frameWidth == null || frameHeight == null || textureIndex == null) break;
|
|
300
|
+
frames.push({
|
|
301
|
+
addDelay: Math.trunc(1e3 / fps * delayRaw),
|
|
302
|
+
offsetX,
|
|
303
|
+
offsetY,
|
|
304
|
+
width: frameWidth,
|
|
305
|
+
height: frameHeight,
|
|
306
|
+
textureIndex
|
|
307
|
+
});
|
|
328
308
|
}
|
|
329
|
-
};
|
|
330
|
-
function createNodeRect() {
|
|
331
309
|
return {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
subIndex: -1,
|
|
339
|
-
flags: 0,
|
|
340
|
-
score1: 0,
|
|
341
|
-
score2: 0,
|
|
342
|
-
sourceKind: void 0
|
|
310
|
+
interval: Math.trunc(1e3 / fps * (speedRaw || 1)),
|
|
311
|
+
repeatDelay: Math.trunc(1e3 / fps * repeatDelayRaw),
|
|
312
|
+
swing: swingRaw === 1,
|
|
313
|
+
width,
|
|
314
|
+
height,
|
|
315
|
+
frames: frames.length === 0 && frameCount > 0 ? [] : frames
|
|
343
316
|
};
|
|
344
317
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
318
|
+
function readUtfBE(data, state, end) {
|
|
319
|
+
const length = readUint16BEAt(data, state, end);
|
|
320
|
+
if (length == null || state.offset + length > end) return null;
|
|
321
|
+
const value = new TextDecoder().decode(data.subarray(state.offset, state.offset + length));
|
|
322
|
+
state.offset += length;
|
|
323
|
+
return value;
|
|
347
324
|
}
|
|
348
|
-
function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
target.rotated = source.rotated;
|
|
354
|
-
target.index = source.index;
|
|
355
|
-
target.subIndex = source.subIndex;
|
|
356
|
-
target.flags = source.flags;
|
|
357
|
-
target.score1 = source.score1;
|
|
358
|
-
target.score2 = source.score2;
|
|
359
|
-
target.sourceKind = source.sourceKind;
|
|
325
|
+
function readUint8At(data, state, end) {
|
|
326
|
+
if (state.offset + 1 > end) return null;
|
|
327
|
+
const value = data[state.offset];
|
|
328
|
+
state.offset += 1;
|
|
329
|
+
return value ?? 0;
|
|
360
330
|
}
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
target.rotated = rotated;
|
|
367
|
-
target.score1 = score1;
|
|
368
|
-
target.score2 = score2;
|
|
331
|
+
function readInt8At(data, state, end) {
|
|
332
|
+
if (state.offset + 1 > end) return null;
|
|
333
|
+
const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt8(state.offset);
|
|
334
|
+
state.offset += 1;
|
|
335
|
+
return value;
|
|
369
336
|
}
|
|
370
|
-
function
|
|
371
|
-
|
|
337
|
+
function readUint16BEAt(data, state, end) {
|
|
338
|
+
if (state.offset + 2 > end) return null;
|
|
339
|
+
const value = readUint16BE(data, state.offset);
|
|
340
|
+
state.offset += 2;
|
|
341
|
+
return value;
|
|
372
342
|
}
|
|
373
|
-
function
|
|
374
|
-
if (
|
|
375
|
-
|
|
343
|
+
function readInt16BEAt(data, state, end) {
|
|
344
|
+
if (state.offset + 2 > end) return null;
|
|
345
|
+
const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt16(state.offset, false);
|
|
346
|
+
state.offset += 2;
|
|
347
|
+
return value;
|
|
376
348
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
349
|
+
function readInt32BEAt(data, state, end) {
|
|
350
|
+
if (state.offset + 4 > end) return null;
|
|
351
|
+
const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt32(state.offset, false);
|
|
352
|
+
state.offset += 4;
|
|
353
|
+
return value;
|
|
354
|
+
}
|
|
355
|
+
function readUint16BE(data, offset) {
|
|
356
|
+
if (offset + 1 >= data.length) return 0;
|
|
357
|
+
return data[offset] << 8 | data[offset + 1];
|
|
358
|
+
}
|
|
359
|
+
function readUint32BE(data, offset) {
|
|
360
|
+
if (offset + 3 >= data.length) return 0;
|
|
361
|
+
return data[offset] * 16777216 + ((data[offset + 1] ?? 0) << 16) + ((data[offset + 2] ?? 0) << 8) + (data[offset + 3] ?? 0);
|
|
379
362
|
}
|
|
380
363
|
//#endregion
|
|
381
|
-
//#region src/
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
...settings
|
|
364
|
+
//#region src/atlas/inputs.ts
|
|
365
|
+
function getPublishedItemId(resource) {
|
|
366
|
+
return (resource.getExtras() ?? {})._publishedId ?? resource.getId();
|
|
367
|
+
}
|
|
368
|
+
function resolveFontFileName(fontName) {
|
|
369
|
+
return /\.fnt$/i.test(fontName) ? fontName : `${fontName}.fnt`;
|
|
370
|
+
}
|
|
371
|
+
function resolveImageFileName$1(resource) {
|
|
372
|
+
const extras = resource.getExtras();
|
|
373
|
+
return resource.getFileName() || extras._fileName || resource.getName();
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Trim transparent edges from an image using the host raster backend.
|
|
377
|
+
* Returns the trimmed buffer, dimensions, and offsets.
|
|
378
|
+
* Falls back to the original image if trim fails (e.g. no alpha channel, no transparent edges).
|
|
379
|
+
*/
|
|
380
|
+
async function _trimImage(encoder, input, originalWidth, originalHeight) {
|
|
381
|
+
try {
|
|
382
|
+
const trimResult = await encoder(input).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
383
|
+
if (!isResolvedBuffer(trimResult)) throw new Error("atlas: encoder raw alpha trim did not return resolved metadata.");
|
|
384
|
+
const { data, info } = trimResult;
|
|
385
|
+
const width = info.width;
|
|
386
|
+
const height = info.height;
|
|
387
|
+
const channels = info.channels || 4;
|
|
388
|
+
let minX = width;
|
|
389
|
+
let minY = height;
|
|
390
|
+
let maxX = -1;
|
|
391
|
+
let maxY = -1;
|
|
392
|
+
for (let y = 0; y < height; y += 1) for (let x = 0; x < width; x += 1) {
|
|
393
|
+
if ((data[(y * width + x) * channels + 3] ?? 0) === 0) continue;
|
|
394
|
+
if (x < minX) minX = x;
|
|
395
|
+
if (y < minY) minY = y;
|
|
396
|
+
if (x > maxX) maxX = x;
|
|
397
|
+
if (y > maxY) maxY = y;
|
|
398
|
+
}
|
|
399
|
+
if (maxX < minX || maxY < minY) return {
|
|
400
|
+
buffer: new Uint8Array(0),
|
|
401
|
+
width: 0,
|
|
402
|
+
height: 0,
|
|
403
|
+
offsetX: 0,
|
|
404
|
+
offsetY: 0,
|
|
405
|
+
originalWidth,
|
|
406
|
+
originalHeight
|
|
407
|
+
};
|
|
408
|
+
const trimmedWidth = maxX - minX + 1;
|
|
409
|
+
const trimmedHeight = maxY - minY + 1;
|
|
410
|
+
return {
|
|
411
|
+
buffer: await encoder(input).extract({
|
|
412
|
+
left: minX,
|
|
413
|
+
top: minY,
|
|
414
|
+
width: trimmedWidth,
|
|
415
|
+
height: trimmedHeight
|
|
416
|
+
}).toBuffer(),
|
|
417
|
+
width: trimmedWidth,
|
|
418
|
+
height: trimmedHeight,
|
|
419
|
+
offsetX: minX,
|
|
420
|
+
offsetY: minY,
|
|
421
|
+
originalWidth,
|
|
422
|
+
originalHeight
|
|
423
|
+
};
|
|
424
|
+
} catch {
|
|
425
|
+
return {
|
|
426
|
+
buffer: await encoder(input).png().toBuffer(),
|
|
427
|
+
width: originalWidth,
|
|
428
|
+
height: originalHeight,
|
|
429
|
+
offsetX: 0,
|
|
430
|
+
offsetY: 0,
|
|
431
|
+
originalWidth,
|
|
432
|
+
originalHeight
|
|
451
433
|
};
|
|
452
434
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
if (this.settings.square) {
|
|
483
|
-
const side = Math.max(page.width, page.height);
|
|
484
|
-
page.width = side;
|
|
485
|
-
page.height = side;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Resolve an ImageResource to its actual file path on disk.
|
|
438
|
+
*/
|
|
439
|
+
function resolveImagePath$1(resource, pkg, basePath) {
|
|
440
|
+
const imgPath = resource.getPath() ?? "/";
|
|
441
|
+
const fileName = resolveImageFileName$1(resource);
|
|
442
|
+
const branchName = resource.getBranch?.() ?? "";
|
|
443
|
+
const normalizedBasePath = basePath.replace(/[/\\]+$/, "");
|
|
444
|
+
return `${!branchName ? normalizedBasePath : /[\\/]assets$/i.test(normalizedBasePath) ? normalizedBasePath.replace(/([\\/])assets$/i, `$1assets_${branchName}`) : `${normalizedBasePath}_${branchName}`}/${pkg.getName()}${imgPath}${fileName}`;
|
|
445
|
+
}
|
|
446
|
+
/** Collect a single ImageResource into the inputs array. */
|
|
447
|
+
async function collectImage(resource, pkg, inputs, encoder, options, doTrim, logger) {
|
|
448
|
+
let origW = resource.getWidth() ?? 0;
|
|
449
|
+
let origH = resource.getHeight() ?? 0;
|
|
450
|
+
const declaredWidth = origW;
|
|
451
|
+
const declaredHeight = origH;
|
|
452
|
+
let sourceHasAlpha = false;
|
|
453
|
+
let rasterizedBuffer;
|
|
454
|
+
if (encoder && options.basePath) {
|
|
455
|
+
const filePath = resolveImagePath$1(resource, pkg, options.basePath);
|
|
456
|
+
try {
|
|
457
|
+
const metadata = await encoder(filePath).metadata();
|
|
458
|
+
if (origW === 0 || origH === 0) {
|
|
459
|
+
origW = metadata.width ?? 0;
|
|
460
|
+
origH = metadata.height ?? 0;
|
|
461
|
+
resource.setWidth(origW);
|
|
462
|
+
resource.setHeight(origH);
|
|
486
463
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (rect.width !== page.width) rect.x += Math.floor(padding / 2);
|
|
496
|
-
if (rect.height !== page.height) rect.y += Math.floor(padding / 2);
|
|
497
|
-
}
|
|
464
|
+
sourceHasAlpha = metadata.hasAlpha === true || metadata.channels === 4;
|
|
465
|
+
if (/\.svg$/i.test(resolveImageFileName$1(resource)) && declaredWidth > 0 && declaredHeight > 0) {
|
|
466
|
+
rasterizedBuffer = await encoder(filePath).resize({
|
|
467
|
+
width: declaredWidth,
|
|
468
|
+
height: declaredHeight,
|
|
469
|
+
fit: "fill"
|
|
470
|
+
}).png().toBuffer();
|
|
471
|
+
sourceHasAlpha = true;
|
|
498
472
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if (!sizeScheme) sizeScheme = initSizeScheme();
|
|
505
|
-
const edgePadding = this.settings.edgePadding ? this.settings.padding : 0;
|
|
506
|
-
let totalArea = 0;
|
|
507
|
-
for (const rect of rects) totalArea += rect.width * rect.height;
|
|
508
|
-
const candidates = sizeScheme.filter((entry) => entry.area >= totalArea && entry.width <= this.settings.maxWidth && entry.height <= this.settings.maxHeight);
|
|
509
|
-
if (candidates.length === 0) candidates.push({
|
|
510
|
-
width: this.settings.maxWidth,
|
|
511
|
-
height: this.settings.maxHeight,
|
|
512
|
-
area: 0,
|
|
513
|
-
aspectRatio: 0,
|
|
514
|
-
len: 0
|
|
515
|
-
});
|
|
516
|
-
let page = null;
|
|
517
|
-
let selectedWidth = 0;
|
|
518
|
-
let selectedHeight = 0;
|
|
519
|
-
for (let index = 0; index < candidates.length; index += 1) {
|
|
520
|
-
selectedWidth = candidates[index].width;
|
|
521
|
-
selectedHeight = candidates[index].height;
|
|
522
|
-
page = this.packAtSize(index !== candidates.length - 1, selectedWidth - edgePadding, selectedHeight - edgePadding, rects);
|
|
523
|
-
if (page) break;
|
|
524
|
-
}
|
|
525
|
-
if (page && !this.settings.pot && page.remainingRects.length === 0) {
|
|
526
|
-
let bestRefined = null;
|
|
527
|
-
if (this.settings.square) {
|
|
528
|
-
const search = new BinarySearchCompat(Math.min(selectedWidth / 2, selectedHeight / 2), Math.max(selectedWidth, selectedHeight), this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
529
|
-
let current = search.reset();
|
|
530
|
-
while (current !== -1) {
|
|
531
|
-
const refined = this.packAtSize(true, current - edgePadding, current - edgePadding, rects);
|
|
532
|
-
bestRefined = getBestPage(bestRefined, refined);
|
|
533
|
-
current = search.next(refined == null);
|
|
534
|
-
}
|
|
535
|
-
} else {
|
|
536
|
-
const widthSearch = new BinarySearchCompat(selectedWidth / 2, selectedWidth, this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
537
|
-
const heightSearch = new BinarySearchCompat(selectedHeight / 2, selectedHeight, this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
538
|
-
let currentHeight = heightSearch.reset();
|
|
539
|
-
let currentWidth = widthSearch.reset();
|
|
540
|
-
while (true) {
|
|
541
|
-
let bestForHeight = null;
|
|
542
|
-
while (currentWidth !== -1) {
|
|
543
|
-
const refined = this.packAtSize(true, currentWidth - edgePadding, currentHeight - edgePadding, rects);
|
|
544
|
-
bestForHeight = getBestPage(bestForHeight, refined);
|
|
545
|
-
currentWidth = widthSearch.next(refined == null);
|
|
546
|
-
}
|
|
547
|
-
bestRefined = getBestPage(bestRefined, bestForHeight);
|
|
548
|
-
currentHeight = heightSearch.next(bestForHeight == null);
|
|
549
|
-
if (currentHeight === -1) break;
|
|
550
|
-
currentWidth = widthSearch.reset();
|
|
551
|
-
}
|
|
473
|
+
} catch {
|
|
474
|
+
if (options.strictOutput) throw new Error(`atlas: Could not read image "${filePath}".`);
|
|
475
|
+
if (origW === 0 || origH === 0) {
|
|
476
|
+
logger.warn(`atlas: Could not read image "${filePath}", skipping.`);
|
|
477
|
+
return;
|
|
552
478
|
}
|
|
553
|
-
if (bestRefined) page = bestRefined;
|
|
554
479
|
}
|
|
555
|
-
return page;
|
|
556
480
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
while (index < rects.length) {
|
|
572
|
-
if (this.maxRects.insert(rects[index], method) == null) {
|
|
573
|
-
while (index < rects.length) {
|
|
574
|
-
remaining.push(cloneCompatRect(rects[index]));
|
|
575
|
-
index += 1;
|
|
576
|
-
}
|
|
577
|
-
break;
|
|
578
|
-
}
|
|
579
|
-
index += 1;
|
|
580
|
-
}
|
|
581
|
-
page = this.maxRects.getResult();
|
|
582
|
-
page.remainingRects = remaining;
|
|
583
|
-
}
|
|
584
|
-
if (!(requireFullFit && page.remainingRects.length > 0) && page.outputRects.length !== 0) best = getBestPage(best, page);
|
|
481
|
+
if (origW <= 0 || origH <= 0) return;
|
|
482
|
+
let packW = origW, packH = origH, offX = 0, offY = 0;
|
|
483
|
+
let trimBuf;
|
|
484
|
+
if (doTrim && sourceHasAlpha && options.basePath && encoder) {
|
|
485
|
+
const filePath = resolveImagePath$1(resource, pkg, options.basePath);
|
|
486
|
+
try {
|
|
487
|
+
const trimResult = await _trimImage(encoder, rasterizedBuffer ?? filePath, origW, origH);
|
|
488
|
+
packW = trimResult.width;
|
|
489
|
+
packH = trimResult.height;
|
|
490
|
+
offX = trimResult.offsetX;
|
|
491
|
+
offY = trimResult.offsetY;
|
|
492
|
+
trimBuf = trimResult.buffer;
|
|
493
|
+
} catch {
|
|
494
|
+
logger.warn(`atlas: Could not trim "${filePath}", using original.`);
|
|
585
495
|
}
|
|
586
|
-
return best;
|
|
587
496
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
497
|
+
inputs.push({
|
|
498
|
+
id: getPublishedItemId(resource),
|
|
499
|
+
width: packW,
|
|
500
|
+
height: packH,
|
|
501
|
+
originalWidth: origW,
|
|
502
|
+
originalHeight: origH,
|
|
503
|
+
offsetX: offX,
|
|
504
|
+
offsetY: offY,
|
|
505
|
+
resource,
|
|
506
|
+
trimBuffer: trimBuf,
|
|
507
|
+
rasterizedBuffer,
|
|
508
|
+
sourceKind: "image"
|
|
509
|
+
});
|
|
592
510
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
let right = hi + 1;
|
|
617
|
-
while (true) {
|
|
618
|
-
do
|
|
619
|
-
left += 1;
|
|
620
|
-
while (left <= hi && compare(items[left], items[lo]) <= 0);
|
|
621
|
-
do
|
|
622
|
-
right -= 1;
|
|
623
|
-
while (right > lo && compare(items[right], items[lo]) >= 0);
|
|
624
|
-
if (right < left) break;
|
|
625
|
-
swapCompat(items, left, right);
|
|
511
|
+
/** Collect MovieClip frame textures from a .jta file into the inputs array. */
|
|
512
|
+
async function collectMovieClipFrames(doc, resource, pkg, inputs, encoder, options, logger) {
|
|
513
|
+
if (!options.basePath || !options.readFileRaw) {
|
|
514
|
+
if (options.strictOutput) throw new Error(`atlas: MovieClip "${resource.getId()}" requires basePath and readFileRaw for complete raster output.`);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (!encoder && options.strictOutput) throw new Error(`atlas: MovieClip "${resource.getId()}" requires an encoder for complete raster output.`);
|
|
518
|
+
const mcId = resource.getId();
|
|
519
|
+
const mcName = resource.getName() + ".jta";
|
|
520
|
+
const mcPath = resource.getPath() ?? "/";
|
|
521
|
+
const filePath = `${options.basePath}/${pkg.getName()}${mcPath}${mcName}`;
|
|
522
|
+
try {
|
|
523
|
+
const jta = extractJtaFrames(await options.readFileRaw(filePath));
|
|
524
|
+
if (jta.frames.length === 0) return;
|
|
525
|
+
const frameMetas = jta.meta?.frames ?? [];
|
|
526
|
+
for (const frame of resource.listFrames()) resource.removeFrame(frame);
|
|
527
|
+
resource.setInterval(jta.meta?.interval ?? 100).setSwing(jta.meta?.swing ?? false).setRepeatDelay(jta.meta?.repeatDelay ?? 0);
|
|
528
|
+
if (frameMetas.length > 0) {
|
|
529
|
+
const firstFrameIndexByTextureIndex = /* @__PURE__ */ new Map();
|
|
530
|
+
for (let frameIndex = 0; frameIndex < frameMetas.length; frameIndex += 1) {
|
|
531
|
+
const meta = frameMetas[frameIndex];
|
|
532
|
+
const textureIndex = Number.isFinite(meta.textureIndex) ? meta.textureIndex : frameIndex;
|
|
533
|
+
if (!firstFrameIndexByTextureIndex.has(textureIndex)) firstFrameIndexByTextureIndex.set(textureIndex, frameIndex);
|
|
626
534
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
hi = right - 1;
|
|
644
|
-
continue;
|
|
645
|
-
}
|
|
535
|
+
const spriteIdByTextureIndex = /* @__PURE__ */ new Map();
|
|
536
|
+
for (let textureIndex = 0; textureIndex < jta.frames.length; textureIndex += 1) {
|
|
537
|
+
const exportFrameIndex = firstFrameIndexByTextureIndex.get(textureIndex);
|
|
538
|
+
if (exportFrameIndex === void 0) continue;
|
|
539
|
+
const itemId = `${mcId}_${exportFrameIndex}`;
|
|
540
|
+
const input = await createMovieClipFrameInput(jta.frames[textureIndex], itemId, resource, encoder, options.strictOutput);
|
|
541
|
+
if (!input) continue;
|
|
542
|
+
inputs.push(input);
|
|
543
|
+
spriteIdByTextureIndex.set(textureIndex, itemId);
|
|
544
|
+
}
|
|
545
|
+
for (let frameIndex = 0; frameIndex < frameMetas.length; frameIndex += 1) {
|
|
546
|
+
const meta = frameMetas[frameIndex];
|
|
547
|
+
const textureIndex = Number.isFinite(meta.textureIndex) ? meta.textureIndex : frameIndex;
|
|
548
|
+
const frame = doc.createMovieFrame(`${mcId}_${frameIndex}`);
|
|
549
|
+
frame.setRectX(meta.offsetX).setRectY(meta.offsetY).setRectWidth(meta.width).setRectHeight(meta.height).setAddDelay(meta.addDelay).setSpriteId(spriteIdByTextureIndex.get(textureIndex) ?? "");
|
|
550
|
+
resource.addFrame(frame);
|
|
646
551
|
}
|
|
552
|
+
} else for (let frameIndex = 0; frameIndex < jta.frames.length; frameIndex += 1) {
|
|
553
|
+
const itemId = `${mcId}_${frameIndex}`;
|
|
554
|
+
const input = await createMovieClipFrameInput(jta.frames[frameIndex], itemId, resource, encoder, options.strictOutput);
|
|
555
|
+
if (!input) continue;
|
|
556
|
+
inputs.push(input);
|
|
557
|
+
const frame = doc.createMovieFrame(itemId);
|
|
558
|
+
frame.setRectX(0).setRectY(0).setRectWidth(input.originalWidth).setRectHeight(input.originalHeight).setAddDelay(0).setSpriteId(itemId);
|
|
559
|
+
resource.addFrame(frame);
|
|
647
560
|
}
|
|
648
|
-
if (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
561
|
+
if ((jta.meta?.width ?? 0) > 0 && (jta.meta?.height ?? 0) > 0) {
|
|
562
|
+
resource.setWidth(jta.meta?.width ?? 0);
|
|
563
|
+
resource.setHeight(jta.meta?.height ?? 0);
|
|
564
|
+
}
|
|
565
|
+
} catch {
|
|
566
|
+
const message = `atlas: Could not parse MovieClip "${filePath}".`;
|
|
567
|
+
if (options.strictOutput) throw new Error(message);
|
|
568
|
+
logger.warn(`${message} Skipping frames.`);
|
|
652
569
|
}
|
|
653
570
|
}
|
|
654
|
-
function
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
const height = 2 ** h;
|
|
664
|
-
const area = width * height;
|
|
665
|
-
const aspectRatio = width > height ? width / height : height / width;
|
|
666
|
-
result.push({
|
|
571
|
+
async function createMovieClipFrameInput(buffer, itemId, resource, encoder, strictOutput) {
|
|
572
|
+
if (!encoder || buffer.length === 0) return null;
|
|
573
|
+
try {
|
|
574
|
+
const meta = await encoder(buffer).metadata();
|
|
575
|
+
const width = meta.width ?? 0;
|
|
576
|
+
const height = meta.height ?? 0;
|
|
577
|
+
if (width <= 0 || height <= 0) return null;
|
|
578
|
+
return {
|
|
579
|
+
id: itemId,
|
|
667
580
|
width,
|
|
668
581
|
height,
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
582
|
+
originalWidth: width,
|
|
583
|
+
originalHeight: height,
|
|
584
|
+
offsetX: 0,
|
|
585
|
+
offsetY: 0,
|
|
586
|
+
resource,
|
|
587
|
+
trimBuffer: buffer,
|
|
588
|
+
sourceKind: "movieclip-frame"
|
|
589
|
+
};
|
|
590
|
+
} catch {
|
|
591
|
+
if (strictOutput) throw new Error(`atlas: Could not decode MovieClip frame "${itemId}".`);
|
|
592
|
+
return null;
|
|
673
593
|
}
|
|
674
|
-
result.sort(compareSizeScheme);
|
|
675
|
-
return result;
|
|
676
|
-
}
|
|
677
|
-
function compareSizeScheme(left, right) {
|
|
678
|
-
if (left.len < right.len) return -1;
|
|
679
|
-
if (left.len > right.len) return 1;
|
|
680
|
-
if (left.area < right.area) return -1;
|
|
681
|
-
if (left.area > right.area) return 1;
|
|
682
|
-
if (left.aspectRatio < right.aspectRatio) return -1;
|
|
683
|
-
if (left.aspectRatio > right.aspectRatio) return 1;
|
|
684
|
-
if (left.width > left.height) return -1;
|
|
685
|
-
if (right.width > right.height) return 1;
|
|
686
|
-
return 0;
|
|
687
594
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
595
|
+
/** Collect a Bitmap Font's texture image, packed under the font's ID. */
|
|
596
|
+
async function collectFontTexture(doc, fontRes, pkg, options) {
|
|
597
|
+
const textureId = fontRes.getTextureId?.() ?? "";
|
|
598
|
+
if (textureId) {
|
|
599
|
+
const fontId = fontRes.getId();
|
|
600
|
+
fontRes.setExtras({
|
|
601
|
+
...fontRes.getExtras(),
|
|
602
|
+
_fontSpriteAlias: {
|
|
603
|
+
fontId,
|
|
604
|
+
textureId
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
if (options.readFileRaw && options.basePath) {
|
|
609
|
+
const fontName = resolveFontFileName(fontRes.getName());
|
|
610
|
+
const fontPath = fontRes.getPath() ?? "/";
|
|
611
|
+
const pkgName = pkg.getName();
|
|
612
|
+
const fntFile = `${options.basePath}/${pkgName}${fontPath}${fontName}`;
|
|
613
|
+
try {
|
|
614
|
+
const fntData = await options.readFileRaw(fntFile);
|
|
615
|
+
const fntParsed = parseFnt(new TextDecoder().decode(fntData));
|
|
616
|
+
for (const glyph of fontRes.listGlyphs()) fontRes.removeGlyph(glyph);
|
|
617
|
+
fontRes.setTtf(fntParsed.hasFace).setTint(fntParsed.colored).setAutoScale(fntParsed.resizable).setHasChannel(fntParsed.hasChannel).setFontSize(fntParsed.fontSize).setXAdvance(fntParsed.xadvance).setLineHeight(fntParsed.lineHeight);
|
|
618
|
+
for (const item of fntParsed.glyphs) {
|
|
619
|
+
const glyph = doc.createFontGlyph(`${fontRes.getId()}_${item.charId}`);
|
|
620
|
+
glyph.setCharId(item.charId).setChar(item.charId > 0 ? String.fromCodePoint(item.charId) : "").setImg(item.img ?? "").setX(item.x).setY(item.y).setXOffset(item.xoffset).setYOffset(item.yoffset).setWidth(item.width).setHeight(item.height).setAdvance(item.xadvance).setLineHeight(fntParsed.lineHeight).setChannel(item.channel);
|
|
621
|
+
fontRes.addGlyph(glyph);
|
|
622
|
+
}
|
|
623
|
+
} catch {}
|
|
624
|
+
}
|
|
692
625
|
}
|
|
693
|
-
function
|
|
694
|
-
return
|
|
626
|
+
function isComponentResource$1(resource) {
|
|
627
|
+
return resource.propertyType === "Component";
|
|
695
628
|
}
|
|
696
|
-
function
|
|
697
|
-
|
|
698
|
-
return (right.width > right.height ? right.width : right.height) - leftEdge;
|
|
629
|
+
function isImageResource$1(resource) {
|
|
630
|
+
return resource.propertyType === "ImageResource";
|
|
699
631
|
}
|
|
700
|
-
function
|
|
701
|
-
|
|
702
|
-
if (delta !== 0) return delta;
|
|
703
|
-
if (left.sourceKind === "movieclip-frame" && right.sourceKind === "movieclip-frame") {
|
|
704
|
-
const areaDelta = right.width * right.height - left.width * left.height;
|
|
705
|
-
if (areaDelta !== 0) return areaDelta;
|
|
706
|
-
const widthDelta = right.width - left.width;
|
|
707
|
-
if (widthDelta !== 0) return widthDelta;
|
|
708
|
-
}
|
|
709
|
-
return left.index - right.index;
|
|
632
|
+
function isMovieClipResource$1(resource) {
|
|
633
|
+
return resource.propertyType === "MovieClipResource";
|
|
710
634
|
}
|
|
711
|
-
function
|
|
712
|
-
return
|
|
635
|
+
function isSkeletonResource$1(resource) {
|
|
636
|
+
return resource.propertyType === "SpineResource" || resource.propertyType === "DragonBonesResource";
|
|
713
637
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
716
|
-
if (delta !== 0) return delta;
|
|
717
|
-
if (left.sourceKind === "movieclip-frame" && right.sourceKind === "movieclip-frame") {
|
|
718
|
-
const areaDelta = right.width * right.height - left.width * left.height;
|
|
719
|
-
if (areaDelta !== 0) return areaDelta;
|
|
720
|
-
const heightDelta = right.height - left.height;
|
|
721
|
-
if (heightDelta !== 0) return heightDelta;
|
|
722
|
-
}
|
|
723
|
-
return left.index - right.index;
|
|
638
|
+
function isFontResource$1(resource) {
|
|
639
|
+
return resource.propertyType === "FontResource";
|
|
724
640
|
}
|
|
725
|
-
function
|
|
726
|
-
return (
|
|
641
|
+
function isPackableResource(resource) {
|
|
642
|
+
return isImageResource$1(resource) || isMovieClipResource$1(resource) || isFontResource$1(resource);
|
|
727
643
|
}
|
|
728
|
-
function
|
|
729
|
-
|
|
730
|
-
if (maxWidth - rect.width > padding || duplicatePadding(rect)) rect.width -= padding;
|
|
731
|
-
if (maxHeight - rect.height > padding || duplicatePadding(rect)) rect.height -= padding;
|
|
732
|
-
} else {
|
|
733
|
-
if (maxHeight - rect.width > padding || duplicatePadding(rect)) rect.width -= padding;
|
|
734
|
-
if (maxWidth - rect.height > padding || duplicatePadding(rect)) rect.height -= padding;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
function cloneCompatRect(rect) {
|
|
738
|
-
return { ...rect };
|
|
644
|
+
function isResolvedBuffer(value) {
|
|
645
|
+
return typeof value === "object" && value !== null && "data" in value && "info" in value;
|
|
739
646
|
}
|
|
740
647
|
//#endregion
|
|
741
|
-
//#region src/
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
trimImage: false,
|
|
751
|
-
preserveInputOrderOnTie: false,
|
|
752
|
-
directSingleImageOutput: false,
|
|
753
|
-
extractAlpha: false,
|
|
754
|
-
separatedAtlasForBranch: false,
|
|
755
|
-
strictOutput: false
|
|
648
|
+
//#region src/max-rects-compat.ts
|
|
649
|
+
const NO_ROTATION = 2;
|
|
650
|
+
const MAX_SCORE = 2147483647;
|
|
651
|
+
const MAX_RECTS_METHOD = {
|
|
652
|
+
BestShortSideFit: 0,
|
|
653
|
+
BestLongSideFit: 1,
|
|
654
|
+
BestAreaFit: 2,
|
|
655
|
+
BottomLeftRule: 3,
|
|
656
|
+
ContactPointRule: 4
|
|
756
657
|
};
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
658
|
+
const COMPAT_NODE_RECT_FLAGS = {
|
|
659
|
+
DUPLICATE_PADDING: 1,
|
|
660
|
+
NO_ROTATION
|
|
661
|
+
};
|
|
662
|
+
var MaxRectsCompat = class MaxRectsCompat {
|
|
663
|
+
static helperRect = createNodeRect();
|
|
664
|
+
binWidth = 0;
|
|
665
|
+
binHeight = 0;
|
|
666
|
+
allowRotations = false;
|
|
667
|
+
usedRectangles = [];
|
|
668
|
+
freeRectangles = [];
|
|
669
|
+
init(width, height, allowRotations = false) {
|
|
670
|
+
this.binWidth = width;
|
|
671
|
+
this.binHeight = height;
|
|
672
|
+
this.allowRotations = allowRotations;
|
|
673
|
+
this.usedRectangles.length = 0;
|
|
674
|
+
this.freeRectangles.length = 0;
|
|
675
|
+
this.freeRectangles.push({
|
|
676
|
+
...createNodeRect(),
|
|
677
|
+
x: 0,
|
|
678
|
+
y: 0,
|
|
679
|
+
width,
|
|
680
|
+
height
|
|
681
|
+
});
|
|
770
682
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const fontName = resolveFontFileName(resource.getName());
|
|
792
|
-
const fontPath = resource.getPath() ?? "/";
|
|
793
|
-
const fntFile = `${options.basePath}/${pkg.getName()}${fontPath}${fontName}`;
|
|
794
|
-
try {
|
|
795
|
-
const fntData = await options.readFileRaw(fntFile);
|
|
796
|
-
const fntText = new TextDecoder().decode(fntData);
|
|
797
|
-
for (const line of fntText.split(/\r?\n/)) {
|
|
798
|
-
const imgMatch = line.match(/\bimg=(\w+)/);
|
|
799
|
-
if (imgMatch) await addResource(resourceMap.get(imgMatch[1] ?? ""));
|
|
800
|
-
}
|
|
801
|
-
} catch {}
|
|
683
|
+
insert(rect, method) {
|
|
684
|
+
const newNode = this.scoreRect(rect, method);
|
|
685
|
+
if (newNode.height === 0) return null;
|
|
686
|
+
const placed = cloneNodeRect(newNode);
|
|
687
|
+
this.placeRect(placed);
|
|
688
|
+
return placed;
|
|
689
|
+
}
|
|
690
|
+
pack(rects, method) {
|
|
691
|
+
const remaining = rects.map(cloneNodeRect);
|
|
692
|
+
while (remaining.length > 0) {
|
|
693
|
+
let bestIndex = -1;
|
|
694
|
+
const bestNode = createNodeRect();
|
|
695
|
+
bestNode.score1 = MAX_SCORE;
|
|
696
|
+
bestNode.score2 = MAX_SCORE;
|
|
697
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
698
|
+
const candidate = this.scoreRect(remaining[index], method);
|
|
699
|
+
if (candidate.score1 < bestNode.score1 || candidate.score1 === bestNode.score1 && candidate.score2 < bestNode.score2) {
|
|
700
|
+
copyNodeRect(bestNode, candidate);
|
|
701
|
+
bestIndex = index;
|
|
702
|
+
}
|
|
802
703
|
}
|
|
704
|
+
if (bestIndex === -1) break;
|
|
705
|
+
this.placeRect(bestNode);
|
|
706
|
+
remaining.splice(bestIndex, 1);
|
|
803
707
|
}
|
|
804
|
-
|
|
708
|
+
const result = this.getResult();
|
|
709
|
+
result.remainingRects = remaining;
|
|
710
|
+
return result;
|
|
805
711
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if (slashIndex >= 0) {
|
|
813
|
-
if (normalized.slice(0, slashIndex) !== pkgId) return;
|
|
814
|
-
resourceId = normalized.slice(slashIndex + 1);
|
|
815
|
-
} else if (normalized.length > 8) {
|
|
816
|
-
if (normalized.slice(0, 8) !== pkgId) return;
|
|
817
|
-
resourceId = normalized.slice(8);
|
|
712
|
+
getResult() {
|
|
713
|
+
let width = 0;
|
|
714
|
+
let height = 0;
|
|
715
|
+
for (const rect of this.usedRectangles) {
|
|
716
|
+
width = Math.max(width, rect.x + rect.width);
|
|
717
|
+
height = Math.max(height, rect.y + rect.height);
|
|
818
718
|
}
|
|
819
|
-
|
|
820
|
-
|
|
719
|
+
return {
|
|
720
|
+
outputRects: this.usedRectangles.map(cloneNodeRect),
|
|
721
|
+
remainingRects: [],
|
|
722
|
+
occupancy: this.getOccupancy(),
|
|
723
|
+
width,
|
|
724
|
+
height
|
|
725
|
+
};
|
|
821
726
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
const defaultValue = gear.getDefaultValue?.();
|
|
827
|
-
if (typeof defaultValue === "string") await addResourceByLocalUiUrl(defaultValue);
|
|
727
|
+
getOccupancy() {
|
|
728
|
+
let usedSurface = 0;
|
|
729
|
+
for (const rect of this.usedRectangles) usedSurface += rect.width * rect.height;
|
|
730
|
+
return usedSurface / (this.binWidth * this.binHeight);
|
|
828
731
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
for (const child of component.listChildren()) {
|
|
834
|
-
const refChild = child;
|
|
835
|
-
await addResource(resourceMap.get(refChild.getSrc?.() ?? ""));
|
|
836
|
-
for (const ref of [
|
|
837
|
-
refChild.getUrl?.(),
|
|
838
|
-
refChild.getDefaultItem?.(),
|
|
839
|
-
refChild.getIcon?.(),
|
|
840
|
-
refChild.getSelectedIcon?.(),
|
|
841
|
-
refChild.getFont?.(),
|
|
842
|
-
refChild.getDropdown?.(),
|
|
843
|
-
refChild.getVtScrollBarRes?.(),
|
|
844
|
-
refChild.getHzScrollBarRes?.(),
|
|
845
|
-
refChild.getHeaderRes?.(),
|
|
846
|
-
refChild.getFooterRes?.(),
|
|
847
|
-
refChild.getSound?.(),
|
|
848
|
-
refChild.getInstanceIcon?.(),
|
|
849
|
-
refChild.getInstanceSelectedIcon?.()
|
|
850
|
-
]) await addResourceByLocalUiUrl(ref);
|
|
851
|
-
for (const item of refChild.getInstanceComboItems?.() ?? []) await addResourceByLocalUiUrl(item.icon ?? void 0);
|
|
852
|
-
for (const item of refChild.getListItems?.() ?? []) {
|
|
853
|
-
await addResourceByLocalUiUrl(item.icon ?? void 0);
|
|
854
|
-
await addResourceByLocalUiUrl(item.url ?? void 0);
|
|
855
|
-
}
|
|
856
|
-
for (const gear of refChild.listGears?.() ?? []) await addGearIconResources(gear);
|
|
732
|
+
placeRect(rect) {
|
|
733
|
+
for (let index = 0; index < this.freeRectangles.length; index += 1) if (this.splitFreeNode(this.freeRectangles[index], rect)) {
|
|
734
|
+
this.freeRectangles.splice(index, 1);
|
|
735
|
+
index -= 1;
|
|
857
736
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
737
|
+
this.pruneFreeList();
|
|
738
|
+
this.usedRectangles.push(rect);
|
|
739
|
+
}
|
|
740
|
+
scoreRect(rect, method) {
|
|
741
|
+
const helper = MaxRectsCompat.helperRect;
|
|
742
|
+
helper.height = 0;
|
|
743
|
+
let newNode;
|
|
744
|
+
switch (method) {
|
|
745
|
+
case MAX_RECTS_METHOD.BestShortSideFit:
|
|
746
|
+
newNode = this.findPositionForNewNodeBestShortSideFit(rect.width, rect.height, allowRotation(rect));
|
|
747
|
+
break;
|
|
748
|
+
case MAX_RECTS_METHOD.BestLongSideFit:
|
|
749
|
+
newNode = this.findPositionForNewNodeBestLongSideFit(rect.width, rect.height, allowRotation(rect));
|
|
750
|
+
break;
|
|
751
|
+
case MAX_RECTS_METHOD.BestAreaFit:
|
|
752
|
+
newNode = this.findPositionForNewNodeBestAreaFit(rect.width, rect.height, allowRotation(rect));
|
|
753
|
+
break;
|
|
754
|
+
case MAX_RECTS_METHOD.BottomLeftRule:
|
|
755
|
+
newNode = this.findPositionForNewNodeBottomLeft(rect.width, rect.height, allowRotation(rect));
|
|
756
|
+
break;
|
|
757
|
+
case MAX_RECTS_METHOD.ContactPointRule:
|
|
758
|
+
newNode = this.findPositionForNewNodeContactPoint(rect.width, rect.height, allowRotation(rect));
|
|
759
|
+
newNode.score1 = -newNode.score1;
|
|
760
|
+
break;
|
|
761
|
+
default:
|
|
762
|
+
newNode = helper;
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
if (newNode.height === 0) {
|
|
766
|
+
newNode.score1 = MAX_SCORE;
|
|
767
|
+
newNode.score2 = MAX_SCORE;
|
|
872
768
|
}
|
|
769
|
+
newNode.index = rect.index;
|
|
770
|
+
newNode.subIndex = rect.subIndex;
|
|
771
|
+
newNode.flags = rect.flags;
|
|
772
|
+
newNode.sourceKind = rect.sourceKind;
|
|
773
|
+
return cloneNodeRect(newNode);
|
|
873
774
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
* a raster backend is provided, it also composites the actual PNG files.
|
|
883
|
-
*
|
|
884
|
-
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
885
|
-
* at image edges are trimmed before packing. The trimmed offset and original
|
|
886
|
-
* dimensions are stored in the Sprite nodes for runtime reconstruction.
|
|
887
|
-
*
|
|
888
|
-
* ```ts
|
|
889
|
-
* import sharp from 'sharp';
|
|
890
|
-
* await doc.transform(atlas({
|
|
891
|
-
* encoder: sharp,
|
|
892
|
-
* maxSize: 2048,
|
|
893
|
-
* trimImage: true,
|
|
894
|
-
* basePath: './assets/',
|
|
895
|
-
* outputPath: './dist/',
|
|
896
|
-
* }));
|
|
897
|
-
* ```
|
|
898
|
-
*/
|
|
899
|
-
function atlas(_options = {}) {
|
|
900
|
-
const options = {
|
|
901
|
-
...ATLAS_DEFAULTS,
|
|
902
|
-
..._options
|
|
903
|
-
};
|
|
904
|
-
return createTransform("atlas", async (doc) => {
|
|
905
|
-
const root = doc.getRoot();
|
|
906
|
-
const logger = doc.getLogger();
|
|
907
|
-
const encoder = options.encoder;
|
|
908
|
-
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
909
|
-
const packageFilter = options.packages ? new Set(options.packages) : null;
|
|
910
|
-
for (const pkg of root.listPackages()) {
|
|
911
|
-
if (packageFilter && !packageFilter.has(pkg.getName())) continue;
|
|
912
|
-
const publishedResourceIds = pkg.getExtras()?.publishedResourceIds;
|
|
913
|
-
const selectedPublishIds = new Set(publishedResourceIds);
|
|
914
|
-
const allResources = publishedResourceIds !== void 0 && (options.strictOutput || selectedPublishIds.size > 0) ? pkg.listResources().filter((resource) => selectedPublishIds.has(resource.getId())) : pkg.listResources();
|
|
915
|
-
const skeletonDependencyImageIds = getSelectedSkeletonDependencyImageIds(allResources);
|
|
916
|
-
const orderedResources = await resolveEditorCompatibleResourceOrder(pkg, allResources, options);
|
|
917
|
-
const orderedAllResources = sortResourcesByOrder(allResources, new Map(orderedResources.map((resource, index) => [resource.getId(), index])), new Map(allResources.map((resource, index) => [resource.getId(), index])));
|
|
918
|
-
if (!allResources.some((resource) => {
|
|
919
|
-
if (isImageResource$1(resource) && skeletonDependencyImageIds.has(resource.getId())) return false;
|
|
920
|
-
return isPackableResource(resource);
|
|
921
|
-
})) continue;
|
|
922
|
-
const inputs = [];
|
|
923
|
-
const referencedIds = /* @__PURE__ */ new Set();
|
|
924
|
-
const resourceMap = /* @__PURE__ */ new Map();
|
|
925
|
-
for (const res of allResources) {
|
|
926
|
-
const id = res.getId();
|
|
927
|
-
if (id) resourceMap.set(id, res);
|
|
775
|
+
findPositionForNewNodeBottomLeft(width, height, allowRectRotation) {
|
|
776
|
+
const bestNode = MaxRectsCompat.helperRect;
|
|
777
|
+
bestNode.score1 = MAX_SCORE;
|
|
778
|
+
bestNode.score2 = 0;
|
|
779
|
+
for (const freeRect of this.freeRectangles) {
|
|
780
|
+
if (freeRect.width >= width && freeRect.height >= height) {
|
|
781
|
+
const topSideY = freeRect.y + height;
|
|
782
|
+
if (topSideY < bestNode.score1 || topSideY === bestNode.score1 && freeRect.x < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, topSideY, freeRect.x);
|
|
928
783
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const src = refChild.getSrc?.();
|
|
933
|
-
if (src && !visited.has(src)) {
|
|
934
|
-
referencedIds.add(src);
|
|
935
|
-
visited.add(src);
|
|
936
|
-
const srcRes = resourceMap.get(src);
|
|
937
|
-
if (srcRes && isComponentResource$1(srcRes)) collectRefs(srcRes, visited);
|
|
938
|
-
}
|
|
939
|
-
for (const ref of [
|
|
940
|
-
refChild.getIcon?.(),
|
|
941
|
-
refChild.getSelectedIcon?.(),
|
|
942
|
-
refChild.getFont?.(),
|
|
943
|
-
refChild.getDropdown?.(),
|
|
944
|
-
refChild.getInstanceIcon?.(),
|
|
945
|
-
refChild.getInstanceSelectedIcon?.(),
|
|
946
|
-
refChild.getVtScrollBarRes?.(),
|
|
947
|
-
refChild.getHzScrollBarRes?.(),
|
|
948
|
-
refChild.getHeaderRes?.(),
|
|
949
|
-
refChild.getFooterRes?.(),
|
|
950
|
-
refChild.getUrl?.()
|
|
951
|
-
]) addUiResourceRef(referencedIds, ref);
|
|
952
|
-
addUiResourceRefsFromText(referencedIds, refChild.getText?.());
|
|
953
|
-
for (const item of refChild.getInstanceComboItems?.() ?? []) addUiResourceRef(referencedIds, item.icon ?? void 0);
|
|
954
|
-
for (const item of refChild.getListItems?.() ?? []) addUiResourceRef(referencedIds, item.icon ?? void 0);
|
|
955
|
-
for (const gear of refChild.listGears?.() ?? []) {
|
|
956
|
-
addUiResourceRefsFromUnknown(referencedIds, gear.getValues?.());
|
|
957
|
-
addUiResourceRefsFromUnknown(referencedIds, gear.getDefaultValue?.());
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
for (const transition of component.listTransitions?.() ?? []) for (const item of transition.listItems?.() ?? []) {
|
|
961
|
-
addUiResourceRefsFromUnknown(referencedIds, item.getStartValue?.());
|
|
962
|
-
addUiResourceRefsFromUnknown(referencedIds, item.getEndValue?.());
|
|
963
|
-
}
|
|
784
|
+
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
785
|
+
const topSideY = freeRect.y + width;
|
|
786
|
+
if (topSideY < bestNode.score1 || topSideY === bestNode.score1 && freeRect.x < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, topSideY, freeRect.x);
|
|
964
787
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
const fntText = new TextDecoder().decode(fntData);
|
|
980
|
-
for (const line of fntText.split(/\r?\n/)) {
|
|
981
|
-
const match = line.match(/img=(\w+)/);
|
|
982
|
-
if (match) referencedIds.add(match[1]);
|
|
983
|
-
}
|
|
984
|
-
} catch {}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
788
|
+
}
|
|
789
|
+
return bestNode;
|
|
790
|
+
}
|
|
791
|
+
findPositionForNewNodeBestShortSideFit(width, height, allowRectRotation) {
|
|
792
|
+
const bestNode = MaxRectsCompat.helperRect;
|
|
793
|
+
bestNode.score1 = MAX_SCORE;
|
|
794
|
+
bestNode.score2 = 0;
|
|
795
|
+
for (const freeRect of this.freeRectangles) {
|
|
796
|
+
if (freeRect.width >= width && freeRect.height >= height) {
|
|
797
|
+
const leftoverHoriz = Math.abs(freeRect.width - width);
|
|
798
|
+
const leftoverVert = Math.abs(freeRect.height - height);
|
|
799
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
800
|
+
const longSideFit = Math.max(leftoverHoriz, leftoverVert);
|
|
801
|
+
if (shortSideFit < bestNode.score1 || shortSideFit === bestNode.score1 && longSideFit < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, shortSideFit, longSideFit);
|
|
987
802
|
}
|
|
988
|
-
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const resId = res.getId();
|
|
995
|
-
if (selectedPublishIds.size === 0 && !res.getExported() && referencedIds.size > 0 && !referencedIds.has(resId)) continue;
|
|
996
|
-
await _collectMovieClipFrames(doc, res, pkg, inputs, encoder, options, logger);
|
|
997
|
-
} else if (isFontResource$1(res)) {
|
|
998
|
-
const resId = res.getId();
|
|
999
|
-
if (selectedPublishIds.size === 0 && !res.getExported() && referencedIds.size > 0 && !referencedIds.has(resId)) continue;
|
|
1000
|
-
await _collectFontTexture(doc, res, pkg, options);
|
|
803
|
+
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
804
|
+
const leftoverHoriz = Math.abs(freeRect.width - height);
|
|
805
|
+
const leftoverVert = Math.abs(freeRect.height - width);
|
|
806
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
807
|
+
const longSideFit = Math.max(leftoverHoriz, leftoverVert);
|
|
808
|
+
if (shortSideFit < bestNode.score1 || shortSideFit === bestNode.score1 && longSideFit < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, shortSideFit, longSideFit);
|
|
1001
809
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
}
|
|
1017
|
-
const pageStart = reserveAutoPageStart(branchPageOffsets, group.branchOrdinal, reservedPageIndexes);
|
|
1018
|
-
const emittedPageCount = await emitPagedAtlasGroup(doc, pkg, allResources, group.inputs, {
|
|
1019
|
-
branchName: group.branchName,
|
|
1020
|
-
branchOrdinal: group.branchOrdinal,
|
|
1021
|
-
pageStart,
|
|
1022
|
-
fileNameAt: (pageIndex) => resolveAtlasOutputFileName(pkg, pageIndex, group.branchName),
|
|
1023
|
-
options,
|
|
1024
|
-
encoder,
|
|
1025
|
-
logger
|
|
1026
|
-
});
|
|
1027
|
-
totalPageCount += emittedPageCount;
|
|
1028
|
-
branchPageOffsets.set(group.branchOrdinal, pageStart + emittedPageCount);
|
|
810
|
+
}
|
|
811
|
+
return bestNode;
|
|
812
|
+
}
|
|
813
|
+
findPositionForNewNodeBestLongSideFit(width, height, allowRectRotation) {
|
|
814
|
+
const bestNode = MaxRectsCompat.helperRect;
|
|
815
|
+
bestNode.score1 = 0;
|
|
816
|
+
bestNode.score2 = MAX_SCORE;
|
|
817
|
+
for (const freeRect of this.freeRectangles) {
|
|
818
|
+
if (freeRect.width >= width && freeRect.height >= height) {
|
|
819
|
+
const leftoverHoriz = Math.abs(freeRect.width - width);
|
|
820
|
+
const leftoverVert = Math.abs(freeRect.height - height);
|
|
821
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
822
|
+
const longSideFit = Math.max(leftoverHoriz, leftoverVert);
|
|
823
|
+
if (longSideFit < bestNode.score2 || longSideFit === bestNode.score2 && shortSideFit < bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, shortSideFit, longSideFit);
|
|
1029
824
|
}
|
|
1030
|
-
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
fileNameAt: () => resolveAtlasOutputFileName(pkg, group.pageIndex, group.branchName),
|
|
1037
|
-
options,
|
|
1038
|
-
encoder,
|
|
1039
|
-
logger
|
|
1040
|
-
});
|
|
1041
|
-
totalPageCount += emittedPageCount;
|
|
825
|
+
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
826
|
+
const leftoverHoriz = Math.abs(freeRect.width - height);
|
|
827
|
+
const leftoverVert = Math.abs(freeRect.height - width);
|
|
828
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
829
|
+
const longSideFit = Math.max(leftoverHoriz, leftoverVert);
|
|
830
|
+
if (longSideFit < bestNode.score2 || longSideFit === bestNode.score2 && shortSideFit < bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, shortSideFit, longSideFit);
|
|
1042
831
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
832
|
+
}
|
|
833
|
+
return bestNode;
|
|
834
|
+
}
|
|
835
|
+
findPositionForNewNodeBestAreaFit(width, height, allowRectRotation) {
|
|
836
|
+
const bestNode = MaxRectsCompat.helperRect;
|
|
837
|
+
bestNode.score1 = MAX_SCORE;
|
|
838
|
+
bestNode.score2 = 0;
|
|
839
|
+
for (const freeRect of this.freeRectangles) {
|
|
840
|
+
const areaFit = freeRect.width * freeRect.height - width * height;
|
|
841
|
+
if (freeRect.width >= width && freeRect.height >= height) {
|
|
842
|
+
const leftoverHoriz = Math.abs(freeRect.width - width);
|
|
843
|
+
const leftoverVert = Math.abs(freeRect.height - height);
|
|
844
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
845
|
+
if (areaFit < bestNode.score1 || areaFit === bestNode.score1 && shortSideFit < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, areaFit, shortSideFit);
|
|
1047
846
|
}
|
|
1048
|
-
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
logger
|
|
1054
|
-
});
|
|
1055
|
-
totalPageCount += emittedPageCount;
|
|
1056
|
-
standalonePageOffsets.set(group.branchOrdinal, (standalonePageOffsets.get(group.branchOrdinal) ?? 0) + emittedPageCount);
|
|
847
|
+
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
848
|
+
const leftoverHoriz = Math.abs(freeRect.width - height);
|
|
849
|
+
const leftoverVert = Math.abs(freeRect.height - width);
|
|
850
|
+
const shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
|
851
|
+
if (areaFit < bestNode.score1 || areaFit === bestNode.score1 && shortSideFit < bestNode.score2) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, areaFit, shortSideFit);
|
|
1057
852
|
}
|
|
1058
|
-
if (usedDirectOutput) logger.info(`atlas: Direct output for single image package "${pkg.getName()}".`);
|
|
1059
|
-
logger.info(`atlas: Packed ${inputs.length} images into ${totalPageCount} atlas(es) for package "${pkg.getName()}".`);
|
|
1060
853
|
}
|
|
1061
|
-
|
|
1062
|
-
}
|
|
1063
|
-
function buildBranchAtlasGroups(doc, inputs, options) {
|
|
1064
|
-
if (!options.separatedAtlasForBranch) return [{
|
|
1065
|
-
branchName: "",
|
|
1066
|
-
branchOrdinal: 0,
|
|
1067
|
-
inputs
|
|
1068
|
-
}];
|
|
1069
|
-
const discoveredBranchNames = [...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName))];
|
|
1070
|
-
if (discoveredBranchNames.length === 0) return [{
|
|
1071
|
-
branchName: "",
|
|
1072
|
-
branchOrdinal: 0,
|
|
1073
|
-
inputs
|
|
1074
|
-
}];
|
|
1075
|
-
const orderedBranchNames = doc.getRoot().listBranches().filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
1076
|
-
for (const branchName of discoveredBranchNames) if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
1077
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1078
|
-
groups.set("", []);
|
|
1079
|
-
for (const branchName of orderedBranchNames) groups.set(branchName, []);
|
|
1080
|
-
for (const input of inputs) {
|
|
1081
|
-
const branchName = getInputBranchName(input);
|
|
1082
|
-
const key = groups.has(branchName) ? branchName : "";
|
|
1083
|
-
groups.get(key).push(input);
|
|
854
|
+
return bestNode;
|
|
1084
855
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
const pages = packAtlasPages(inputs, context.options, context.forceSinglePage === true);
|
|
1101
|
-
assertPackedInputCoverage(pages, inputs.length, `package "${pkg.getName()}"`);
|
|
1102
|
-
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
1103
|
-
const page = pages[pageOffset];
|
|
1104
|
-
const pageIndex = context.pageStart + pageOffset;
|
|
1105
|
-
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(context.branchOrdinal, pageIndex)}`);
|
|
1106
|
-
atlasNode.setIndex(resolveAtlasIndex(context.branchOrdinal, pageIndex));
|
|
1107
|
-
atlasNode.setFile(context.fileNameAt(pageIndex));
|
|
1108
|
-
atlasNode.setWidth(page.width);
|
|
1109
|
-
atlasNode.setHeight(page.height);
|
|
1110
|
-
pkg.addAtlas(atlasNode);
|
|
1111
|
-
attachSpritesToAtlas(doc, allResources, inputs, page.outputRects, atlasNode);
|
|
1112
|
-
await writeAtlasPageImage(pkg, inputs, page, atlasNode.getFile(), context.encoder, context.options, context.logger);
|
|
856
|
+
findPositionForNewNodeContactPoint(width, height, allowRectRotation) {
|
|
857
|
+
const bestNode = MaxRectsCompat.helperRect;
|
|
858
|
+
bestNode.score1 = -1;
|
|
859
|
+
bestNode.score2 = 0;
|
|
860
|
+
for (const freeRect of this.freeRectangles) {
|
|
861
|
+
if (freeRect.width >= width && freeRect.height >= height) {
|
|
862
|
+
const score = this.contactPointScoreNode(freeRect.x, freeRect.y, width, height);
|
|
863
|
+
if (score > bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, width, height, false, score, bestNode.score2);
|
|
864
|
+
}
|
|
865
|
+
if (this.allowRotations && allowRectRotation && freeRect.width >= height && freeRect.height >= width) {
|
|
866
|
+
const score = this.contactPointScoreNode(freeRect.x, freeRect.y, height, width);
|
|
867
|
+
if (score > bestNode.score1) setNodeRect(bestNode, freeRect.x, freeRect.y, height, width, true, score, bestNode.score2);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return bestNode;
|
|
1113
871
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
powerOfTwo: false,
|
|
1124
|
-
multipleOfFour: true,
|
|
1125
|
-
square: false
|
|
1126
|
-
} : void 0);
|
|
1127
|
-
assertPackedInputCoverage(pages, group.inputs.length, `standalone texture in package "${pkg.getName()}"`);
|
|
1128
|
-
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
1129
|
-
const page = pages[pageOffset];
|
|
1130
|
-
const baseFileName = resolveStandaloneAtlasOutputFileName(pkg, group.resource, group.branchName);
|
|
1131
|
-
const atlasFileName = pages.length <= 1 ? baseFileName : insertFileNameSuffix(baseFileName, `_${pageOffset}`);
|
|
1132
|
-
const atlasIndex = context.atlasIndexStart + pageOffset;
|
|
1133
|
-
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(group.branchOrdinal, atlasIndex)}`);
|
|
1134
|
-
atlasNode.setIndex(resolveAtlasIndex(group.branchOrdinal, atlasIndex));
|
|
1135
|
-
atlasNode.setFile(atlasFileName);
|
|
1136
|
-
const standaloneSize = resolveStandaloneAtlasSize(page.width, page.height, group.sizeMode, context.options);
|
|
1137
|
-
atlasNode.setWidth(standaloneSize.width);
|
|
1138
|
-
atlasNode.setHeight(standaloneSize.height);
|
|
1139
|
-
pkg.addAtlas(atlasNode);
|
|
1140
|
-
attachSpritesToAtlas(doc, [], group.inputs, page.outputRects, atlasNode);
|
|
1141
|
-
await writeAtlasPageImage(pkg, group.inputs, {
|
|
1142
|
-
...page,
|
|
1143
|
-
width: standaloneSize.width,
|
|
1144
|
-
height: standaloneSize.height
|
|
1145
|
-
}, atlasFileName, context.encoder, context.options, context.logger);
|
|
872
|
+
contactPointScoreNode(x, y, width, height) {
|
|
873
|
+
let score = 0;
|
|
874
|
+
if (x === 0 || x + width === this.binWidth) score += height;
|
|
875
|
+
if (y === 0 || y + height === this.binHeight) score += width;
|
|
876
|
+
for (const rect of this.usedRectangles) {
|
|
877
|
+
if (rect.x === x + width || rect.x + rect.width === x) score += commonIntervalLength(rect.y, rect.y + rect.height, y, y + height);
|
|
878
|
+
if (rect.y === y + height || rect.y + rect.height === y) score += commonIntervalLength(rect.x, rect.x + rect.width, x, x + width);
|
|
879
|
+
}
|
|
880
|
+
return score;
|
|
1146
881
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
882
|
+
splitFreeNode(freeNode, usedNode) {
|
|
883
|
+
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x || usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y) return false;
|
|
884
|
+
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
|
|
885
|
+
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
|
|
886
|
+
const newNode = cloneNodeRect(freeNode);
|
|
887
|
+
newNode.height = usedNode.y - newNode.y;
|
|
888
|
+
this.freeRectangles.push(newNode);
|
|
889
|
+
}
|
|
890
|
+
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
|
|
891
|
+
const newNode = cloneNodeRect(freeNode);
|
|
892
|
+
newNode.y = usedNode.y + usedNode.height;
|
|
893
|
+
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
|
|
894
|
+
this.freeRectangles.push(newNode);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
|
|
898
|
+
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
|
|
899
|
+
const newNode = cloneNodeRect(freeNode);
|
|
900
|
+
newNode.width = usedNode.x - newNode.x;
|
|
901
|
+
this.freeRectangles.push(newNode);
|
|
902
|
+
}
|
|
903
|
+
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
|
|
904
|
+
const newNode = cloneNodeRect(freeNode);
|
|
905
|
+
newNode.x = usedNode.x + usedNode.width;
|
|
906
|
+
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
|
|
907
|
+
this.freeRectangles.push(newNode);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
pruneFreeList() {
|
|
913
|
+
let length = this.freeRectangles.length;
|
|
914
|
+
let left = 0;
|
|
915
|
+
while (left < length) {
|
|
916
|
+
let right = left + 1;
|
|
917
|
+
while (right < length) {
|
|
918
|
+
if (isContainedIn(this.freeRectangles[left], this.freeRectangles[right])) {
|
|
919
|
+
this.freeRectangles.splice(left, 1);
|
|
920
|
+
length -= 1;
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
if (isContainedIn(this.freeRectangles[right], this.freeRectangles[left])) {
|
|
924
|
+
this.freeRectangles.splice(right, 1);
|
|
925
|
+
length -= 1;
|
|
926
|
+
}
|
|
927
|
+
right += 1;
|
|
928
|
+
}
|
|
929
|
+
left += 1;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
function createNodeRect() {
|
|
934
|
+
return {
|
|
935
|
+
x: 0,
|
|
936
|
+
y: 0,
|
|
937
|
+
width: 0,
|
|
938
|
+
height: 0,
|
|
939
|
+
rotated: false,
|
|
940
|
+
index: 0,
|
|
941
|
+
subIndex: -1,
|
|
942
|
+
flags: 0,
|
|
943
|
+
score1: 0,
|
|
944
|
+
score2: 0,
|
|
945
|
+
sourceKind: void 0
|
|
946
|
+
};
|
|
1169
947
|
}
|
|
1170
|
-
function
|
|
1171
|
-
|
|
1172
|
-
for (const page of pages) for (const outputRect of page.outputRects) packedIndexes.add(outputRect.index);
|
|
1173
|
-
const hasEveryInput = Array.from({ length: inputCount }, (_, index) => packedIndexes.has(index)).every(Boolean);
|
|
1174
|
-
if (packedIndexes.size !== inputCount || !hasEveryInput) throw new Error(`atlas: Could not pack every input for ${label}.`);
|
|
948
|
+
function cloneNodeRect(rect) {
|
|
949
|
+
return { ...rect };
|
|
1175
950
|
}
|
|
1176
|
-
function
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
951
|
+
function copyNodeRect(target, source) {
|
|
952
|
+
target.x = source.x;
|
|
953
|
+
target.y = source.y;
|
|
954
|
+
target.width = source.width;
|
|
955
|
+
target.height = source.height;
|
|
956
|
+
target.rotated = source.rotated;
|
|
957
|
+
target.index = source.index;
|
|
958
|
+
target.subIndex = source.subIndex;
|
|
959
|
+
target.flags = source.flags;
|
|
960
|
+
target.score1 = source.score1;
|
|
961
|
+
target.score2 = source.score2;
|
|
962
|
+
target.sourceKind = source.sourceKind;
|
|
963
|
+
}
|
|
964
|
+
function setNodeRect(target, x, y, width, height, rotated, score1, score2) {
|
|
965
|
+
target.x = x;
|
|
966
|
+
target.y = y;
|
|
967
|
+
target.width = width;
|
|
968
|
+
target.height = height;
|
|
969
|
+
target.rotated = rotated;
|
|
970
|
+
target.score1 = score1;
|
|
971
|
+
target.score2 = score2;
|
|
972
|
+
}
|
|
973
|
+
function allowRotation(rect) {
|
|
974
|
+
return (rect.flags & NO_ROTATION) === 0;
|
|
975
|
+
}
|
|
976
|
+
function commonIntervalLength(startA, endA, startB, endB) {
|
|
977
|
+
if (endA < startB || endB < startA) return 0;
|
|
978
|
+
return Math.min(endA, endB) - Math.max(startA, startB);
|
|
979
|
+
}
|
|
980
|
+
function isContainedIn(left, right) {
|
|
981
|
+
return left.x >= right.x && left.y >= right.y && left.x + left.width <= right.x + right.width && left.y + left.height <= right.y + right.height;
|
|
982
|
+
}
|
|
983
|
+
//#endregion
|
|
984
|
+
//#region src/max-rects-packer-compat.ts
|
|
985
|
+
const DEFAULT_SETTINGS = {
|
|
986
|
+
pot: true,
|
|
987
|
+
mof: true,
|
|
988
|
+
padding: 2,
|
|
989
|
+
rotation: false,
|
|
990
|
+
minWidth: 16,
|
|
991
|
+
minHeight: 16,
|
|
992
|
+
maxWidth: 2048,
|
|
993
|
+
maxHeight: 2048,
|
|
994
|
+
square: false,
|
|
995
|
+
fast: true,
|
|
996
|
+
edgePadding: false,
|
|
997
|
+
duplicatePadding: false,
|
|
998
|
+
multiPage: false,
|
|
999
|
+
preserveInputOrderOnTie: false
|
|
1000
|
+
};
|
|
1001
|
+
let sizeScheme = null;
|
|
1002
|
+
var BinarySearchCompat = class {
|
|
1003
|
+
min;
|
|
1004
|
+
max;
|
|
1005
|
+
fuzziness;
|
|
1006
|
+
low;
|
|
1007
|
+
high;
|
|
1008
|
+
current;
|
|
1009
|
+
constructor(min, max, fuzziness, pot, mof) {
|
|
1010
|
+
this.pot = pot;
|
|
1011
|
+
this.mof = mof;
|
|
1012
|
+
this.fuzziness = pot ? 0 : fuzziness;
|
|
1013
|
+
if (pot) {
|
|
1014
|
+
this.min = Math.log(MaxRectsPackerCompat.getNextPowerOfTwo(min)) / Math.log(2);
|
|
1015
|
+
this.max = Math.log(MaxRectsPackerCompat.getNextPowerOfTwo(max)) / Math.log(2);
|
|
1016
|
+
} else if (mof) {
|
|
1017
|
+
this.min = min / 4;
|
|
1018
|
+
this.max = max / 4;
|
|
1019
|
+
} else {
|
|
1020
|
+
this.min = min;
|
|
1021
|
+
this.max = max;
|
|
1022
|
+
}
|
|
1023
|
+
this.low = this.min;
|
|
1024
|
+
this.high = this.max;
|
|
1025
|
+
this.current = this.min;
|
|
1194
1026
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1027
|
+
reset() {
|
|
1028
|
+
this.low = this.min;
|
|
1029
|
+
this.high = this.max;
|
|
1030
|
+
this.current = this.low + this.high >>> 1;
|
|
1031
|
+
return this.getCurrent();
|
|
1032
|
+
}
|
|
1033
|
+
next(failed) {
|
|
1034
|
+
if (this.low >= this.high) return -1;
|
|
1035
|
+
if (failed) this.low = this.current + 1;
|
|
1036
|
+
else this.high = this.current - 1;
|
|
1037
|
+
this.current = this.low + this.high >>> 1;
|
|
1038
|
+
if (Math.abs(this.low - this.high) < this.fuzziness) return -1;
|
|
1039
|
+
return this.getCurrent();
|
|
1040
|
+
}
|
|
1041
|
+
getCurrent() {
|
|
1042
|
+
if (this.pot) return Math.trunc(2 ** this.current);
|
|
1043
|
+
if (this.mof) return this.current * 4;
|
|
1044
|
+
return this.current;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
var MaxRectsPackerCompat = class MaxRectsPackerCompat {
|
|
1048
|
+
maxRects = new MaxRectsCompat();
|
|
1049
|
+
settings;
|
|
1050
|
+
constructor(settings = {}) {
|
|
1051
|
+
this.settings = {
|
|
1052
|
+
...DEFAULT_SETTINGS,
|
|
1053
|
+
...settings
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
static getNextPowerOfTwo(value) {
|
|
1057
|
+
if (Number.isInteger(value) && value > 0 && (value & value - 1) === 0) return value;
|
|
1058
|
+
let result = 1;
|
|
1059
|
+
const target = value - 1e-9;
|
|
1060
|
+
while (result < target) result <<= 1;
|
|
1061
|
+
return result;
|
|
1062
|
+
}
|
|
1063
|
+
pack(inputRects) {
|
|
1064
|
+
const rects = inputRects.map(cloneCompatRect);
|
|
1065
|
+
if (this.settings.fast) vectorSortCompat(rects, this.settings.preserveInputOrderOnTie ? this.settings.rotation ? compareNodeRectStable : compareNodeRect2Stable : this.settings.rotation ? compareNodeRect : compareNodeRect2);
|
|
1066
|
+
const padding = this.settings.padding;
|
|
1067
|
+
let hasDuplicatePadding = false;
|
|
1068
|
+
for (const rect of rects) {
|
|
1069
|
+
if (duplicatePadding(rect)) hasDuplicatePadding = true;
|
|
1070
|
+
if (this.settings.maxWidth - rect.width > padding || duplicatePadding(rect)) rect.width += padding;
|
|
1071
|
+
if (this.settings.maxHeight - rect.height > padding || duplicatePadding(rect)) rect.height += padding;
|
|
1214
1072
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1073
|
+
const pages = [];
|
|
1074
|
+
let remaining = rects;
|
|
1075
|
+
while (remaining.length > 0) {
|
|
1076
|
+
const page = this.packPage(remaining);
|
|
1077
|
+
if (!page) return null;
|
|
1078
|
+
if (this.settings.pot) {
|
|
1079
|
+
page.width = MaxRectsPackerCompat.getNextPowerOfTwo(page.width);
|
|
1080
|
+
page.height = MaxRectsPackerCompat.getNextPowerOfTwo(page.height);
|
|
1081
|
+
} else if (this.settings.mof) {
|
|
1082
|
+
page.width = Math.ceil(page.width / 4) * 4;
|
|
1083
|
+
page.height = Math.ceil(page.height / 4) * 4;
|
|
1084
|
+
}
|
|
1085
|
+
if (this.settings.square) {
|
|
1086
|
+
const side = Math.max(page.width, page.height);
|
|
1087
|
+
page.width = side;
|
|
1088
|
+
page.height = side;
|
|
1089
|
+
}
|
|
1090
|
+
pages.push(page);
|
|
1091
|
+
remaining = page.remainingRects.map(cloneCompatRect);
|
|
1092
|
+
}
|
|
1093
|
+
pages.sort(comparePage);
|
|
1094
|
+
for (const page of pages) {
|
|
1095
|
+
for (const rect of page.outputRects) {
|
|
1096
|
+
shrinkRectForPadding(rect, padding, this.settings.maxWidth, this.settings.maxHeight);
|
|
1097
|
+
if (hasDuplicatePadding) {
|
|
1098
|
+
if (rect.width !== page.width) rect.x += Math.floor(padding / 2);
|
|
1099
|
+
if (rect.height !== page.height) rect.y += Math.floor(padding / 2);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
for (const rect of page.remainingRects) shrinkRectForPadding(rect, padding, this.settings.maxWidth, this.settings.maxHeight);
|
|
1103
|
+
}
|
|
1104
|
+
return pages;
|
|
1217
1105
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1106
|
+
packPage(rects) {
|
|
1107
|
+
if (!sizeScheme) sizeScheme = initSizeScheme();
|
|
1108
|
+
const edgePadding = this.settings.edgePadding ? this.settings.padding : 0;
|
|
1109
|
+
let totalArea = 0;
|
|
1110
|
+
for (const rect of rects) totalArea += rect.width * rect.height;
|
|
1111
|
+
const candidates = sizeScheme.filter((entry) => entry.area >= totalArea && entry.width <= this.settings.maxWidth && entry.height <= this.settings.maxHeight);
|
|
1112
|
+
if (candidates.length === 0) candidates.push({
|
|
1113
|
+
width: this.settings.maxWidth,
|
|
1114
|
+
height: this.settings.maxHeight,
|
|
1115
|
+
area: 0,
|
|
1116
|
+
aspectRatio: 0,
|
|
1117
|
+
len: 0
|
|
1118
|
+
});
|
|
1119
|
+
let page = null;
|
|
1120
|
+
let selectedWidth = 0;
|
|
1121
|
+
let selectedHeight = 0;
|
|
1122
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
1123
|
+
selectedWidth = candidates[index].width;
|
|
1124
|
+
selectedHeight = candidates[index].height;
|
|
1125
|
+
page = this.packAtSize(index !== candidates.length - 1, selectedWidth - edgePadding, selectedHeight - edgePadding, rects);
|
|
1126
|
+
if (page) break;
|
|
1127
|
+
}
|
|
1128
|
+
if (page && !this.settings.pot && page.remainingRects.length === 0) {
|
|
1129
|
+
let bestRefined = null;
|
|
1130
|
+
if (this.settings.square) {
|
|
1131
|
+
const search = new BinarySearchCompat(Math.min(selectedWidth / 2, selectedHeight / 2), Math.max(selectedWidth, selectedHeight), this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
1132
|
+
let current = search.reset();
|
|
1133
|
+
while (current !== -1) {
|
|
1134
|
+
const refined = this.packAtSize(true, current - edgePadding, current - edgePadding, rects);
|
|
1135
|
+
bestRefined = getBestPage(bestRefined, refined);
|
|
1136
|
+
current = search.next(refined == null);
|
|
1137
|
+
}
|
|
1138
|
+
} else {
|
|
1139
|
+
const widthSearch = new BinarySearchCompat(selectedWidth / 2, selectedWidth, this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
1140
|
+
const heightSearch = new BinarySearchCompat(selectedHeight / 2, selectedHeight, this.settings.fast ? 25 : 15, this.settings.pot, this.settings.mof);
|
|
1141
|
+
let currentHeight = heightSearch.reset();
|
|
1142
|
+
let currentWidth = widthSearch.reset();
|
|
1143
|
+
while (true) {
|
|
1144
|
+
let bestForHeight = null;
|
|
1145
|
+
while (currentWidth !== -1) {
|
|
1146
|
+
const refined = this.packAtSize(true, currentWidth - edgePadding, currentHeight - edgePadding, rects);
|
|
1147
|
+
bestForHeight = getBestPage(bestForHeight, refined);
|
|
1148
|
+
currentWidth = widthSearch.next(refined == null);
|
|
1149
|
+
}
|
|
1150
|
+
bestRefined = getBestPage(bestRefined, bestForHeight);
|
|
1151
|
+
currentHeight = heightSearch.next(bestForHeight == null);
|
|
1152
|
+
if (currentHeight === -1) break;
|
|
1153
|
+
currentWidth = widthSearch.reset();
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
if (bestRefined) page = bestRefined;
|
|
1157
|
+
}
|
|
1158
|
+
return page;
|
|
1159
|
+
}
|
|
1160
|
+
packAtSize(requireFullFit, width, height, rects) {
|
|
1161
|
+
const methods = [
|
|
1162
|
+
MAX_RECTS_METHOD.BestShortSideFit,
|
|
1163
|
+
MAX_RECTS_METHOD.BestLongSideFit,
|
|
1164
|
+
MAX_RECTS_METHOD.BestAreaFit
|
|
1165
|
+
];
|
|
1166
|
+
let best = null;
|
|
1167
|
+
for (const method of methods) {
|
|
1168
|
+
this.maxRects.init(width, height, this.settings.rotation);
|
|
1169
|
+
let page;
|
|
1170
|
+
if (!this.settings.fast) page = this.maxRects.pack(rects, method);
|
|
1233
1171
|
else {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1172
|
+
const remaining = [];
|
|
1173
|
+
let index = 0;
|
|
1174
|
+
while (index < rects.length) {
|
|
1175
|
+
if (this.maxRects.insert(rects[index], method) == null) {
|
|
1176
|
+
while (index < rects.length) {
|
|
1177
|
+
remaining.push(cloneCompatRect(rects[index]));
|
|
1178
|
+
index += 1;
|
|
1179
|
+
}
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
index += 1;
|
|
1183
|
+
}
|
|
1184
|
+
page = this.maxRects.getResult();
|
|
1185
|
+
page.remainingRects = remaining;
|
|
1186
|
+
}
|
|
1187
|
+
if (!(requireFullFit && page.remainingRects.length > 0) && page.outputRects.length !== 0) best = getBestPage(best, page);
|
|
1188
|
+
}
|
|
1189
|
+
return best;
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
function vectorSortCompat(items, compare) {
|
|
1193
|
+
if (items.length <= 1) return;
|
|
1194
|
+
avmQuickSortCompat(items, 0, items.length - 1, compare);
|
|
1195
|
+
}
|
|
1196
|
+
function avmQuickSortCompat(items, initialLo, initialHi, compare) {
|
|
1197
|
+
if (initialLo >= initialHi) return;
|
|
1198
|
+
const stack = [];
|
|
1199
|
+
let lo = initialLo;
|
|
1200
|
+
let hi = initialHi;
|
|
1201
|
+
while (true) {
|
|
1202
|
+
const size = hi - lo + 1;
|
|
1203
|
+
if (size < 4) {
|
|
1204
|
+
if (size === 3) {
|
|
1205
|
+
if (compare(items[lo], items[lo + 1]) > 0) {
|
|
1206
|
+
swapCompat(items, lo, lo + 1);
|
|
1207
|
+
if (compare(items[lo + 1], items[lo + 2]) > 0) {
|
|
1208
|
+
swapCompat(items, lo + 1, lo + 2);
|
|
1209
|
+
if (compare(items[lo], items[lo + 1]) > 0) swapCompat(items, lo, lo + 1);
|
|
1210
|
+
}
|
|
1211
|
+
} else if (compare(items[lo + 1], items[lo + 2]) > 0) {
|
|
1212
|
+
swapCompat(items, lo + 1, lo + 2);
|
|
1213
|
+
if (compare(items[lo], items[lo + 1]) > 0) swapCompat(items, lo, lo + 1);
|
|
1214
|
+
}
|
|
1215
|
+
} else if (size === 2 && compare(items[lo], items[lo + 1]) > 0) swapCompat(items, lo, lo + 1);
|
|
1216
|
+
} else {
|
|
1217
|
+
swapCompat(items, lo + (size >> 1), lo);
|
|
1218
|
+
let left = lo;
|
|
1219
|
+
let right = hi + 1;
|
|
1220
|
+
while (true) {
|
|
1221
|
+
do
|
|
1222
|
+
left += 1;
|
|
1223
|
+
while (left <= hi && compare(items[left], items[lo]) <= 0);
|
|
1224
|
+
do
|
|
1225
|
+
right -= 1;
|
|
1226
|
+
while (right > lo && compare(items[right], items[lo]) >= 0);
|
|
1227
|
+
if (right < left) break;
|
|
1228
|
+
swapCompat(items, left, right);
|
|
1229
|
+
}
|
|
1230
|
+
swapCompat(items, lo, right);
|
|
1231
|
+
if (right - 1 - lo >= hi - left) {
|
|
1232
|
+
if (lo + 1 < right) stack.push({
|
|
1233
|
+
lo,
|
|
1234
|
+
hi: right - 1
|
|
1235
|
+
});
|
|
1236
|
+
if (left < hi) {
|
|
1237
|
+
lo = left;
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
if (left < hi) stack.push({
|
|
1242
|
+
lo: left,
|
|
1243
|
+
hi
|
|
1244
|
+
});
|
|
1245
|
+
if (lo + 1 < right) {
|
|
1246
|
+
hi = right - 1;
|
|
1238
1247
|
continue;
|
|
1239
1248
|
}
|
|
1240
|
-
imageBuffer = await encoder(_resolveImagePath(input.resource, pkg, options.basePath)).toBuffer();
|
|
1241
1249
|
}
|
|
1242
|
-
if (packedRect.rotated) imageBuffer = await encoder(imageBuffer).rotate(270).toBuffer();
|
|
1243
|
-
compositeInputs.push({
|
|
1244
|
-
input: imageBuffer,
|
|
1245
|
-
left: packedRect.x,
|
|
1246
|
-
top: packedRect.y
|
|
1247
|
-
});
|
|
1248
|
-
} catch {
|
|
1249
|
-
const message = `atlas: Could not read image "${input.id}" for compositing.`;
|
|
1250
|
-
if (options.strictOutput) throw new Error(message);
|
|
1251
|
-
logger.warn(message);
|
|
1252
1250
|
}
|
|
1251
|
+
if (stack.length === 0) return;
|
|
1252
|
+
const frame = stack.pop();
|
|
1253
|
+
lo = frame.lo;
|
|
1254
|
+
hi = frame.hi;
|
|
1253
1255
|
}
|
|
1254
|
-
const outputFile = `${options.outputPath}/${atlasFileName}`;
|
|
1255
|
-
await encoder({ create: {
|
|
1256
|
-
width: page.width,
|
|
1257
|
-
height: page.height,
|
|
1258
|
-
channels: 4,
|
|
1259
|
-
background: {
|
|
1260
|
-
r: 0,
|
|
1261
|
-
g: 0,
|
|
1262
|
-
b: 0,
|
|
1263
|
-
alpha: 0
|
|
1264
|
-
}
|
|
1265
|
-
} }).composite(compositeInputs).toFile(outputFile);
|
|
1266
|
-
logger.info(`atlas: Generated ${atlasFileName} (${page.width}x${page.height}, ${page.outputRects.length} sprites)`);
|
|
1267
|
-
}
|
|
1268
|
-
function inputToCompatRect(input, index) {
|
|
1269
|
-
const duplicatePadding = isImageResource$1(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
1270
|
-
return {
|
|
1271
|
-
x: 0,
|
|
1272
|
-
y: 0,
|
|
1273
|
-
width: input.width,
|
|
1274
|
-
height: input.height,
|
|
1275
|
-
rotated: false,
|
|
1276
|
-
index,
|
|
1277
|
-
subIndex: -1,
|
|
1278
|
-
flags: duplicatePadding ? COMPAT_NODE_RECT_FLAGS.DUPLICATE_PADDING : 0,
|
|
1279
|
-
score1: 0,
|
|
1280
|
-
score2: 0,
|
|
1281
|
-
sourceKind: input.sourceKind
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
function resolvePackedRectSize(input, width, height, rectRotated) {
|
|
1285
|
-
if (!rectRotated) return {
|
|
1286
|
-
width,
|
|
1287
|
-
height
|
|
1288
|
-
};
|
|
1289
|
-
return {
|
|
1290
|
-
width: input.height,
|
|
1291
|
-
height: input.width
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
function resolveDirectImageOutput(inputs, options) {
|
|
1295
|
-
if (!options.directSingleImageOutput || options.extractAlpha) return null;
|
|
1296
|
-
if (inputs.length !== 1) return null;
|
|
1297
|
-
const [input] = inputs;
|
|
1298
|
-
if (!input || input.sourceKind !== "image" || !isImageResource$1(input.resource)) return null;
|
|
1299
|
-
if (input.resource.getDuplicatePadding?.() === true) return null;
|
|
1300
|
-
if (input.width !== input.originalWidth || input.height !== input.originalHeight) return null;
|
|
1301
|
-
if (!resolveImageFileName$1(input.resource).toLowerCase().endsWith(".png")) return null;
|
|
1302
|
-
return input;
|
|
1303
1256
|
}
|
|
1304
|
-
function
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
const side = Math.max(resolvedWidth, resolvedHeight);
|
|
1309
|
-
resolvedWidth = side;
|
|
1310
|
-
resolvedHeight = side;
|
|
1311
|
-
}
|
|
1312
|
-
if (options.powerOfTwo) {
|
|
1313
|
-
resolvedWidth = nextPow2(resolvedWidth);
|
|
1314
|
-
resolvedHeight = nextPow2(resolvedHeight);
|
|
1315
|
-
}
|
|
1316
|
-
return {
|
|
1317
|
-
width: resolvedWidth,
|
|
1318
|
-
height: resolvedHeight
|
|
1319
|
-
};
|
|
1257
|
+
function swapCompat(items, left, right) {
|
|
1258
|
+
const value = items[left];
|
|
1259
|
+
items[left] = items[right];
|
|
1260
|
+
items[right] = value;
|
|
1320
1261
|
}
|
|
1321
|
-
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
sprite.setRectHeight(input.originalHeight);
|
|
1336
|
-
sprite.setRotated(false);
|
|
1337
|
-
sprite.setOffsetX(0);
|
|
1338
|
-
sprite.setOffsetY(0);
|
|
1339
|
-
sprite.setOriginalWidth(input.originalWidth);
|
|
1340
|
-
sprite.setOriginalHeight(input.originalHeight);
|
|
1341
|
-
sprite.setAtlas(atlasNode);
|
|
1342
|
-
atlasNode.addSprite(sprite);
|
|
1343
|
-
if (!encoder || !options.outputPath || !isImageResource$1(input.resource) || !options.basePath) return;
|
|
1344
|
-
if (options.mkdir) await options.mkdir(options.outputPath);
|
|
1345
|
-
const outputFile = `${options.outputPath}/${atlasFileName}`;
|
|
1346
|
-
const filePath = _resolveImagePath(input.resource, pkg, options.basePath);
|
|
1347
|
-
try {
|
|
1348
|
-
if (atlasSize.width === input.originalWidth && atlasSize.height === input.originalHeight) await encoder(filePath).png().toFile(outputFile);
|
|
1349
|
-
else {
|
|
1350
|
-
const imageBuffer = await encoder(filePath).png().toBuffer();
|
|
1351
|
-
await encoder({ create: {
|
|
1352
|
-
width: atlasSize.width,
|
|
1353
|
-
height: atlasSize.height,
|
|
1354
|
-
channels: 4,
|
|
1355
|
-
background: {
|
|
1356
|
-
r: 0,
|
|
1357
|
-
g: 0,
|
|
1358
|
-
b: 0,
|
|
1359
|
-
alpha: 0
|
|
1360
|
-
}
|
|
1361
|
-
} }).composite([{
|
|
1362
|
-
input: imageBuffer,
|
|
1363
|
-
left: 0,
|
|
1364
|
-
top: 0
|
|
1365
|
-
}]).png().toFile(outputFile);
|
|
1366
|
-
}
|
|
1367
|
-
} catch {
|
|
1368
|
-
const message = `atlas: Could not write direct-output atlas "${atlasFileName}".`;
|
|
1369
|
-
if (options.strictOutput) throw new Error(message);
|
|
1370
|
-
logger.warn(message);
|
|
1262
|
+
function initSizeScheme() {
|
|
1263
|
+
const result = [];
|
|
1264
|
+
for (let w = 5; w <= 13; w += 1) for (let h = 5; h <= 13; h += 1) {
|
|
1265
|
+
const width = 2 ** w;
|
|
1266
|
+
const height = 2 ** h;
|
|
1267
|
+
const area = width * height;
|
|
1268
|
+
const aspectRatio = width > height ? width / height : height / width;
|
|
1269
|
+
result.push({
|
|
1270
|
+
width,
|
|
1271
|
+
height,
|
|
1272
|
+
area,
|
|
1273
|
+
aspectRatio,
|
|
1274
|
+
len: Math.max(width, height)
|
|
1275
|
+
});
|
|
1371
1276
|
}
|
|
1277
|
+
result.sort(compareSizeScheme);
|
|
1278
|
+
return result;
|
|
1372
1279
|
}
|
|
1373
|
-
function
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
if (
|
|
1378
|
-
|
|
1280
|
+
function compareSizeScheme(left, right) {
|
|
1281
|
+
if (left.len < right.len) return -1;
|
|
1282
|
+
if (left.len > right.len) return 1;
|
|
1283
|
+
if (left.area < right.area) return -1;
|
|
1284
|
+
if (left.area > right.area) return 1;
|
|
1285
|
+
if (left.aspectRatio < right.aspectRatio) return -1;
|
|
1286
|
+
if (left.aspectRatio > right.aspectRatio) return 1;
|
|
1287
|
+
if (left.width > left.height) return -1;
|
|
1288
|
+
if (right.width > right.height) return 1;
|
|
1289
|
+
return 0;
|
|
1379
1290
|
}
|
|
1380
|
-
function
|
|
1381
|
-
|
|
1382
|
-
|
|
1291
|
+
function getBestPage(left, right) {
|
|
1292
|
+
if (!left) return right;
|
|
1293
|
+
if (!right) return left;
|
|
1294
|
+
return left.occupancy > right.occupancy ? left : right;
|
|
1383
1295
|
}
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
const suffix = branchName ? `_${branchName}` : "";
|
|
1387
|
-
if (isImageResource$1(resource)) return `${baseName}${suffix}${extname$1(resolveImageFileName$1(resource)) || ".png"}`;
|
|
1388
|
-
return `${baseName}${suffix}.png`;
|
|
1296
|
+
function comparePage(left, right) {
|
|
1297
|
+
return right.outputRects.length - left.outputRects.length;
|
|
1389
1298
|
}
|
|
1390
|
-
function
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
height
|
|
1394
|
-
};
|
|
1395
|
-
if (sizeMode === "multipleOf4") return {
|
|
1396
|
-
width: roundUpToMultiple(width, 4),
|
|
1397
|
-
height: roundUpToMultiple(height, 4)
|
|
1398
|
-
};
|
|
1399
|
-
return resolveDirectOutputAtlasSize(width, height, options);
|
|
1299
|
+
function compareNodeRect(left, right) {
|
|
1300
|
+
const leftEdge = left.width > left.height ? left.width : left.height;
|
|
1301
|
+
return (right.width > right.height ? right.width : right.height) - leftEdge;
|
|
1400
1302
|
}
|
|
1401
|
-
function
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1303
|
+
function compareNodeRectStable(left, right) {
|
|
1304
|
+
const delta = compareNodeRect(left, right);
|
|
1305
|
+
if (delta !== 0) return delta;
|
|
1306
|
+
if (left.sourceKind === "movieclip-frame" && right.sourceKind === "movieclip-frame") {
|
|
1307
|
+
const areaDelta = right.width * right.height - left.width * left.height;
|
|
1308
|
+
if (areaDelta !== 0) return areaDelta;
|
|
1309
|
+
const widthDelta = right.width - left.width;
|
|
1310
|
+
if (widthDelta !== 0) return widthDelta;
|
|
1311
|
+
}
|
|
1312
|
+
return left.index - right.index;
|
|
1404
1313
|
}
|
|
1405
|
-
function
|
|
1406
|
-
|
|
1407
|
-
const lastSlash = normalized.lastIndexOf("/");
|
|
1408
|
-
const lastDot = normalized.lastIndexOf(".");
|
|
1409
|
-
if (lastDot <= lastSlash) return "";
|
|
1410
|
-
return normalized.slice(lastDot);
|
|
1314
|
+
function compareNodeRect2(left, right) {
|
|
1315
|
+
return right.width - left.width;
|
|
1411
1316
|
}
|
|
1412
|
-
function
|
|
1413
|
-
const
|
|
1414
|
-
if (
|
|
1415
|
-
|
|
1317
|
+
function compareNodeRect2Stable(left, right) {
|
|
1318
|
+
const delta = compareNodeRect2(left, right);
|
|
1319
|
+
if (delta !== 0) return delta;
|
|
1320
|
+
if (left.sourceKind === "movieclip-frame" && right.sourceKind === "movieclip-frame") {
|
|
1321
|
+
const areaDelta = right.width * right.height - left.width * left.height;
|
|
1322
|
+
if (areaDelta !== 0) return areaDelta;
|
|
1323
|
+
const heightDelta = right.height - left.height;
|
|
1324
|
+
if (heightDelta !== 0) return heightDelta;
|
|
1325
|
+
}
|
|
1326
|
+
return left.index - right.index;
|
|
1416
1327
|
}
|
|
1417
|
-
function
|
|
1418
|
-
|
|
1419
|
-
return 2 ** Math.ceil(Math.log2(value));
|
|
1328
|
+
function duplicatePadding(rect) {
|
|
1329
|
+
return (rect.flags & COMPAT_NODE_RECT_FLAGS.DUPLICATE_PADDING) !== 0;
|
|
1420
1330
|
}
|
|
1421
|
-
function
|
|
1422
|
-
if (
|
|
1423
|
-
|
|
1331
|
+
function shrinkRectForPadding(rect, padding, maxWidth, maxHeight) {
|
|
1332
|
+
if (!rect.rotated) {
|
|
1333
|
+
if (maxWidth - rect.width > padding || duplicatePadding(rect)) rect.width -= padding;
|
|
1334
|
+
if (maxHeight - rect.height > padding || duplicatePadding(rect)) rect.height -= padding;
|
|
1335
|
+
} else {
|
|
1336
|
+
if (maxHeight - rect.width > padding || duplicatePadding(rect)) rect.width -= padding;
|
|
1337
|
+
if (maxWidth - rect.height > padding || duplicatePadding(rect)) rect.height -= padding;
|
|
1338
|
+
}
|
|
1424
1339
|
}
|
|
1425
|
-
function
|
|
1426
|
-
|
|
1427
|
-
ordered.sort((left, right) => {
|
|
1428
|
-
const leftId = left.getId();
|
|
1429
|
-
const rightId = right.getId();
|
|
1430
|
-
const leftOrder = leftId && orderMap.has(leftId) ? orderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1431
|
-
const rightOrder = rightId && orderMap.has(rightId) ? orderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1432
|
-
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
1433
|
-
const leftInputOrder = leftId && inputOrderMap.has(leftId) ? inputOrderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1434
|
-
const rightInputOrder = rightId && inputOrderMap.has(rightId) ? inputOrderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1435
|
-
if (leftInputOrder !== rightInputOrder) return leftInputOrder - rightInputOrder;
|
|
1436
|
-
return (leftId ?? "").localeCompare(rightId ?? "");
|
|
1437
|
-
});
|
|
1438
|
-
return ordered;
|
|
1340
|
+
function cloneCompatRect(rect) {
|
|
1341
|
+
return { ...rect };
|
|
1439
1342
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1343
|
+
//#endregion
|
|
1344
|
+
//#region src/atlas/packing.ts
|
|
1345
|
+
async function emitAtlasInputs(input) {
|
|
1346
|
+
const { doc, pkg, allResources, inputs, options, encoder, logger } = input;
|
|
1347
|
+
let totalPageCount = 0;
|
|
1348
|
+
let usedDirectOutput = false;
|
|
1349
|
+
const { autoInputs, fixedPageGroups, standaloneGroups, reservedPageIndexes } = groupStandaloneInputs(doc, inputs, options);
|
|
1350
|
+
const branchGroups = buildBranchAtlasGroups(doc, autoInputs, options);
|
|
1351
|
+
const branchPageOffsets = /* @__PURE__ */ new Map();
|
|
1352
|
+
for (const group of branchGroups) {
|
|
1353
|
+
const directOutput = fixedPageGroups.length === 0 && standaloneGroups.length === 0 ? resolveDirectImageOutput(group.inputs, options) : null;
|
|
1354
|
+
if (directOutput) {
|
|
1355
|
+
await emitDirectImageOutput(doc, pkg, directOutput, encoder, options, logger, group.branchName, group.branchOrdinal);
|
|
1356
|
+
usedDirectOutput = true;
|
|
1357
|
+
totalPageCount += 1;
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
const pageStart = reserveAutoPageStart(branchPageOffsets, group.branchOrdinal, reservedPageIndexes);
|
|
1361
|
+
const emittedPageCount = await emitPagedAtlasGroup(doc, pkg, allResources, group.inputs, {
|
|
1362
|
+
branchName: group.branchName,
|
|
1363
|
+
branchOrdinal: group.branchOrdinal,
|
|
1364
|
+
pageStart,
|
|
1365
|
+
fileNameAt: (pageIndex) => resolveAtlasOutputFileName(pkg, pageIndex, group.branchName),
|
|
1366
|
+
options,
|
|
1367
|
+
encoder,
|
|
1368
|
+
logger
|
|
1369
|
+
});
|
|
1370
|
+
totalPageCount += emittedPageCount;
|
|
1371
|
+
branchPageOffsets.set(group.branchOrdinal, pageStart + emittedPageCount);
|
|
1372
|
+
}
|
|
1373
|
+
for (const group of fixedPageGroups) {
|
|
1374
|
+
const emittedPageCount = await emitPagedAtlasGroup(doc, pkg, allResources, group.inputs, {
|
|
1375
|
+
branchName: group.branchName,
|
|
1376
|
+
branchOrdinal: group.branchOrdinal,
|
|
1377
|
+
pageStart: group.pageIndex,
|
|
1378
|
+
forceSinglePage: true,
|
|
1379
|
+
fileNameAt: () => resolveAtlasOutputFileName(pkg, group.pageIndex, group.branchName),
|
|
1380
|
+
options,
|
|
1381
|
+
encoder,
|
|
1382
|
+
logger
|
|
1383
|
+
});
|
|
1384
|
+
totalPageCount += emittedPageCount;
|
|
1385
|
+
}
|
|
1386
|
+
const standalonePageOffsets = new Map(branchPageOffsets);
|
|
1387
|
+
for (const group of fixedPageGroups) {
|
|
1388
|
+
const nextPageIndex = group.pageIndex + 1;
|
|
1389
|
+
if (nextPageIndex > (standalonePageOffsets.get(group.branchOrdinal) ?? 0)) standalonePageOffsets.set(group.branchOrdinal, nextPageIndex);
|
|
1390
|
+
}
|
|
1391
|
+
for (const group of standaloneGroups) {
|
|
1392
|
+
const emittedPageCount = await emitStandaloneAtlasGroup(doc, pkg, group, {
|
|
1393
|
+
atlasIndexStart: standalonePageOffsets.get(group.branchOrdinal) ?? 0,
|
|
1394
|
+
options,
|
|
1395
|
+
encoder,
|
|
1396
|
+
logger
|
|
1397
|
+
});
|
|
1398
|
+
totalPageCount += emittedPageCount;
|
|
1399
|
+
standalonePageOffsets.set(group.branchOrdinal, (standalonePageOffsets.get(group.branchOrdinal) ?? 0) + emittedPageCount);
|
|
1400
|
+
}
|
|
1401
|
+
if (usedDirectOutput) logger.info(`atlas: Direct output for single image package "${pkg.getName()}".`);
|
|
1402
|
+
logger.info(`atlas: Packed ${inputs.length} images into ${totalPageCount} atlas(es) for package "${pkg.getName()}".`);
|
|
1443
1403
|
}
|
|
1444
|
-
function
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1404
|
+
function buildBranchAtlasGroups(doc, inputs, options) {
|
|
1405
|
+
if (!options.separatedAtlasForBranch) return [{
|
|
1406
|
+
branchName: "",
|
|
1407
|
+
branchOrdinal: 0,
|
|
1408
|
+
inputs
|
|
1409
|
+
}];
|
|
1449
1410
|
const discoveredBranchNames = [...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName))];
|
|
1411
|
+
if (discoveredBranchNames.length === 0) return [{
|
|
1412
|
+
branchName: "",
|
|
1413
|
+
branchOrdinal: 0,
|
|
1414
|
+
inputs
|
|
1415
|
+
}];
|
|
1450
1416
|
const orderedBranchNames = doc.getRoot().listBranches().filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
1451
1417
|
for (const branchName of discoveredBranchNames) if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
let ordinal = 1;
|
|
1456
|
-
for (const branchName of orderedBranchNames) branchOrdinalByName.set(branchName, ordinal++);
|
|
1457
|
-
} else for (const branchName of orderedBranchNames) branchOrdinalByName.set(branchName, 0);
|
|
1418
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1419
|
+
groups.set("", []);
|
|
1420
|
+
for (const branchName of orderedBranchNames) groups.set(branchName, []);
|
|
1458
1421
|
for (const input of inputs) {
|
|
1459
1422
|
const branchName = getInputBranchName(input);
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
if (mode.kind === "standalone") {
|
|
1463
|
-
const key = `${branchName}\u0000${getPublishedItemId(input.resource)}`;
|
|
1464
|
-
const existing = standaloneGroups.get(key);
|
|
1465
|
-
if (existing) existing.inputs.push(input);
|
|
1466
|
-
else standaloneGroups.set(key, {
|
|
1467
|
-
resource: input.resource,
|
|
1468
|
-
branchName,
|
|
1469
|
-
branchOrdinal,
|
|
1470
|
-
sizeMode: mode.sizeMode,
|
|
1471
|
-
inputs: [input]
|
|
1472
|
-
});
|
|
1473
|
-
continue;
|
|
1474
|
-
}
|
|
1475
|
-
if (mode.kind === "page") {
|
|
1476
|
-
reservedPageIndexes.add(mode.pageIndex);
|
|
1477
|
-
const key = `${branchName}\u0000${mode.pageIndex}`;
|
|
1478
|
-
const existing = fixedInputsByPage.get(key);
|
|
1479
|
-
if (existing) existing.inputs.push(input);
|
|
1480
|
-
else fixedInputsByPage.set(key, {
|
|
1481
|
-
pageIndex: mode.pageIndex,
|
|
1482
|
-
branchName,
|
|
1483
|
-
branchOrdinal,
|
|
1484
|
-
inputs: [input]
|
|
1485
|
-
});
|
|
1486
|
-
continue;
|
|
1487
|
-
}
|
|
1488
|
-
autoInputs.push(input);
|
|
1423
|
+
const key = groups.has(branchName) ? branchName : "";
|
|
1424
|
+
groups.get(key).push(input);
|
|
1489
1425
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1426
|
+
const orderedKeys = [""];
|
|
1427
|
+
for (const branchName of orderedBranchNames) if ((groups.get(branchName)?.length ?? 0) > 0) orderedKeys.push(branchName);
|
|
1428
|
+
return orderedKeys.filter((branchName) => (groups.get(branchName)?.length ?? 0) > 0).map((branchName, index) => ({
|
|
1429
|
+
branchName,
|
|
1430
|
+
branchOrdinal: index,
|
|
1431
|
+
inputs: groups.get(branchName) ?? []
|
|
1432
|
+
}));
|
|
1496
1433
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
async function
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const
|
|
1508
|
-
const
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1434
|
+
function reserveAutoPageStart(branchPageOffsets, branchOrdinal, reservedPageIndexes) {
|
|
1435
|
+
let pageIndex = branchPageOffsets.get(branchOrdinal) ?? 0;
|
|
1436
|
+
while (branchOrdinal === 0 && reservedPageIndexes.has(pageIndex)) pageIndex += 1;
|
|
1437
|
+
return pageIndex;
|
|
1438
|
+
}
|
|
1439
|
+
async function emitPagedAtlasGroup(doc, pkg, allResources, inputs, context) {
|
|
1440
|
+
if (inputs.length === 0) return 0;
|
|
1441
|
+
const pages = packAtlasPages(inputs, context.options, context.forceSinglePage === true);
|
|
1442
|
+
assertPackedInputCoverage(pages, inputs.length, `package "${pkg.getName()}"`);
|
|
1443
|
+
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
1444
|
+
const page = pages[pageOffset];
|
|
1445
|
+
const pageIndex = context.pageStart + pageOffset;
|
|
1446
|
+
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(context.branchOrdinal, pageIndex)}`);
|
|
1447
|
+
atlasNode.setIndex(resolveAtlasIndex(context.branchOrdinal, pageIndex));
|
|
1448
|
+
atlasNode.setFile(context.fileNameAt(pageIndex));
|
|
1449
|
+
atlasNode.setWidth(page.width);
|
|
1450
|
+
atlasNode.setHeight(page.height);
|
|
1451
|
+
pkg.addAtlas(atlasNode);
|
|
1452
|
+
attachSpritesToAtlas(doc, allResources, inputs, page.outputRects, atlasNode);
|
|
1453
|
+
await writeAtlasPageImage(pkg, inputs, page, atlasNode.getFile(), context.encoder, context.options, context.logger);
|
|
1454
|
+
}
|
|
1455
|
+
return pages.length;
|
|
1456
|
+
}
|
|
1457
|
+
async function emitStandaloneAtlasGroup(doc, pkg, group, context) {
|
|
1458
|
+
if (group.inputs.length === 0) return 0;
|
|
1459
|
+
const pages = packAtlasPages(group.inputs, context.options, true, group.sizeMode === "npot" ? {
|
|
1460
|
+
powerOfTwo: false,
|
|
1461
|
+
multipleOfFour: false,
|
|
1462
|
+
square: false
|
|
1463
|
+
} : group.sizeMode === "multipleOf4" ? {
|
|
1464
|
+
powerOfTwo: false,
|
|
1465
|
+
multipleOfFour: true,
|
|
1466
|
+
square: false
|
|
1467
|
+
} : void 0);
|
|
1468
|
+
assertPackedInputCoverage(pages, group.inputs.length, `standalone texture in package "${pkg.getName()}"`);
|
|
1469
|
+
for (let pageOffset = 0; pageOffset < pages.length; pageOffset += 1) {
|
|
1470
|
+
const page = pages[pageOffset];
|
|
1471
|
+
const baseFileName = resolveStandaloneAtlasOutputFileName(pkg, group.resource, group.branchName);
|
|
1472
|
+
const atlasFileName = pages.length <= 1 ? baseFileName : insertFileNameSuffix(baseFileName, `_${pageOffset}`);
|
|
1473
|
+
const atlasIndex = context.atlasIndexStart + pageOffset;
|
|
1474
|
+
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(group.branchOrdinal, atlasIndex)}`);
|
|
1475
|
+
atlasNode.setIndex(resolveAtlasIndex(group.branchOrdinal, atlasIndex));
|
|
1476
|
+
atlasNode.setFile(atlasFileName);
|
|
1477
|
+
const standaloneSize = resolveStandaloneAtlasSize(page.width, page.height, group.sizeMode, context.options);
|
|
1478
|
+
atlasNode.setWidth(standaloneSize.width);
|
|
1479
|
+
atlasNode.setHeight(standaloneSize.height);
|
|
1480
|
+
pkg.addAtlas(atlasNode);
|
|
1481
|
+
attachSpritesToAtlas(doc, [], group.inputs, page.outputRects, atlasNode);
|
|
1482
|
+
await writeAtlasPageImage(pkg, group.inputs, {
|
|
1483
|
+
...page,
|
|
1484
|
+
width: standaloneSize.width,
|
|
1485
|
+
height: standaloneSize.height
|
|
1486
|
+
}, atlasFileName, context.encoder, context.options, context.logger);
|
|
1487
|
+
}
|
|
1488
|
+
return pages.length;
|
|
1489
|
+
}
|
|
1490
|
+
function packAtlasPages(inputs, options, forceSinglePage, sizeOverrides) {
|
|
1491
|
+
const hasDuplicatePadding = inputs.some((input) => {
|
|
1492
|
+
return isImageResource$1(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
1493
|
+
});
|
|
1494
|
+
return new MaxRectsPackerCompat({
|
|
1495
|
+
pot: sizeOverrides?.powerOfTwo ?? options.powerOfTwo,
|
|
1496
|
+
mof: sizeOverrides?.multipleOfFour ?? !options.powerOfTwo,
|
|
1497
|
+
padding: options.padding,
|
|
1498
|
+
rotation: options.allowRotation,
|
|
1499
|
+
minWidth: 16,
|
|
1500
|
+
minHeight: 16,
|
|
1501
|
+
maxWidth: options.maxSize,
|
|
1502
|
+
maxHeight: options.maxSize,
|
|
1503
|
+
square: sizeOverrides?.square ?? options.square,
|
|
1504
|
+
fast: options.fast,
|
|
1505
|
+
edgePadding: false,
|
|
1506
|
+
duplicatePadding: hasDuplicatePadding,
|
|
1507
|
+
multiPage: forceSinglePage ? false : options.multiPage,
|
|
1508
|
+
preserveInputOrderOnTie: options.preserveInputOrderOnTie
|
|
1509
|
+
}).pack(inputs.map((input, index) => inputToCompatRect(input, index))) ?? [];
|
|
1510
|
+
}
|
|
1511
|
+
function assertPackedInputCoverage(pages, inputCount, label) {
|
|
1512
|
+
const packedIndexes = /* @__PURE__ */ new Set();
|
|
1513
|
+
for (const page of pages) for (const outputRect of page.outputRects) packedIndexes.add(outputRect.index);
|
|
1514
|
+
const hasEveryInput = Array.from({ length: inputCount }, (_, index) => packedIndexes.has(index)).every(Boolean);
|
|
1515
|
+
if (packedIndexes.size !== inputCount || !hasEveryInput) throw new Error(`atlas: Could not pack every input for ${label}.`);
|
|
1516
|
+
}
|
|
1517
|
+
function attachSpritesToAtlas(doc, allResources, inputs, outputRects, atlasNode) {
|
|
1518
|
+
for (const packedRect of outputRects) {
|
|
1519
|
+
const input = inputs[packedRect.index];
|
|
1520
|
+
if (!input) continue;
|
|
1521
|
+
const packedSize = resolvePackedRectSize(input, packedRect.width, packedRect.height, packedRect.rotated);
|
|
1522
|
+
const sprite = doc.createSprite();
|
|
1523
|
+
sprite.setItemId(input.id);
|
|
1524
|
+
sprite.setRectX(packedRect.x);
|
|
1525
|
+
sprite.setRectY(packedRect.y);
|
|
1526
|
+
sprite.setRectWidth(packedSize.width);
|
|
1527
|
+
sprite.setRectHeight(packedSize.height);
|
|
1528
|
+
sprite.setRotated(packedRect.rotated);
|
|
1529
|
+
sprite.setOffsetX(input.offsetX);
|
|
1530
|
+
sprite.setOffsetY(input.offsetY);
|
|
1531
|
+
sprite.setOriginalWidth(input.originalWidth);
|
|
1532
|
+
sprite.setOriginalHeight(input.originalHeight);
|
|
1533
|
+
sprite.setAtlas(atlasNode);
|
|
1534
|
+
atlasNode.addSprite(sprite);
|
|
1535
|
+
}
|
|
1536
|
+
for (const resource of allResources) {
|
|
1537
|
+
if (!isFontResource$1(resource)) continue;
|
|
1538
|
+
const alias = resource.getExtras()?._fontSpriteAlias;
|
|
1539
|
+
if (!alias) continue;
|
|
1540
|
+
const imageSprite = outputRects.find((result) => inputs[result.index]?.id === alias.textureId);
|
|
1541
|
+
if (!imageSprite) continue;
|
|
1542
|
+
const imageInput = inputs[imageSprite.index];
|
|
1543
|
+
const fontSprite = doc.createSprite();
|
|
1544
|
+
fontSprite.setItemId(alias.fontId);
|
|
1545
|
+
fontSprite.setRectX(imageSprite.x);
|
|
1546
|
+
fontSprite.setRectY(imageSprite.y);
|
|
1547
|
+
fontSprite.setRectWidth(imageSprite.width);
|
|
1548
|
+
fontSprite.setRectHeight(imageSprite.height);
|
|
1549
|
+
fontSprite.setRotated(imageSprite.rotated);
|
|
1550
|
+
if (imageInput) {
|
|
1551
|
+
fontSprite.setOffsetX(imageInput.offsetX);
|
|
1552
|
+
fontSprite.setOffsetY(imageInput.offsetY);
|
|
1553
|
+
fontSprite.setOriginalWidth(imageInput.originalWidth);
|
|
1554
|
+
fontSprite.setOriginalHeight(imageInput.originalHeight);
|
|
1555
|
+
}
|
|
1556
|
+
fontSprite.setAtlas(atlasNode);
|
|
1557
|
+
atlasNode.addSprite(fontSprite);
|
|
1556
1558
|
}
|
|
1557
1559
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
return `${!branchName ? normalizedBasePath : /[\\/]assets$/i.test(normalizedBasePath) ? normalizedBasePath.replace(/([\\/])assets$/i, `$1assets_${branchName}`) : `${normalizedBasePath}_${branchName}`}/${pkg.getName()}${imgPath}${fileName}`;
|
|
1567
|
-
}
|
|
1568
|
-
/** Collect a single ImageResource into the inputs array. */
|
|
1569
|
-
async function _collectImage(resource, pkg, inputs, encoder, options, doTrim, logger) {
|
|
1570
|
-
let origW = resource.getWidth() ?? 0;
|
|
1571
|
-
let origH = resource.getHeight() ?? 0;
|
|
1572
|
-
const declaredWidth = origW;
|
|
1573
|
-
const declaredHeight = origH;
|
|
1574
|
-
let sourceHasAlpha = false;
|
|
1575
|
-
let rasterizedBuffer;
|
|
1576
|
-
if (encoder && options.basePath) {
|
|
1577
|
-
const filePath = _resolveImagePath(resource, pkg, options.basePath);
|
|
1560
|
+
async function writeAtlasPageImage(pkg, inputs, page, atlasFileName, encoder, options, logger) {
|
|
1561
|
+
if (!encoder || !options.outputPath) return;
|
|
1562
|
+
if (options.mkdir) await options.mkdir(options.outputPath);
|
|
1563
|
+
const compositeInputs = [];
|
|
1564
|
+
for (const packedRect of page.outputRects) {
|
|
1565
|
+
const input = inputs[packedRect.index];
|
|
1566
|
+
if (!input) continue;
|
|
1567
|
+
if (packedRect.width <= 0 || packedRect.height <= 0 || input.width <= 0 || input.height <= 0) continue;
|
|
1578
1568
|
try {
|
|
1579
|
-
|
|
1580
|
-
if (
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
}).png().toBuffer();
|
|
1593
|
-
sourceHasAlpha = true;
|
|
1569
|
+
let imageBuffer;
|
|
1570
|
+
if (input.trimBuffer) {
|
|
1571
|
+
imageBuffer = input.trimBuffer;
|
|
1572
|
+
if (imageBuffer.length === 0) continue;
|
|
1573
|
+
} else if (input.rasterizedBuffer) imageBuffer = input.rasterizedBuffer;
|
|
1574
|
+
else {
|
|
1575
|
+
if (!isImageResource$1(input.resource)) {
|
|
1576
|
+
const message = `atlas: Non-image input "${input.id}" is missing inline buffer.`;
|
|
1577
|
+
if (options.strictOutput) throw new Error(message);
|
|
1578
|
+
logger.warn(`${message} Skipping compositing.`);
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
imageBuffer = await encoder(resolveImagePath$1(input.resource, pkg, options.basePath)).toBuffer();
|
|
1594
1582
|
}
|
|
1583
|
+
if (packedRect.rotated) imageBuffer = await encoder(imageBuffer).rotate(270).toBuffer();
|
|
1584
|
+
compositeInputs.push({
|
|
1585
|
+
input: imageBuffer,
|
|
1586
|
+
left: packedRect.x,
|
|
1587
|
+
top: packedRect.y
|
|
1588
|
+
});
|
|
1595
1589
|
} catch {
|
|
1596
|
-
|
|
1597
|
-
if (
|
|
1598
|
-
|
|
1599
|
-
return;
|
|
1600
|
-
}
|
|
1590
|
+
const message = `atlas: Could not read image "${input.id}" for compositing.`;
|
|
1591
|
+
if (options.strictOutput) throw new Error(message);
|
|
1592
|
+
logger.warn(message);
|
|
1601
1593
|
}
|
|
1602
1594
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
offY = trimResult.offsetY;
|
|
1614
|
-
trimBuf = trimResult.buffer;
|
|
1615
|
-
} catch {
|
|
1616
|
-
logger.warn(`atlas: Could not trim "${filePath}", using original.`);
|
|
1595
|
+
const outputFile = `${options.outputPath}/${atlasFileName}`;
|
|
1596
|
+
await encoder({ create: {
|
|
1597
|
+
width: page.width,
|
|
1598
|
+
height: page.height,
|
|
1599
|
+
channels: 4,
|
|
1600
|
+
background: {
|
|
1601
|
+
r: 0,
|
|
1602
|
+
g: 0,
|
|
1603
|
+
b: 0,
|
|
1604
|
+
alpha: 0
|
|
1617
1605
|
}
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
id: getPublishedItemId(resource),
|
|
1621
|
-
width: packW,
|
|
1622
|
-
height: packH,
|
|
1623
|
-
originalWidth: origW,
|
|
1624
|
-
originalHeight: origH,
|
|
1625
|
-
offsetX: offX,
|
|
1626
|
-
offsetY: offY,
|
|
1627
|
-
resource,
|
|
1628
|
-
trimBuffer: trimBuf,
|
|
1629
|
-
rasterizedBuffer,
|
|
1630
|
-
sourceKind: "image"
|
|
1631
|
-
});
|
|
1606
|
+
} }).composite(compositeInputs).toFile(outputFile);
|
|
1607
|
+
logger.info(`atlas: Generated ${atlasFileName} (${page.width}x${page.height}, ${page.outputRects.length} sprites)`);
|
|
1632
1608
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1609
|
+
function inputToCompatRect(input, index) {
|
|
1610
|
+
const duplicatePadding = isImageResource$1(input.resource) && input.resource.getDuplicatePadding?.() === true;
|
|
1611
|
+
return {
|
|
1612
|
+
x: 0,
|
|
1613
|
+
y: 0,
|
|
1614
|
+
width: input.width,
|
|
1615
|
+
height: input.height,
|
|
1616
|
+
rotated: false,
|
|
1617
|
+
index,
|
|
1618
|
+
subIndex: -1,
|
|
1619
|
+
flags: duplicatePadding ? COMPAT_NODE_RECT_FLAGS.DUPLICATE_PADDING : 0,
|
|
1620
|
+
score1: 0,
|
|
1621
|
+
score2: 0,
|
|
1622
|
+
sourceKind: input.sourceKind
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
function resolvePackedRectSize(input, width, height, rectRotated) {
|
|
1626
|
+
if (!rectRotated) return {
|
|
1627
|
+
width,
|
|
1628
|
+
height
|
|
1629
|
+
};
|
|
1630
|
+
return {
|
|
1631
|
+
width: input.height,
|
|
1632
|
+
height: input.width
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function resolveDirectImageOutput(inputs, options) {
|
|
1636
|
+
if (!options.directSingleImageOutput || options.extractAlpha) return null;
|
|
1637
|
+
if (inputs.length !== 1) return null;
|
|
1638
|
+
const [input] = inputs;
|
|
1639
|
+
if (!input || input.sourceKind !== "image" || !isImageResource$1(input.resource)) return null;
|
|
1640
|
+
if (input.resource.getDuplicatePadding?.() === true) return null;
|
|
1641
|
+
if (input.width !== input.originalWidth || input.height !== input.originalHeight) return null;
|
|
1642
|
+
if (!resolveImageFileName$1(input.resource).toLowerCase().endsWith(".png")) return null;
|
|
1643
|
+
return input;
|
|
1644
|
+
}
|
|
1645
|
+
function resolveDirectOutputAtlasSize(width, height, options) {
|
|
1646
|
+
let resolvedWidth = width;
|
|
1647
|
+
let resolvedHeight = height;
|
|
1648
|
+
if (options.square) {
|
|
1649
|
+
const side = Math.max(resolvedWidth, resolvedHeight);
|
|
1650
|
+
resolvedWidth = side;
|
|
1651
|
+
resolvedHeight = side;
|
|
1638
1652
|
}
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1653
|
+
if (options.powerOfTwo) {
|
|
1654
|
+
resolvedWidth = nextPow2(resolvedWidth);
|
|
1655
|
+
resolvedHeight = nextPow2(resolvedHeight);
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
width: resolvedWidth,
|
|
1659
|
+
height: resolvedHeight
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
async function emitDirectImageOutput(doc, pkg, input, encoder, options, logger, branchName = "", branchOrdinal = 0) {
|
|
1663
|
+
const atlasFileName = resolveAtlasOutputFileName(pkg, 0, branchName);
|
|
1664
|
+
const atlasSize = resolveDirectOutputAtlasSize(input.originalWidth, input.originalHeight, options);
|
|
1665
|
+
const atlasNode = doc.createAtlas(`atlas${resolveAtlasIndex(branchOrdinal, 0)}`);
|
|
1666
|
+
atlasNode.setIndex(resolveAtlasIndex(branchOrdinal, 0));
|
|
1667
|
+
atlasNode.setFile(atlasFileName);
|
|
1668
|
+
atlasNode.setWidth(atlasSize.width);
|
|
1669
|
+
atlasNode.setHeight(atlasSize.height);
|
|
1670
|
+
pkg.addAtlas(atlasNode);
|
|
1671
|
+
const sprite = doc.createSprite();
|
|
1672
|
+
sprite.setItemId(input.id);
|
|
1673
|
+
sprite.setRectX(0);
|
|
1674
|
+
sprite.setRectY(0);
|
|
1675
|
+
sprite.setRectWidth(input.originalWidth);
|
|
1676
|
+
sprite.setRectHeight(input.originalHeight);
|
|
1677
|
+
sprite.setRotated(false);
|
|
1678
|
+
sprite.setOffsetX(0);
|
|
1679
|
+
sprite.setOffsetY(0);
|
|
1680
|
+
sprite.setOriginalWidth(input.originalWidth);
|
|
1681
|
+
sprite.setOriginalHeight(input.originalHeight);
|
|
1682
|
+
sprite.setAtlas(atlasNode);
|
|
1683
|
+
atlasNode.addSprite(sprite);
|
|
1684
|
+
if (!encoder || !options.outputPath || !isImageResource$1(input.resource) || !options.basePath) return;
|
|
1685
|
+
if (options.mkdir) await options.mkdir(options.outputPath);
|
|
1686
|
+
const outputFile = `${options.outputPath}/${atlasFileName}`;
|
|
1687
|
+
const filePath = resolveImagePath$1(input.resource, pkg, options.basePath);
|
|
1644
1688
|
try {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
if (!input) continue;
|
|
1664
|
-
inputs.push(input);
|
|
1665
|
-
spriteIdByTextureIndex.set(textureIndex, itemId);
|
|
1666
|
-
}
|
|
1667
|
-
for (let frameIndex = 0; frameIndex < frameMetas.length; frameIndex += 1) {
|
|
1668
|
-
const meta = frameMetas[frameIndex];
|
|
1669
|
-
const textureIndex = Number.isFinite(meta.textureIndex) ? meta.textureIndex : frameIndex;
|
|
1670
|
-
const frame = doc.createMovieFrame(`${mcId}_${frameIndex}`);
|
|
1671
|
-
frame.setRectX(meta.offsetX).setRectY(meta.offsetY).setRectWidth(meta.width).setRectHeight(meta.height).setAddDelay(meta.addDelay).setSpriteId(spriteIdByTextureIndex.get(textureIndex) ?? "");
|
|
1672
|
-
resource.addFrame(frame);
|
|
1673
|
-
}
|
|
1674
|
-
} else for (let frameIndex = 0; frameIndex < jta.frames.length; frameIndex += 1) {
|
|
1675
|
-
const itemId = `${mcId}_${frameIndex}`;
|
|
1676
|
-
const input = await _createMovieClipFrameInput(jta.frames[frameIndex], itemId, resource, encoder, options.strictOutput);
|
|
1677
|
-
if (!input) continue;
|
|
1678
|
-
inputs.push(input);
|
|
1679
|
-
const frame = doc.createMovieFrame(itemId);
|
|
1680
|
-
frame.setRectX(0).setRectY(0).setRectWidth(input.originalWidth).setRectHeight(input.originalHeight).setAddDelay(0).setSpriteId(itemId);
|
|
1681
|
-
resource.addFrame(frame);
|
|
1682
|
-
}
|
|
1683
|
-
if ((jta.meta?.width ?? 0) > 0 && (jta.meta?.height ?? 0) > 0) {
|
|
1684
|
-
resource.setWidth(jta.meta?.width ?? 0);
|
|
1685
|
-
resource.setHeight(jta.meta?.height ?? 0);
|
|
1689
|
+
if (atlasSize.width === input.originalWidth && atlasSize.height === input.originalHeight) await encoder(filePath).png().toFile(outputFile);
|
|
1690
|
+
else {
|
|
1691
|
+
const imageBuffer = await encoder(filePath).png().toBuffer();
|
|
1692
|
+
await encoder({ create: {
|
|
1693
|
+
width: atlasSize.width,
|
|
1694
|
+
height: atlasSize.height,
|
|
1695
|
+
channels: 4,
|
|
1696
|
+
background: {
|
|
1697
|
+
r: 0,
|
|
1698
|
+
g: 0,
|
|
1699
|
+
b: 0,
|
|
1700
|
+
alpha: 0
|
|
1701
|
+
}
|
|
1702
|
+
} }).composite([{
|
|
1703
|
+
input: imageBuffer,
|
|
1704
|
+
left: 0,
|
|
1705
|
+
top: 0
|
|
1706
|
+
}]).png().toFile(outputFile);
|
|
1686
1707
|
}
|
|
1687
1708
|
} catch {
|
|
1688
|
-
const message = `atlas: Could not
|
|
1709
|
+
const message = `atlas: Could not write direct-output atlas "${atlasFileName}".`;
|
|
1689
1710
|
if (options.strictOutput) throw new Error(message);
|
|
1690
|
-
logger.warn(
|
|
1711
|
+
logger.warn(message);
|
|
1691
1712
|
}
|
|
1692
1713
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
try {
|
|
1696
|
-
const meta = await encoder(buffer).metadata();
|
|
1697
|
-
const width = meta.width ?? 0;
|
|
1698
|
-
const height = meta.height ?? 0;
|
|
1699
|
-
if (width <= 0 || height <= 0) return null;
|
|
1700
|
-
return {
|
|
1701
|
-
id: itemId,
|
|
1702
|
-
width,
|
|
1703
|
-
height,
|
|
1704
|
-
originalWidth: width,
|
|
1705
|
-
originalHeight: height,
|
|
1706
|
-
offsetX: 0,
|
|
1707
|
-
offsetY: 0,
|
|
1708
|
-
resource,
|
|
1709
|
-
trimBuffer: buffer,
|
|
1710
|
-
sourceKind: "movieclip-frame"
|
|
1711
|
-
};
|
|
1712
|
-
} catch {
|
|
1713
|
-
if (strictOutput) throw new Error(`atlas: Could not decode MovieClip frame "${itemId}".`);
|
|
1714
|
-
return null;
|
|
1715
|
-
}
|
|
1714
|
+
function getInputBranchName(input) {
|
|
1715
|
+
return input.resource.getBranch?.() ?? "";
|
|
1716
1716
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
78,
|
|
1721
|
-
71,
|
|
1722
|
-
13,
|
|
1723
|
-
10,
|
|
1724
|
-
26,
|
|
1725
|
-
10
|
|
1726
|
-
]);
|
|
1727
|
-
function _extractJtaFrames(data) {
|
|
1728
|
-
const frames = [];
|
|
1729
|
-
let offset = 0;
|
|
1730
|
-
let firstPngOffset = -1;
|
|
1731
|
-
while (offset < data.length) {
|
|
1732
|
-
const sigIndex = _findPngSignature(data, offset);
|
|
1733
|
-
if (sigIndex === -1) break;
|
|
1734
|
-
if (firstPngOffset === -1) firstPngOffset = sigIndex;
|
|
1735
|
-
const end = _findPngEnd(data, sigIndex);
|
|
1736
|
-
if (end === -1) break;
|
|
1737
|
-
frames.push(data.subarray(sigIndex, end));
|
|
1738
|
-
offset = end;
|
|
1739
|
-
}
|
|
1740
|
-
if (firstPngOffset === -1 || frames.length === 0) return { frames: [] };
|
|
1741
|
-
return {
|
|
1742
|
-
frames,
|
|
1743
|
-
meta: _parseJtaHeader(data, firstPngOffset, frames.length)
|
|
1744
|
-
};
|
|
1717
|
+
function resolveAtlasIndex(branchOrdinal, pageIndex) {
|
|
1718
|
+
if (branchOrdinal <= 0) return pageIndex;
|
|
1719
|
+
return branchOrdinal * 100 + pageIndex;
|
|
1745
1720
|
}
|
|
1746
|
-
function
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
for (let sigIndex = 0; sigIndex < PNG_SIGNATURE.length; sigIndex += 1) if (data[index + sigIndex] !== PNG_SIGNATURE[sigIndex]) {
|
|
1750
|
-
matched = false;
|
|
1751
|
-
break;
|
|
1752
|
-
}
|
|
1753
|
-
if (matched) return index;
|
|
1754
|
-
}
|
|
1755
|
-
return -1;
|
|
1721
|
+
function resolveAtlasOutputFileName(pkg, pageIndex, branchName) {
|
|
1722
|
+
const suffix = branchName ? `_${branchName}` : "";
|
|
1723
|
+
return `${pkg.getPublishName() || pkg.getName()}_atlas${pageIndex}${suffix}.png`;
|
|
1756
1724
|
}
|
|
1757
|
-
function
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
if (pos + length + 4 > data.length) return -1;
|
|
1763
|
-
const isIEND = data[pos - 4] === 73 && data[pos - 3] === 69 && data[pos - 2] === 78 && data[pos - 1] === 68;
|
|
1764
|
-
pos += length + 4;
|
|
1765
|
-
if (isIEND) return pos;
|
|
1766
|
-
}
|
|
1767
|
-
return -1;
|
|
1725
|
+
function resolveStandaloneAtlasOutputFileName(pkg, resource, branchName) {
|
|
1726
|
+
const baseName = `${pkg.getPublishName() || pkg.getName()}_atlas_${getPublishedItemId(resource)}`;
|
|
1727
|
+
const suffix = branchName ? `_${branchName}` : "";
|
|
1728
|
+
if (isImageResource$1(resource)) return `${baseName}${suffix}${extname$1(resolveImageFileName$1(resource)) || ".png"}`;
|
|
1729
|
+
return `${baseName}${suffix}.png`;
|
|
1768
1730
|
}
|
|
1769
|
-
function
|
|
1770
|
-
if (
|
|
1771
|
-
const state = { offset: 0 };
|
|
1772
|
-
const end = Math.min(firstPngOffset, data.length);
|
|
1773
|
-
if (!_readUtfBE(data, state, end)) return void 0;
|
|
1774
|
-
const version = _readInt32BEAt(data, state, end);
|
|
1775
|
-
if (version == null) return void 0;
|
|
1776
|
-
const fpsRaw = _readInt8At(data, state, end);
|
|
1777
|
-
if (fpsRaw == null) return void 0;
|
|
1778
|
-
const fps = fpsRaw > 0 ? fpsRaw : 24;
|
|
1779
|
-
if (state.offset + 3 > end) return void 0;
|
|
1780
|
-
state.offset += 3;
|
|
1781
|
-
if (version < 102) return void 0;
|
|
1782
|
-
_readUint16BEAt(data, state, end);
|
|
1783
|
-
_readUint16BEAt(data, state, end);
|
|
1784
|
-
const width = _readUint16BEAt(data, state, end);
|
|
1785
|
-
const height = _readUint16BEAt(data, state, end);
|
|
1786
|
-
if (width == null || height == null) return void 0;
|
|
1787
|
-
const speedRaw = _readUint8At(data, state, end);
|
|
1788
|
-
const repeatDelayRaw = _readUint8At(data, state, end);
|
|
1789
|
-
const swingRaw = _readInt8At(data, state, end);
|
|
1790
|
-
const frameTableCount = _readInt16BEAt(data, state, end);
|
|
1791
|
-
if (speedRaw == null || repeatDelayRaw == null || swingRaw == null || frameTableCount == null) return void 0;
|
|
1792
|
-
const frames = [];
|
|
1793
|
-
for (let index = 0; index < frameTableCount; index += 1) {
|
|
1794
|
-
const delayRaw = _readInt16BEAt(data, state, end);
|
|
1795
|
-
const offsetX = _readInt16BEAt(data, state, end);
|
|
1796
|
-
const offsetY = _readInt16BEAt(data, state, end);
|
|
1797
|
-
const frameWidth = _readInt16BEAt(data, state, end);
|
|
1798
|
-
const frameHeight = _readInt16BEAt(data, state, end);
|
|
1799
|
-
const textureIndex = _readInt16BEAt(data, state, end);
|
|
1800
|
-
if (delayRaw == null || offsetX == null || offsetY == null || frameWidth == null || frameHeight == null || textureIndex == null) break;
|
|
1801
|
-
frames.push({
|
|
1802
|
-
addDelay: Math.trunc(1e3 / fps * delayRaw),
|
|
1803
|
-
offsetX,
|
|
1804
|
-
offsetY,
|
|
1805
|
-
width: frameWidth,
|
|
1806
|
-
height: frameHeight,
|
|
1807
|
-
textureIndex
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
return {
|
|
1811
|
-
interval: Math.trunc(1e3 / fps * (speedRaw || 1)),
|
|
1812
|
-
repeatDelay: Math.trunc(1e3 / fps * repeatDelayRaw),
|
|
1813
|
-
swing: swingRaw === 1,
|
|
1731
|
+
function resolveStandaloneAtlasSize(width, height, sizeMode, options) {
|
|
1732
|
+
if (sizeMode === "npot") return {
|
|
1814
1733
|
width,
|
|
1815
|
-
height
|
|
1816
|
-
frames: frames.length === 0 && frameCount > 0 ? [] : frames
|
|
1734
|
+
height
|
|
1817
1735
|
};
|
|
1736
|
+
if (sizeMode === "multipleOf4") return {
|
|
1737
|
+
width: roundUpToMultiple(width, 4),
|
|
1738
|
+
height: roundUpToMultiple(height, 4)
|
|
1739
|
+
};
|
|
1740
|
+
return resolveDirectOutputAtlasSize(width, height, options);
|
|
1818
1741
|
}
|
|
1819
|
-
function
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
return
|
|
1825
|
-
}
|
|
1826
|
-
function _readUint8At(data, state, end) {
|
|
1827
|
-
if (state.offset + 1 > end) return null;
|
|
1828
|
-
const value = data[state.offset];
|
|
1829
|
-
state.offset += 1;
|
|
1830
|
-
return value ?? 0;
|
|
1831
|
-
}
|
|
1832
|
-
function _readInt8At(data, state, end) {
|
|
1833
|
-
if (state.offset + 1 > end) return null;
|
|
1834
|
-
const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt8(state.offset);
|
|
1835
|
-
state.offset += 1;
|
|
1836
|
-
return value;
|
|
1837
|
-
}
|
|
1838
|
-
function _readUint16BEAt(data, state, end) {
|
|
1839
|
-
if (state.offset + 2 > end) return null;
|
|
1840
|
-
const value = _readUint16BE(data, state.offset);
|
|
1841
|
-
state.offset += 2;
|
|
1842
|
-
return value;
|
|
1742
|
+
function extname$1(fileName) {
|
|
1743
|
+
const normalized = fileName.replace(/\\/g, "/");
|
|
1744
|
+
const lastSlash = normalized.lastIndexOf("/");
|
|
1745
|
+
const lastDot = normalized.lastIndexOf(".");
|
|
1746
|
+
if (lastDot <= lastSlash) return "";
|
|
1747
|
+
return normalized.slice(lastDot);
|
|
1843
1748
|
}
|
|
1844
|
-
function
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
return value;
|
|
1749
|
+
function insertFileNameSuffix(fileName, suffix) {
|
|
1750
|
+
const extension = extname$1(fileName);
|
|
1751
|
+
if (!extension) return `${fileName}${suffix}`;
|
|
1752
|
+
return `${fileName.slice(0, -extension.length)}${suffix}${extension}`;
|
|
1849
1753
|
}
|
|
1850
|
-
function
|
|
1851
|
-
if (
|
|
1852
|
-
|
|
1853
|
-
state.offset += 4;
|
|
1854
|
-
return value;
|
|
1754
|
+
function nextPow2(value) {
|
|
1755
|
+
if (value <= 1) return 1;
|
|
1756
|
+
return 2 ** Math.ceil(Math.log2(value));
|
|
1855
1757
|
}
|
|
1856
|
-
function
|
|
1857
|
-
if (
|
|
1858
|
-
return
|
|
1758
|
+
function roundUpToMultiple(value, base) {
|
|
1759
|
+
if (value <= 0) return 0;
|
|
1760
|
+
return Math.ceil(value / base) * base;
|
|
1859
1761
|
}
|
|
1860
|
-
function
|
|
1861
|
-
|
|
1862
|
-
|
|
1762
|
+
function sortResourcesByOrder(resources, orderMap, inputOrderMap) {
|
|
1763
|
+
const ordered = [...resources];
|
|
1764
|
+
ordered.sort((left, right) => {
|
|
1765
|
+
const leftId = left.getId();
|
|
1766
|
+
const rightId = right.getId();
|
|
1767
|
+
const leftOrder = leftId && orderMap.has(leftId) ? orderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1768
|
+
const rightOrder = rightId && orderMap.has(rightId) ? orderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1769
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
1770
|
+
const leftInputOrder = leftId && inputOrderMap.has(leftId) ? inputOrderMap.get(leftId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1771
|
+
const rightInputOrder = rightId && inputOrderMap.has(rightId) ? inputOrderMap.get(rightId) ?? Number.MAX_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
|
|
1772
|
+
if (leftInputOrder !== rightInputOrder) return leftInputOrder - rightInputOrder;
|
|
1773
|
+
return (leftId ?? "").localeCompare(rightId ?? "");
|
|
1774
|
+
});
|
|
1775
|
+
return ordered;
|
|
1863
1776
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
if (textureId) {
|
|
1868
|
-
const fontId = fontRes.getId();
|
|
1869
|
-
fontRes.setExtras({
|
|
1870
|
-
...fontRes.getExtras(),
|
|
1871
|
-
_fontSpriteAlias: {
|
|
1872
|
-
fontId,
|
|
1873
|
-
textureId
|
|
1874
|
-
}
|
|
1875
|
-
});
|
|
1876
|
-
}
|
|
1877
|
-
if (options.readFileRaw && options.basePath) {
|
|
1878
|
-
const fontName = resolveFontFileName(fontRes.getName());
|
|
1879
|
-
const fontPath = fontRes.getPath() ?? "/";
|
|
1880
|
-
const pkgName = pkg.getName();
|
|
1881
|
-
const fntFile = `${options.basePath}/${pkgName}${fontPath}${fontName}`;
|
|
1882
|
-
try {
|
|
1883
|
-
const fntData = await options.readFileRaw(fntFile);
|
|
1884
|
-
const fntParsed = _parseFnt(new TextDecoder().decode(fntData));
|
|
1885
|
-
for (const glyph of fontRes.listGlyphs()) fontRes.removeGlyph(glyph);
|
|
1886
|
-
fontRes.setTtf(fntParsed.hasFace).setTint(fntParsed.colored).setAutoScale(fntParsed.resizable).setHasChannel(fntParsed.hasChannel).setFontSize(fntParsed.fontSize).setXAdvance(fntParsed.xadvance).setLineHeight(fntParsed.lineHeight);
|
|
1887
|
-
for (const item of fntParsed.glyphs) {
|
|
1888
|
-
const glyph = doc.createFontGlyph(`${fontRes.getId()}_${item.charId}`);
|
|
1889
|
-
glyph.setCharId(item.charId).setChar(item.charId > 0 ? String.fromCodePoint(item.charId) : "").setImg(item.img ?? "").setX(item.x).setY(item.y).setXOffset(item.xoffset).setYOffset(item.yoffset).setWidth(item.width).setHeight(item.height).setAdvance(item.xadvance).setLineHeight(fntParsed.lineHeight).setChannel(item.channel);
|
|
1890
|
-
fontRes.addGlyph(glyph);
|
|
1891
|
-
}
|
|
1892
|
-
} catch {}
|
|
1893
|
-
}
|
|
1777
|
+
function getResourceTextureSetMode(resource) {
|
|
1778
|
+
if (isImageResource$1(resource)) return parseTextureSetMode(resource.getTextureSetMode?.());
|
|
1779
|
+
return parseTextureSetMode(resource.getTextureSetMode?.());
|
|
1894
1780
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
width: parseInt(attrs.width, 10) || 0,
|
|
1939
|
-
height: parseInt(attrs.height, 10) || 0,
|
|
1940
|
-
xadvance: parseInt(attrs.xadvance, 10) || 0,
|
|
1941
|
-
channel: chnl
|
|
1942
|
-
});
|
|
1943
|
-
break;
|
|
1944
|
-
}
|
|
1781
|
+
function groupStandaloneInputs(doc, inputs, options) {
|
|
1782
|
+
const autoInputs = [];
|
|
1783
|
+
const fixedInputsByPage = /* @__PURE__ */ new Map();
|
|
1784
|
+
const standaloneGroups = /* @__PURE__ */ new Map();
|
|
1785
|
+
const reservedPageIndexes = /* @__PURE__ */ new Set();
|
|
1786
|
+
const discoveredBranchNames = [...new Set(inputs.map((input) => getInputBranchName(input)).filter((branchName) => !!branchName))];
|
|
1787
|
+
const orderedBranchNames = doc.getRoot().listBranches().filter((branchName) => discoveredBranchNames.includes(branchName));
|
|
1788
|
+
for (const branchName of discoveredBranchNames) if (!orderedBranchNames.includes(branchName)) orderedBranchNames.push(branchName);
|
|
1789
|
+
const branchOrdinalByName = /* @__PURE__ */ new Map();
|
|
1790
|
+
branchOrdinalByName.set("", 0);
|
|
1791
|
+
if (options.separatedAtlasForBranch) {
|
|
1792
|
+
let ordinal = 1;
|
|
1793
|
+
for (const branchName of orderedBranchNames) branchOrdinalByName.set(branchName, ordinal++);
|
|
1794
|
+
} else for (const branchName of orderedBranchNames) branchOrdinalByName.set(branchName, 0);
|
|
1795
|
+
for (const input of inputs) {
|
|
1796
|
+
const branchName = getInputBranchName(input);
|
|
1797
|
+
const branchOrdinal = branchOrdinalByName.get(branchName) ?? 0;
|
|
1798
|
+
const mode = getResourceTextureSetMode(input.resource);
|
|
1799
|
+
if (mode.kind === "standalone") {
|
|
1800
|
+
const key = `${branchName}\u0000${getPublishedItemId(input.resource)}`;
|
|
1801
|
+
const existing = standaloneGroups.get(key);
|
|
1802
|
+
if (existing) existing.inputs.push(input);
|
|
1803
|
+
else standaloneGroups.set(key, {
|
|
1804
|
+
resource: input.resource,
|
|
1805
|
+
branchName,
|
|
1806
|
+
branchOrdinal,
|
|
1807
|
+
sizeMode: mode.sizeMode,
|
|
1808
|
+
inputs: [input]
|
|
1809
|
+
});
|
|
1810
|
+
continue;
|
|
1811
|
+
}
|
|
1812
|
+
if (mode.kind === "page") {
|
|
1813
|
+
reservedPageIndexes.add(mode.pageIndex);
|
|
1814
|
+
const key = `${branchName}\u0000${mode.pageIndex}`;
|
|
1815
|
+
const existing = fixedInputsByPage.get(key);
|
|
1816
|
+
if (existing) existing.inputs.push(input);
|
|
1817
|
+
else fixedInputsByPage.set(key, {
|
|
1818
|
+
pageIndex: mode.pageIndex,
|
|
1819
|
+
branchName,
|
|
1820
|
+
branchOrdinal,
|
|
1821
|
+
inputs: [input]
|
|
1822
|
+
});
|
|
1823
|
+
continue;
|
|
1945
1824
|
}
|
|
1825
|
+
autoInputs.push(input);
|
|
1946
1826
|
}
|
|
1947
1827
|
return {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
fontSize,
|
|
1953
|
-
xadvance: globalXadvance,
|
|
1954
|
-
lineHeight,
|
|
1955
|
-
glyphs
|
|
1828
|
+
autoInputs,
|
|
1829
|
+
fixedPageGroups: [...fixedInputsByPage.values()].sort((left, right) => left.branchOrdinal - right.branchOrdinal || left.pageIndex - right.pageIndex),
|
|
1830
|
+
standaloneGroups: [...standaloneGroups.values()].sort((left, right) => left.branchOrdinal - right.branchOrdinal || getPublishedItemId(left.resource).localeCompare(getPublishedItemId(right.resource))),
|
|
1831
|
+
reservedPageIndexes
|
|
1956
1832
|
};
|
|
1957
1833
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
const
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
const refId = match[1] ?? "";
|
|
1986
|
-
if (refId) target.add(refId);
|
|
1834
|
+
//#endregion
|
|
1835
|
+
//#region src/atlas.ts
|
|
1836
|
+
const ATLAS_DEFAULTS = {
|
|
1837
|
+
maxSize: 2048,
|
|
1838
|
+
fast: true,
|
|
1839
|
+
allowRotation: true,
|
|
1840
|
+
padding: 1,
|
|
1841
|
+
powerOfTwo: false,
|
|
1842
|
+
square: false,
|
|
1843
|
+
multiPage: true,
|
|
1844
|
+
trimImage: false,
|
|
1845
|
+
preserveInputOrderOnTie: false,
|
|
1846
|
+
directSingleImageOutput: false,
|
|
1847
|
+
extractAlpha: false,
|
|
1848
|
+
separatedAtlasForBranch: false,
|
|
1849
|
+
strictOutput: false
|
|
1850
|
+
};
|
|
1851
|
+
function getSelectedSkeletonDependencyImageIds(resources) {
|
|
1852
|
+
const imageIds = /* @__PURE__ */ new Set();
|
|
1853
|
+
const resourcesById = new Map(resources.map((resource) => [resource.getId(), resource]));
|
|
1854
|
+
for (const resource of resources) {
|
|
1855
|
+
if (!isSkeletonResource$1(resource)) continue;
|
|
1856
|
+
for (const requiredId of resource.getRequireIds()) {
|
|
1857
|
+
if (!requiredId) continue;
|
|
1858
|
+
const required = resourcesById.get(requiredId);
|
|
1859
|
+
if (required && isImageResource$1(required)) imageIds.add(requiredId);
|
|
1860
|
+
}
|
|
1987
1861
|
}
|
|
1862
|
+
return imageIds;
|
|
1988
1863
|
}
|
|
1989
|
-
function
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1864
|
+
async function resolveEditorCompatibleResourceOrder(pkg, allResources, options) {
|
|
1865
|
+
const pkgId = pkg.getId();
|
|
1866
|
+
const resourceMap = new Map(allResources.map((resource) => [resource.getId(), resource]));
|
|
1867
|
+
const ordered = [];
|
|
1868
|
+
const added = /* @__PURE__ */ new Set();
|
|
1869
|
+
const componentStack = [];
|
|
1870
|
+
async function addResource(resource) {
|
|
1871
|
+
if (!resource) return;
|
|
1872
|
+
const resourceId = resource.getId();
|
|
1873
|
+
if (!resourceId || added.has(resourceId)) return;
|
|
1874
|
+
added.add(resourceId);
|
|
1875
|
+
ordered.push(resource);
|
|
1876
|
+
if (isFontResource$1(resource)) {
|
|
1877
|
+
await addResource(resourceMap.get(resource.getTextureId?.() ?? ""));
|
|
1878
|
+
if (options.readFileRaw && options.basePath) {
|
|
1879
|
+
const fontName = resolveFontFileName(resource.getName());
|
|
1880
|
+
const fontPath = resource.getPath() ?? "/";
|
|
1881
|
+
const fntFile = `${options.basePath}/${pkg.getName()}${fontPath}${fontName}`;
|
|
1882
|
+
try {
|
|
1883
|
+
const fntData = await options.readFileRaw(fntFile);
|
|
1884
|
+
const fntText = new TextDecoder().decode(fntData);
|
|
1885
|
+
for (const line of fntText.split(/\r?\n/)) {
|
|
1886
|
+
const imgMatch = line.match(/\bimg=(\w+)/);
|
|
1887
|
+
if (imgMatch) await addResource(resourceMap.get(imgMatch[1] ?? ""));
|
|
1888
|
+
}
|
|
1889
|
+
} catch {}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
if (isComponentResource$1(resource)) componentStack.push(resource);
|
|
1993
1893
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1894
|
+
async function addResourceByLocalUiUrl(value) {
|
|
1895
|
+
if (!value || typeof value !== "string" || !value.startsWith("ui://")) return;
|
|
1896
|
+
const normalized = value.slice(5).split(",")[0] ?? "";
|
|
1897
|
+
if (!normalized) return;
|
|
1898
|
+
let resourceId = "";
|
|
1899
|
+
const slashIndex = normalized.indexOf("/");
|
|
1900
|
+
if (slashIndex >= 0) {
|
|
1901
|
+
if (normalized.slice(0, slashIndex) !== pkgId) return;
|
|
1902
|
+
resourceId = normalized.slice(slashIndex + 1);
|
|
1903
|
+
} else if (normalized.length > 8) {
|
|
1904
|
+
if (normalized.slice(0, 8) !== pkgId) return;
|
|
1905
|
+
resourceId = normalized.slice(8);
|
|
1906
|
+
}
|
|
1907
|
+
if (!resourceId) return;
|
|
1908
|
+
await addResource(resourceMap.get(resourceId));
|
|
1909
|
+
}
|
|
1910
|
+
async function addGearIconResources(gear) {
|
|
1911
|
+
if (gear.getGearType?.() !== GearType.Icon) return;
|
|
1912
|
+
const values = gear.getValues?.();
|
|
1913
|
+
if (typeof values === "string" && values) for (const value of values.split("|")) await addResourceByLocalUiUrl(value.trim());
|
|
1914
|
+
const defaultValue = gear.getDefaultValue?.();
|
|
1915
|
+
if (typeof defaultValue === "string") await addResourceByLocalUiUrl(defaultValue);
|
|
1916
|
+
}
|
|
1917
|
+
for (const resource of allResources) if (resource.getExported()) await addResource(resource);
|
|
1918
|
+
while (componentStack.length > 0) {
|
|
1919
|
+
const component = componentStack.pop();
|
|
1920
|
+
if (!component) continue;
|
|
1921
|
+
for (const child of component.listChildren()) {
|
|
1922
|
+
const refChild = child;
|
|
1923
|
+
await addResource(resourceMap.get(refChild.getSrc?.() ?? ""));
|
|
1924
|
+
for (const ref of [
|
|
1925
|
+
refChild.getUrl?.(),
|
|
1926
|
+
refChild.getDefaultItem?.(),
|
|
1927
|
+
refChild.getIcon?.(),
|
|
1928
|
+
refChild.getSelectedIcon?.(),
|
|
1929
|
+
refChild.getFont?.(),
|
|
1930
|
+
refChild.getDropdown?.(),
|
|
1931
|
+
refChild.getVtScrollBarRes?.(),
|
|
1932
|
+
refChild.getHzScrollBarRes?.(),
|
|
1933
|
+
refChild.getHeaderRes?.(),
|
|
1934
|
+
refChild.getFooterRes?.(),
|
|
1935
|
+
refChild.getSound?.(),
|
|
1936
|
+
refChild.getInstanceIcon?.(),
|
|
1937
|
+
refChild.getInstanceSelectedIcon?.()
|
|
1938
|
+
]) await addResourceByLocalUiUrl(ref);
|
|
1939
|
+
for (const item of refChild.getInstanceComboItems?.() ?? []) await addResourceByLocalUiUrl(item.icon ?? void 0);
|
|
1940
|
+
for (const item of refChild.getListItems?.() ?? []) {
|
|
1941
|
+
await addResourceByLocalUiUrl(item.icon ?? void 0);
|
|
1942
|
+
await addResourceByLocalUiUrl(item.url ?? void 0);
|
|
1943
|
+
}
|
|
1944
|
+
for (const gear of refChild.listGears?.() ?? []) await addGearIconResources(gear);
|
|
1945
|
+
}
|
|
1946
|
+
for (const ref of [
|
|
1947
|
+
component.getDropdown?.(),
|
|
1948
|
+
component.getVtScrollBarRes?.(),
|
|
1949
|
+
component.getHzScrollBarRes?.(),
|
|
1950
|
+
component.getHeaderRes?.(),
|
|
1951
|
+
component.getFooterRes?.(),
|
|
1952
|
+
component.getSound?.()
|
|
1953
|
+
]) await addResourceByLocalUiUrl(ref);
|
|
1954
|
+
for (const transition of component.listTransitions?.() ?? []) for (const item of transition.listItems?.() ?? []) {
|
|
1955
|
+
const actionType = item.getActionType?.();
|
|
1956
|
+
if (actionType !== TransitionActionType.Sound && actionType !== TransitionActionType.Icon) continue;
|
|
1957
|
+
for (const value of [item.getStartValue?.(), item.getEndValue?.()]) if (Array.isArray(value)) {
|
|
1958
|
+
for (const entry of value) if (typeof entry === "string") await addResourceByLocalUiUrl(entry);
|
|
1959
|
+
} else if (typeof value === "string") await addResourceByLocalUiUrl(value);
|
|
1960
|
+
}
|
|
1997
1961
|
}
|
|
1962
|
+
for (const resource of allResources) await addResource(resource);
|
|
1963
|
+
return ordered;
|
|
1998
1964
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
1965
|
+
/**
|
|
1966
|
+
* Packs image resources into texture atlases.
|
|
1967
|
+
*
|
|
1968
|
+
* This transform performs MaxRects bin-packing on all ImageResource items
|
|
1969
|
+
* within each package, creating Atlas and Sprite property nodes. When an
|
|
1970
|
+
* a raster backend is provided, it also composites the actual PNG files.
|
|
1971
|
+
*
|
|
1972
|
+
* When `trimImage` is enabled and encoder is available, transparent pixels
|
|
1973
|
+
* at image edges are trimmed before packing. The trimmed offset and original
|
|
1974
|
+
* dimensions are stored in the Sprite nodes for runtime reconstruction.
|
|
1975
|
+
*
|
|
1976
|
+
* ```ts
|
|
1977
|
+
* import sharp from 'sharp';
|
|
1978
|
+
* await doc.transform(atlas({
|
|
1979
|
+
* encoder: sharp,
|
|
1980
|
+
* maxSize: 2048,
|
|
1981
|
+
* trimImage: true,
|
|
1982
|
+
* basePath: './assets/',
|
|
1983
|
+
* outputPath: './dist/',
|
|
1984
|
+
* }));
|
|
1985
|
+
* ```
|
|
1986
|
+
*/
|
|
1987
|
+
function atlas(_options = {}) {
|
|
1988
|
+
const options = {
|
|
1989
|
+
...ATLAS_DEFAULTS,
|
|
1990
|
+
..._options
|
|
1991
|
+
};
|
|
1992
|
+
return createTransform("atlas", async (doc) => {
|
|
1993
|
+
const root = doc.getRoot();
|
|
1994
|
+
const logger = doc.getLogger();
|
|
1995
|
+
const encoder = options.encoder;
|
|
1996
|
+
const doTrim = options.trimImage && !!encoder && !!options.basePath;
|
|
1997
|
+
const packageFilter = options.packages ? new Set(options.packages) : null;
|
|
1998
|
+
for (const pkg of root.listPackages()) {
|
|
1999
|
+
if (packageFilter && !packageFilter.has(pkg.getName())) continue;
|
|
2000
|
+
const publishedResourceIds = pkg.getExtras()?.publishedResourceIds;
|
|
2001
|
+
const selectedPublishIds = new Set(publishedResourceIds);
|
|
2002
|
+
const allResources = publishedResourceIds !== void 0 && (options.strictOutput || selectedPublishIds.size > 0) ? pkg.listResources().filter((resource) => selectedPublishIds.has(resource.getId())) : pkg.listResources();
|
|
2003
|
+
const skeletonDependencyImageIds = getSelectedSkeletonDependencyImageIds(allResources);
|
|
2004
|
+
const orderedResources = await resolveEditorCompatibleResourceOrder(pkg, allResources, options);
|
|
2005
|
+
const orderedAllResources = sortResourcesByOrder(allResources, new Map(orderedResources.map((resource, index) => [resource.getId(), index])), new Map(allResources.map((resource, index) => [resource.getId(), index])));
|
|
2006
|
+
if (!allResources.some((resource) => {
|
|
2007
|
+
if (isImageResource$1(resource) && skeletonDependencyImageIds.has(resource.getId())) return false;
|
|
2008
|
+
return isPackableResource(resource);
|
|
2009
|
+
})) continue;
|
|
2010
|
+
const inputs = [];
|
|
2011
|
+
const referencedIds = collectPackageResourceReferences(pkg).localResourceIds;
|
|
2012
|
+
for (const res of orderedAllResources) {
|
|
2013
|
+
if (isSkeletonResource$1(res) && referencedIds.has(res.getId())) {
|
|
2014
|
+
for (const requiredId of res.getRequireIds()) if (requiredId) referencedIds.add(requiredId);
|
|
2015
|
+
}
|
|
2016
|
+
if (isFontResource$1(res)) {
|
|
2017
|
+
const textureId = res.getTextureId?.() ?? "";
|
|
2018
|
+
if (textureId) referencedIds.add(textureId);
|
|
2019
|
+
if (options.readFileRaw && options.basePath) {
|
|
2020
|
+
const fontName = resolveFontFileName(res.getName());
|
|
2021
|
+
const fontPath = res.getPath() ?? "/";
|
|
2022
|
+
const fntFile = `${options.basePath}/${pkg.getName()}${fontPath}${fontName}`;
|
|
2023
|
+
try {
|
|
2024
|
+
const fntData = await options.readFileRaw(fntFile);
|
|
2025
|
+
const fntText = new TextDecoder().decode(fntData);
|
|
2026
|
+
for (const line of fntText.split(/\r?\n/)) {
|
|
2027
|
+
const match = line.match(/img=(\w+)/);
|
|
2028
|
+
if (match) referencedIds.add(match[1]);
|
|
2029
|
+
}
|
|
2030
|
+
} catch {}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
for (const res of orderedAllResources) if (isImageResource$1(res)) {
|
|
2035
|
+
const resId = res.getId();
|
|
2036
|
+
if (skeletonDependencyImageIds.has(resId)) continue;
|
|
2037
|
+
if (selectedPublishIds.size === 0 && !res.getExported() && referencedIds.size > 0 && !referencedIds.has(resId)) continue;
|
|
2038
|
+
await collectImage(res, pkg, inputs, encoder, options, doTrim, logger);
|
|
2039
|
+
} else if (isMovieClipResource$1(res)) {
|
|
2040
|
+
const resId = res.getId();
|
|
2041
|
+
if (selectedPublishIds.size === 0 && !res.getExported() && referencedIds.size > 0 && !referencedIds.has(resId)) continue;
|
|
2042
|
+
await collectMovieClipFrames(doc, res, pkg, inputs, encoder, options, logger);
|
|
2043
|
+
} else if (isFontResource$1(res)) {
|
|
2044
|
+
const resId = res.getId();
|
|
2045
|
+
if (selectedPublishIds.size === 0 && !res.getExported() && referencedIds.size > 0 && !referencedIds.has(resId)) continue;
|
|
2046
|
+
await collectFontTexture(doc, res, pkg, options);
|
|
2047
|
+
}
|
|
2048
|
+
if (inputs.length === 0) continue;
|
|
2049
|
+
if (options.strictOutput && (!encoder || !options.basePath || !options.outputPath)) throw new Error(`atlas: Package "${pkg.getName()}" requires encoder, basePath, and outputPath for complete raster output.`);
|
|
2050
|
+
await emitAtlasInputs({
|
|
2051
|
+
doc,
|
|
2052
|
+
pkg,
|
|
2053
|
+
allResources,
|
|
2054
|
+
inputs,
|
|
2055
|
+
options,
|
|
2056
|
+
encoder,
|
|
2057
|
+
logger
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
});
|
|
2001
2061
|
}
|
|
2002
2062
|
//#endregion
|
|
2003
2063
|
//#region src/codegen-templates.ts
|
|
@@ -2467,102 +2527,8 @@ function decodeText(value) {
|
|
|
2467
2527
|
return new TextDecoder().decode(value);
|
|
2468
2528
|
}
|
|
2469
2529
|
//#endregion
|
|
2470
|
-
//#region src/publish.ts
|
|
2471
|
-
|
|
2472
|
-
const logger = doc.getLogger();
|
|
2473
|
-
for (const plugin of plugins) {
|
|
2474
|
-
const fn = plugin.plugin[hook];
|
|
2475
|
-
if (typeof fn !== "function") continue;
|
|
2476
|
-
try {
|
|
2477
|
-
await fn(doc, options);
|
|
2478
|
-
} catch (error) {
|
|
2479
|
-
logger.warn(`publish: Plugin "${plugin.name}" ${hook} failed: ${formatPluginError(error)}`);
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
const UNITY_PROJECT_TYPE = ProjectType.Unity;
|
|
2484
|
-
const COCOS_CREATOR_PROJECT_TYPE = ProjectType.CocosCreator;
|
|
2485
|
-
function resolveDefaultPublishFileExtension(projectType, publishSettings) {
|
|
2486
|
-
if (projectType === UNITY_PROJECT_TYPE) return "bytes";
|
|
2487
|
-
if (projectType === COCOS_CREATOR_PROJECT_TYPE) return publishSettings.fileExtension || "bin";
|
|
2488
|
-
return publishSettings.fileExtension || "fui";
|
|
2489
|
-
}
|
|
2490
|
-
function resolvePublishAtlasRuntimeOptions(fileExtension) {
|
|
2491
|
-
return {
|
|
2492
|
-
preserveInputOrderOnTie: fileExtension === "fui",
|
|
2493
|
-
directSingleImageOutput: fileExtension === "bytes"
|
|
2494
|
-
};
|
|
2495
|
-
}
|
|
2496
|
-
function resolvePublishFileName(publishName, fileExtension) {
|
|
2497
|
-
if (fileExtension === "bytes") return `${publishName}_fui.bytes`;
|
|
2498
|
-
return `${publishName}.${fileExtension}`;
|
|
2499
|
-
}
|
|
2500
|
-
/**
|
|
2501
|
-
* Resolve publish defaults from the document's project settings.
|
|
2502
|
-
*
|
|
2503
|
-
* This keeps the editor-aligned publish rules reusable across environments,
|
|
2504
|
-
* while callers still provide environment-specific concerns such as fs/encoder/basePath.
|
|
2505
|
-
*/
|
|
2506
|
-
function resolvePublishOptions(doc, overrides = {}) {
|
|
2507
|
-
const root = doc.getRoot();
|
|
2508
|
-
const publishSettings = (root.getSettings?.() ?? {}).publish ?? {};
|
|
2509
|
-
const atlasSetting = publishSettings.atlasSetting ?? {};
|
|
2510
|
-
const projectType = root.getProjectType();
|
|
2511
|
-
const fileExtension = overrides.fileExtension ?? resolveDefaultPublishFileExtension(projectType, publishSettings);
|
|
2512
|
-
let compressed = overrides.compressed ?? publishSettings.compressDesc ?? false;
|
|
2513
|
-
if (projectType === UNITY_PROJECT_TYPE) compressed = overrides.compressed ?? false;
|
|
2514
|
-
const atlasOptions = {
|
|
2515
|
-
maxSize: overrides.atlas?.maxSize ?? atlasSetting.maxSize ?? 2048,
|
|
2516
|
-
fast: overrides.atlas?.fast ?? atlasSetting.fast ?? true,
|
|
2517
|
-
allowRotation: overrides.atlas?.allowRotation ?? atlasSetting.allowRotation ?? false,
|
|
2518
|
-
padding: overrides.atlas?.padding ?? atlasSetting.padding ?? 2,
|
|
2519
|
-
powerOfTwo: overrides.atlas?.powerOfTwo ?? atlasSetting.sizeOption === "pot",
|
|
2520
|
-
square: overrides.atlas?.square ?? atlasSetting.forceSquare ?? false,
|
|
2521
|
-
multiPage: overrides.atlas?.multiPage ?? atlasSetting.paging ?? true,
|
|
2522
|
-
trimImage: overrides.atlas?.trimImage ?? atlasSetting.trimImage ?? false,
|
|
2523
|
-
extractAlpha: overrides.atlas?.extractAlpha ?? atlasSetting.extractAlpha ?? false
|
|
2524
|
-
};
|
|
2525
|
-
return {
|
|
2526
|
-
compressed,
|
|
2527
|
-
fileExtension,
|
|
2528
|
-
packages: overrides.packages,
|
|
2529
|
-
atlas: atlasOptions
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
function trimTrailingSlashes(value) {
|
|
2533
|
-
return value.replace(/[/\\]+$/, "");
|
|
2534
|
-
}
|
|
2535
|
-
function isAbsolutePathLike(value) {
|
|
2536
|
-
return /^(?:[a-zA-Z]:[/\\]|[/\\]{1,2})/u.test(value);
|
|
2537
|
-
}
|
|
2538
|
-
function joinPathSegments(left, right) {
|
|
2539
|
-
const normalizedLeft = trimTrailingSlashes(left);
|
|
2540
|
-
const normalizedRight = right.replace(/^[/\\]+/, "");
|
|
2541
|
-
if (!normalizedLeft) return normalizedRight;
|
|
2542
|
-
if (!normalizedRight) return normalizedLeft;
|
|
2543
|
-
return `${normalizedLeft}${normalizedLeft.includes("\\") ? "\\" : "/"}${normalizedRight}`;
|
|
2544
|
-
}
|
|
2545
|
-
function dirname(filePath) {
|
|
2546
|
-
return filePath.replace(/[/\\]+$/, "").match(/^(.*)[/\\][^/\\]+$/)?.[1] ?? "";
|
|
2547
|
-
}
|
|
2548
|
-
function createUnsupportedFsOperation(name) {
|
|
2549
|
-
return async () => {
|
|
2550
|
-
throw new Error(`publish: FileSystem.${name}() is not available in the publish writer adapter.`);
|
|
2551
|
-
};
|
|
2552
|
-
}
|
|
2553
|
-
function toBinaryWriterFileSystem(fs) {
|
|
2554
|
-
return {
|
|
2555
|
-
readFile: createUnsupportedFsOperation("readFile"),
|
|
2556
|
-
readFileRaw: createUnsupportedFsOperation("readFileRaw"),
|
|
2557
|
-
writeFile: createUnsupportedFsOperation("writeFile"),
|
|
2558
|
-
writeFileRaw: fs.writeFileRaw,
|
|
2559
|
-
mkdir: fs.mkdir,
|
|
2560
|
-
readdir: createUnsupportedFsOperation("readdir"),
|
|
2561
|
-
exists: createUnsupportedFsOperation("exists"),
|
|
2562
|
-
join: fs.join,
|
|
2563
|
-
dirname
|
|
2564
|
-
};
|
|
2565
|
-
}
|
|
2530
|
+
//#region src/publish/package-context.ts
|
|
2531
|
+
const UNITY_PROJECT_TYPE$1 = ProjectType.Unity;
|
|
2566
2532
|
function isComponentResource(resource) {
|
|
2567
2533
|
return resource.propertyType === "Component";
|
|
2568
2534
|
}
|
|
@@ -2593,39 +2559,6 @@ function isDragonBonesResource(resource) {
|
|
|
2593
2559
|
function isSkeletonResource(resource) {
|
|
2594
2560
|
return isSpineResource(resource) || isDragonBonesResource(resource);
|
|
2595
2561
|
}
|
|
2596
|
-
function addLocalUiResourceRef(target, pkgId, value) {
|
|
2597
|
-
if (!value || typeof value !== "string" || !value.startsWith(`ui://${pkgId}`) || value.length <= 13) return;
|
|
2598
|
-
target.add(value.slice(13));
|
|
2599
|
-
}
|
|
2600
|
-
function addLocalUiResourceRefsFromText(target, pkgId, value) {
|
|
2601
|
-
if (!value || typeof value !== "string") return;
|
|
2602
|
-
const prefix = `ui://${pkgId}`;
|
|
2603
|
-
let index = value.indexOf(prefix);
|
|
2604
|
-
while (index !== -1) {
|
|
2605
|
-
const start = index + prefix.length;
|
|
2606
|
-
let end = start;
|
|
2607
|
-
while (end < value.length && /[0-9a-z]/i.test(value[end] ?? "")) end++;
|
|
2608
|
-
if (end > start) target.add(value.slice(start, end));
|
|
2609
|
-
index = value.indexOf(prefix, end);
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
function addLocalUiResourceRefsFromUnknown(target, pkgId, value) {
|
|
2613
|
-
if (Array.isArray(value)) {
|
|
2614
|
-
for (const entry of value) addLocalUiResourceRefsFromUnknown(target, pkgId, entry);
|
|
2615
|
-
return;
|
|
2616
|
-
}
|
|
2617
|
-
if (typeof value === "string") {
|
|
2618
|
-
addLocalUiResourceRef(target, pkgId, value);
|
|
2619
|
-
addLocalUiResourceRefsFromText(target, pkgId, value);
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
function addLocalFontRef(target, pkgId, value) {
|
|
2623
|
-
if (Array.isArray(value)) {
|
|
2624
|
-
for (const entry of value) addLocalUiResourceRef(target, pkgId, entry);
|
|
2625
|
-
return;
|
|
2626
|
-
}
|
|
2627
|
-
addLocalUiResourceRef(target, pkgId, value ?? void 0);
|
|
2628
|
-
}
|
|
2629
2562
|
function resolvePackageAssetsBasePath(basePath, resource) {
|
|
2630
2563
|
const branchName = resource?.getBranch?.() ?? "";
|
|
2631
2564
|
if (!branchName) return basePath;
|
|
@@ -2659,12 +2592,12 @@ function extname(fileName) {
|
|
|
2659
2592
|
}
|
|
2660
2593
|
function resolvePublishedMiscFileName(resource, projectType) {
|
|
2661
2594
|
const file = resource.getFile();
|
|
2662
|
-
if (projectType !== UNITY_PROJECT_TYPE) return file;
|
|
2595
|
+
if (projectType !== UNITY_PROJECT_TYPE$1) return file;
|
|
2663
2596
|
if (file.toLowerCase().endsWith(".atlas")) return `${file}.txt`;
|
|
2664
2597
|
return file;
|
|
2665
2598
|
}
|
|
2666
2599
|
function resolvePublishedSkeletonFileName(resource, projectType) {
|
|
2667
|
-
if (projectType === UNITY_PROJECT_TYPE && isSpineResource(resource) && resource.getFile().toLowerCase().endsWith(".skel")) return `${resource.getFile()}.bytes`;
|
|
2600
|
+
if (projectType === UNITY_PROJECT_TYPE$1 && isSpineResource(resource) && resource.getFile().toLowerCase().endsWith(".skel")) return `${resource.getFile()}.bytes`;
|
|
2668
2601
|
return resource.getFile();
|
|
2669
2602
|
}
|
|
2670
2603
|
function setPublishedFileExtra(resource, fileName) {
|
|
@@ -2761,10 +2694,9 @@ function collectHighResolutionItemIds(resources, publishedResourceIds, includeHi
|
|
|
2761
2694
|
return result;
|
|
2762
2695
|
}
|
|
2763
2696
|
function collectPackagePublishContext(pkg, options) {
|
|
2764
|
-
const pkgId = pkg.getId();
|
|
2765
2697
|
const resources = pkg.listResources();
|
|
2766
2698
|
const resourceMap = new Map(resources.map((resource) => [resource.getId(), resource]));
|
|
2767
|
-
const referencedIds =
|
|
2699
|
+
const referencedIds = collectPackageResourceReferences(pkg).localResourceIds;
|
|
2768
2700
|
const pixelHitTestImageIds = /* @__PURE__ */ new Set();
|
|
2769
2701
|
const spriteItemIds = /* @__PURE__ */ new Set();
|
|
2770
2702
|
const collectExportedResourceIds = (sourceResources, sourcePublishedResourceIds) => {
|
|
@@ -2799,49 +2731,6 @@ function collectPackagePublishContext(pkg, options) {
|
|
|
2799
2731
|
if (sourceResource && isImageResource(sourceResource)) pixelHitTestImageIds.add(sourceId);
|
|
2800
2732
|
}
|
|
2801
2733
|
}
|
|
2802
|
-
for (const child of children) {
|
|
2803
|
-
const src = child.getSrc?.();
|
|
2804
|
-
if (src) referencedIds.add(src);
|
|
2805
|
-
addLocalFontRef(referencedIds, pkgId, child.getFont?.());
|
|
2806
|
-
addLocalUiResourceRefsFromText(referencedIds, pkgId, child.getText?.());
|
|
2807
|
-
for (const ref of [
|
|
2808
|
-
child.getUrl?.(),
|
|
2809
|
-
child.getDefaultItem?.(),
|
|
2810
|
-
child.getIcon?.(),
|
|
2811
|
-
child.getSelectedIcon?.(),
|
|
2812
|
-
child.getDropdown?.(),
|
|
2813
|
-
child.getSound?.(),
|
|
2814
|
-
child.getInstanceSound?.(),
|
|
2815
|
-
child.getInstanceIcon?.(),
|
|
2816
|
-
child.getInstanceSelectedIcon?.(),
|
|
2817
|
-
child.getVtScrollBarRes?.(),
|
|
2818
|
-
child.getHzScrollBarRes?.(),
|
|
2819
|
-
child.getHeaderRes?.(),
|
|
2820
|
-
child.getFooterRes?.()
|
|
2821
|
-
]) addLocalUiResourceRef(referencedIds, pkgId, ref);
|
|
2822
|
-
for (const item of child.getInstanceComboItems?.() ?? []) addLocalUiResourceRef(referencedIds, pkgId, item.icon ?? void 0);
|
|
2823
|
-
for (const item of child.getListItems?.() ?? []) {
|
|
2824
|
-
addLocalUiResourceRef(referencedIds, pkgId, item.icon ?? void 0);
|
|
2825
|
-
addLocalUiResourceRef(referencedIds, pkgId, item.url ?? void 0);
|
|
2826
|
-
}
|
|
2827
|
-
for (const gear of child.listGears?.() ?? []) {
|
|
2828
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, gear.getValues?.());
|
|
2829
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, gear.getDefaultValue?.());
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
addLocalFontRef(referencedIds, pkgId, component.getFont?.());
|
|
2833
|
-
for (const ref of [
|
|
2834
|
-
component.getDropdown?.(),
|
|
2835
|
-
component.getHeaderRes?.(),
|
|
2836
|
-
component.getFooterRes?.(),
|
|
2837
|
-
component.getVtScrollBarRes?.(),
|
|
2838
|
-
component.getHzScrollBarRes?.(),
|
|
2839
|
-
component.getSound?.()
|
|
2840
|
-
]) addLocalUiResourceRef(referencedIds, pkgId, ref);
|
|
2841
|
-
for (const transition of component.listTransitions?.() ?? []) for (const item of transition.listItems?.() ?? []) {
|
|
2842
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, item.getStartValue?.());
|
|
2843
|
-
addLocalUiResourceRefsFromUnknown(referencedIds, pkgId, item.getEndValue?.());
|
|
2844
|
-
}
|
|
2845
2734
|
}
|
|
2846
2735
|
const publishedResourceIds = new Set(spriteItemIds);
|
|
2847
2736
|
for (const resource of resources) {
|
|
@@ -3023,6 +2912,8 @@ function getPublishedSkeletonDependencyImageIds(pkg, publishedResourceIds) {
|
|
|
3023
2912
|
}
|
|
3024
2913
|
return imageIds;
|
|
3025
2914
|
}
|
|
2915
|
+
//#endregion
|
|
2916
|
+
//#region src/publish/external-resources.ts
|
|
3026
2917
|
async function exportPackageSounds(pkg, outputDir, basePath, fs, readFileRaw) {
|
|
3027
2918
|
const publishedResourceIds = getAnnotatedPublishedResourceIds(pkg);
|
|
3028
2919
|
if (publishedResourceIds.size === 0) return;
|
|
@@ -3079,6 +2970,105 @@ async function exportPackageExternalResources(pkg, outputDir, basePath, fs, read
|
|
|
3079
2970
|
}
|
|
3080
2971
|
}
|
|
3081
2972
|
}
|
|
2973
|
+
//#endregion
|
|
2974
|
+
//#region src/publish/options.ts
|
|
2975
|
+
const UNITY_PROJECT_TYPE = ProjectType.Unity;
|
|
2976
|
+
const COCOS_CREATOR_PROJECT_TYPE = ProjectType.CocosCreator;
|
|
2977
|
+
function resolveDefaultPublishFileExtension(projectType, publishSettings) {
|
|
2978
|
+
if (projectType === UNITY_PROJECT_TYPE) return "bytes";
|
|
2979
|
+
if (projectType === COCOS_CREATOR_PROJECT_TYPE) return publishSettings.fileExtension || "bin";
|
|
2980
|
+
return publishSettings.fileExtension || "fui";
|
|
2981
|
+
}
|
|
2982
|
+
function resolvePublishAtlasRuntimeOptions(fileExtension) {
|
|
2983
|
+
return {
|
|
2984
|
+
preserveInputOrderOnTie: fileExtension === "fui",
|
|
2985
|
+
directSingleImageOutput: fileExtension === "bytes"
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
function resolvePublishFileName(publishName, fileExtension) {
|
|
2989
|
+
if (fileExtension === "bytes") return `${publishName}_fui.bytes`;
|
|
2990
|
+
return `${publishName}.${fileExtension}`;
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Resolve publish defaults from the document's project settings.
|
|
2994
|
+
*
|
|
2995
|
+
* This keeps the editor-aligned publish rules reusable across environments,
|
|
2996
|
+
* while callers still provide environment-specific concerns such as fs/encoder/basePath.
|
|
2997
|
+
*/
|
|
2998
|
+
function resolvePublishOptions(doc, overrides = {}) {
|
|
2999
|
+
const root = doc.getRoot();
|
|
3000
|
+
const publishSettings = (root.getSettings?.() ?? {}).publish ?? {};
|
|
3001
|
+
const atlasSetting = publishSettings.atlasSetting ?? {};
|
|
3002
|
+
const projectType = root.getProjectType();
|
|
3003
|
+
const fileExtension = overrides.fileExtension ?? resolveDefaultPublishFileExtension(projectType, publishSettings);
|
|
3004
|
+
let compressed = overrides.compressed ?? publishSettings.compressDesc ?? false;
|
|
3005
|
+
if (projectType === UNITY_PROJECT_TYPE) compressed = overrides.compressed ?? false;
|
|
3006
|
+
const atlasOptions = {
|
|
3007
|
+
maxSize: overrides.atlas?.maxSize ?? atlasSetting.maxSize ?? 2048,
|
|
3008
|
+
fast: overrides.atlas?.fast ?? atlasSetting.fast ?? true,
|
|
3009
|
+
allowRotation: overrides.atlas?.allowRotation ?? atlasSetting.allowRotation ?? false,
|
|
3010
|
+
padding: overrides.atlas?.padding ?? atlasSetting.padding ?? 2,
|
|
3011
|
+
powerOfTwo: overrides.atlas?.powerOfTwo ?? atlasSetting.sizeOption === "pot",
|
|
3012
|
+
square: overrides.atlas?.square ?? atlasSetting.forceSquare ?? false,
|
|
3013
|
+
multiPage: overrides.atlas?.multiPage ?? atlasSetting.paging ?? true,
|
|
3014
|
+
trimImage: overrides.atlas?.trimImage ?? atlasSetting.trimImage ?? false,
|
|
3015
|
+
extractAlpha: overrides.atlas?.extractAlpha ?? atlasSetting.extractAlpha ?? false
|
|
3016
|
+
};
|
|
3017
|
+
return {
|
|
3018
|
+
compressed,
|
|
3019
|
+
fileExtension,
|
|
3020
|
+
packages: overrides.packages,
|
|
3021
|
+
atlas: atlasOptions
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
//#endregion
|
|
3025
|
+
//#region src/publish.ts
|
|
3026
|
+
async function runPublishPluginHook(plugins, hook, doc, options) {
|
|
3027
|
+
const logger = doc.getLogger();
|
|
3028
|
+
for (const plugin of plugins) {
|
|
3029
|
+
const fn = plugin.plugin[hook];
|
|
3030
|
+
if (typeof fn !== "function") continue;
|
|
3031
|
+
try {
|
|
3032
|
+
await fn(doc, options);
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
logger.warn(`publish: Plugin "${plugin.name}" ${hook} failed: ${formatPluginError(error)}`);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
function trimTrailingSlashes(value) {
|
|
3039
|
+
return value.replace(/[/\\]+$/, "");
|
|
3040
|
+
}
|
|
3041
|
+
function isAbsolutePathLike(value) {
|
|
3042
|
+
return /^(?:[a-zA-Z]:[/\\]|[/\\]{1,2})/u.test(value);
|
|
3043
|
+
}
|
|
3044
|
+
function joinPathSegments(left, right) {
|
|
3045
|
+
const normalizedLeft = trimTrailingSlashes(left);
|
|
3046
|
+
const normalizedRight = right.replace(/^[/\\]+/, "");
|
|
3047
|
+
if (!normalizedLeft) return normalizedRight;
|
|
3048
|
+
if (!normalizedRight) return normalizedLeft;
|
|
3049
|
+
return `${normalizedLeft}${normalizedLeft.includes("\\") ? "\\" : "/"}${normalizedRight}`;
|
|
3050
|
+
}
|
|
3051
|
+
function dirname(filePath) {
|
|
3052
|
+
return filePath.replace(/[/\\]+$/, "").match(/^(.*)[/\\][^/\\]+$/)?.[1] ?? "";
|
|
3053
|
+
}
|
|
3054
|
+
function createUnsupportedFsOperation(name) {
|
|
3055
|
+
return async () => {
|
|
3056
|
+
throw new Error(`publish: FileSystem.${name}() is not available in the publish writer adapter.`);
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
function toBinaryWriterFileSystem(fs) {
|
|
3060
|
+
return {
|
|
3061
|
+
readFile: createUnsupportedFsOperation("readFile"),
|
|
3062
|
+
readFileRaw: createUnsupportedFsOperation("readFileRaw"),
|
|
3063
|
+
writeFile: createUnsupportedFsOperation("writeFile"),
|
|
3064
|
+
writeFileRaw: fs.writeFileRaw,
|
|
3065
|
+
mkdir: fs.mkdir,
|
|
3066
|
+
readdir: createUnsupportedFsOperation("readdir"),
|
|
3067
|
+
exists: createUnsupportedFsOperation("exists"),
|
|
3068
|
+
join: fs.join,
|
|
3069
|
+
dirname
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3082
3072
|
/**
|
|
3083
3073
|
* Publishes a FairyGUI project.
|
|
3084
3074
|
*
|
|
@@ -3257,95 +3247,8 @@ function publish(options) {
|
|
|
3257
3247
|
* @internal
|
|
3258
3248
|
*/
|
|
3259
3249
|
function _computeDependencies(doc, pkg, pkgMap) {
|
|
3260
|
-
const referencedPkgIds =
|
|
3261
|
-
const pkgId = pkg.getId();
|
|
3250
|
+
const referencedPkgIds = collectPackageResourceReferences(pkg).packageIds;
|
|
3262
3251
|
const packageOrder = new Map(doc.getRoot().listPackages().map((entry, index) => [entry.getId(), index]));
|
|
3263
|
-
const addDependencyPackageId = (dependencyPkgId) => {
|
|
3264
|
-
const normalized = dependencyPkgId?.trim() ?? "";
|
|
3265
|
-
if (!normalized || normalized === pkgId) return;
|
|
3266
|
-
referencedPkgIds.add(normalized);
|
|
3267
|
-
};
|
|
3268
|
-
const extractPackageIdFromUiUrl = (value) => {
|
|
3269
|
-
if (!value.startsWith("ui://")) return null;
|
|
3270
|
-
const rest = value.slice(5);
|
|
3271
|
-
if (!rest) return null;
|
|
3272
|
-
const slashIndex = rest.indexOf("/");
|
|
3273
|
-
if (slashIndex >= 0) return rest.slice(0, slashIndex) || null;
|
|
3274
|
-
if (rest.length >= 8) return rest.slice(0, 8);
|
|
3275
|
-
return null;
|
|
3276
|
-
};
|
|
3277
|
-
const addDependencyPackageIdFromUiValue = (value) => {
|
|
3278
|
-
if (!value || typeof value !== "string") return;
|
|
3279
|
-
addDependencyPackageId(extractPackageIdFromUiUrl(value));
|
|
3280
|
-
};
|
|
3281
|
-
const addDependencyPackageIdsFromText = (value) => {
|
|
3282
|
-
if (!value || typeof value !== "string") return;
|
|
3283
|
-
const matches = value.matchAll(/ui:\/\/([0-9a-z]{8})/giu);
|
|
3284
|
-
for (const match of matches) addDependencyPackageId(match[1] ?? "");
|
|
3285
|
-
};
|
|
3286
|
-
const addDependencyPackageIdsFromUnknown = (value) => {
|
|
3287
|
-
if (Array.isArray(value)) {
|
|
3288
|
-
for (const entry of value) addDependencyPackageIdsFromUnknown(entry);
|
|
3289
|
-
return;
|
|
3290
|
-
}
|
|
3291
|
-
if (typeof value === "string") {
|
|
3292
|
-
addDependencyPackageIdFromUiValue(value);
|
|
3293
|
-
addDependencyPackageIdsFromText(value);
|
|
3294
|
-
}
|
|
3295
|
-
};
|
|
3296
|
-
const addDependencyFontRef = (value) => {
|
|
3297
|
-
if (Array.isArray(value)) {
|
|
3298
|
-
for (const entry of value) addDependencyPackageIdFromUiValue(entry);
|
|
3299
|
-
return;
|
|
3300
|
-
}
|
|
3301
|
-
addDependencyPackageIdFromUiValue(value ?? void 0);
|
|
3302
|
-
};
|
|
3303
|
-
for (const res of pkg.listResources()) {
|
|
3304
|
-
if (res.propertyType !== "Component") continue;
|
|
3305
|
-
const component = res;
|
|
3306
|
-
for (const child of component.listChildren?.() ?? []) {
|
|
3307
|
-
addDependencyPackageId(child.getPackageId?.());
|
|
3308
|
-
addDependencyFontRef(child.getFont?.());
|
|
3309
|
-
addDependencyPackageIdsFromText(child.getText?.());
|
|
3310
|
-
for (const ref of [
|
|
3311
|
-
child.getUrl?.(),
|
|
3312
|
-
child.getDefaultItem?.(),
|
|
3313
|
-
child.getIcon?.(),
|
|
3314
|
-
child.getSelectedIcon?.(),
|
|
3315
|
-
child.getDropdown?.(),
|
|
3316
|
-
child.getSound?.(),
|
|
3317
|
-
child.getInstanceSound?.(),
|
|
3318
|
-
child.getInstanceIcon?.(),
|
|
3319
|
-
child.getInstanceSelectedIcon?.(),
|
|
3320
|
-
child.getVtScrollBarRes?.(),
|
|
3321
|
-
child.getHzScrollBarRes?.(),
|
|
3322
|
-
child.getHeaderRes?.(),
|
|
3323
|
-
child.getFooterRes?.()
|
|
3324
|
-
]) addDependencyPackageIdFromUiValue(ref);
|
|
3325
|
-
for (const item of child.getInstanceComboItems?.() ?? []) addDependencyPackageIdFromUiValue(item.icon ?? void 0);
|
|
3326
|
-
for (const item of child.getListItems?.() ?? []) {
|
|
3327
|
-
addDependencyPackageIdFromUiValue(item.icon ?? void 0);
|
|
3328
|
-
addDependencyPackageIdFromUiValue(item.url ?? void 0);
|
|
3329
|
-
}
|
|
3330
|
-
for (const gear of child.listGears?.() ?? []) {
|
|
3331
|
-
addDependencyPackageIdsFromUnknown(gear.getValues?.());
|
|
3332
|
-
addDependencyPackageIdsFromUnknown(gear.getDefaultValue?.());
|
|
3333
|
-
}
|
|
3334
|
-
}
|
|
3335
|
-
addDependencyFontRef(component.getFont?.());
|
|
3336
|
-
for (const ref of [
|
|
3337
|
-
component.getDropdown?.(),
|
|
3338
|
-
component.getHeaderRes?.(),
|
|
3339
|
-
component.getFooterRes?.(),
|
|
3340
|
-
component.getVtScrollBarRes?.(),
|
|
3341
|
-
component.getHzScrollBarRes?.(),
|
|
3342
|
-
component.getSound?.()
|
|
3343
|
-
]) addDependencyPackageIdFromUiValue(ref);
|
|
3344
|
-
for (const transition of component.listTransitions?.() ?? []) for (const item of transition.listItems?.() ?? []) {
|
|
3345
|
-
addDependencyPackageIdsFromUnknown(item.getStartValue?.());
|
|
3346
|
-
addDependencyPackageIdsFromUnknown(item.getEndValue?.());
|
|
3347
|
-
}
|
|
3348
|
-
}
|
|
3349
3252
|
for (const dep of pkg.listDependencies()) pkg.removeDependency(dep);
|
|
3350
3253
|
if (referencedPkgIds.size > 0) {
|
|
3351
3254
|
const sortedIds = [...referencedPkgIds].sort((a, b) => {
|