implements PLN-0082
This commit is contained in:
parent
ad2bfeb66f
commit
0be02622c4
@ -1,5 +1,5 @@
|
||||
{"type":"meta","next_id":{"DSC":39,"AGD":42,"DEC":38,"PLN":87,"LSN":53,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0038","status":"open","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-05-23","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0081","file":"PLN-0081-rgba8888-packer-and-studio-spec-propagation.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0082","file":"PLN-0082-rgba8888-palette-model-and-argb-conversion.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0083","file":"PLN-0083-rgba8888-glyph-payload-emission-and-sizes.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0084","file":"PLN-0084-rgba8888-packer-projections-and-workspace-apis.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0085","file":"PLN-0085-rgba8888-tests-fixtures-and-conformance.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0086","file":"PLN-0086-rgba8888-final-integration-and-residue-removal.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0038","status":"open","ticket":"studio-packer-rgba8888-asset-pipeline","title":"Studio and Packer RGBA8888 Asset Pipeline Alignment","created_at":"2026-05-23","updated_at":"2026-05-23","tags":["studio","packer","assets","glyph-bank","palette","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-studio-packer-rgba8888-asset-pipeline.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0081","file":"PLN-0081-rgba8888-packer-and-studio-spec-propagation.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0082","file":"PLN-0082-rgba8888-palette-model-and-argb-conversion.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0083","file":"PLN-0083-rgba8888-glyph-payload-emission-and-sizes.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0084","file":"PLN-0084-rgba8888-packer-projections-and-workspace-apis.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0085","file":"PLN-0085-rgba8888-tests-fixtures-and-conformance.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]},{"id":"PLN-0086","file":"PLN-0086-rgba8888-final-integration-and-residue-removal.md","status":"review","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0037"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0037","status":"done","ticket":"pbs-autocomplete-parameter-names","title":"PBS autocomplete parameter names for stdlib and method calls","created_at":"2026-05-08","updated_at":"2026-05-14","tags":["compiler-pbs","studio","lsp","autocomplete","signature-help","stdlib"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0037-pbs-autocomplete-parameter-names/LSN-0052-canonical-callable-parameter-names-through-pbs-editor-assistance.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]}
|
||||
{"type":"discussion","id":"DSC-0036","status":"open","ticket":"pbs-symbol-documentation-and-hover-markdown","title":"Modelo de documentacao de simbolos em PBS e consumo markdown no hover","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["compiler","compiler-pbs","studio","lsp","vscode","editor","hover","documentation","markdown"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-pbs-symbol-documentation-and-hover-markdown.md","status":"open","created_at":"2026-05-08","updated_at":"2026-05-08"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"pbs-lsp-editor-assistance-wave-1","title":"Wave 1 de assistencia editorial via LSP para PBS no VS Code","created_at":"2026-05-08","updated_at":"2026-05-08","tags":["studio","lsp","vscode","compiler","compiler-pbs","editor","completion","hover","signature-help"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0051","file":"discussion/lessons/DSC-0035-pbs-lsp-editor-assistance-wave-1/LSN-0051-compiler-backed-editor-assistance-for-pbs.md","status":"done","created_at":"2026-05-08","updated_at":"2026-05-08"}]}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
id: PLN-0082
|
||||
ticket: studio-packer-rgba8888-asset-pipeline
|
||||
title: RGBA8888 Palette Model and ARGB Conversion
|
||||
status: review
|
||||
status: done
|
||||
created: 2026-05-23
|
||||
completed:
|
||||
completed: 2026-05-23
|
||||
ref_decisions:
|
||||
- DEC-0037
|
||||
tags:
|
||||
@ -124,12 +124,12 @@ diagnostic wording to remove `indices 1..15`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `PackerPaletteV1` exposes `rgba8888` and no RGB565 field.
|
||||
- [ ] `PackerGlyphBankWalker` stores canonical RGBA values.
|
||||
- [ ] Magenta is not special.
|
||||
- [ ] Alpha zero and partial alpha are valid palette data.
|
||||
- [ ] All 16 palette indices are usable.
|
||||
- [ ] Unit tests cover conversion and palette indexing semantics.
|
||||
- [x] `PackerPaletteV1` exposes `rgba8888` and no RGB565 field.
|
||||
- [x] `PackerGlyphBankWalker` stores canonical RGBA values.
|
||||
- [x] Magenta is not special.
|
||||
- [x] Alpha zero and partial alpha are valid palette data.
|
||||
- [x] All 16 palette indices are usable.
|
||||
- [x] Unit tests cover conversion and palette indexing semantics.
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package p.packer.models;
|
||||
|
||||
public record PackerPaletteV1(
|
||||
int[] originalArgb8888,
|
||||
short[] convertedRgb565) {
|
||||
}
|
||||
int[] rgba8888) {
|
||||
}
|
||||
|
||||
@ -383,13 +383,10 @@ public class PackerAssetWalker {
|
||||
if (!indexNode.isInt() || !paletteNode.isObject()) {
|
||||
continue;
|
||||
}
|
||||
final int originalCount = paletteNode.path("originalArgb8888").isArray()
|
||||
? paletteNode.path("originalArgb8888").size()
|
||||
final int colorCount = paletteNode.path("rgba8888").isArray()
|
||||
? paletteNode.path("rgba8888").size()
|
||||
: 0;
|
||||
final int convertedCount = paletteNode.path("convertedRgb565").isArray()
|
||||
? paletteNode.path("convertedRgb565").size()
|
||||
: 0;
|
||||
palettes.add(new DeclaredPalette(indexNode.intValue(), Math.min(originalCount, convertedCount)));
|
||||
palettes.add(new DeclaredPalette(indexNode.intValue(), colorCount));
|
||||
}
|
||||
palettes.sort(java.util.Comparator.comparingInt(DeclaredPalette::index));
|
||||
return List.copyOf(palettes);
|
||||
|
||||
@ -11,8 +11,7 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphBankRequirements> {
|
||||
private static final int MAX_COLORS_PER_PALETTE = 15;
|
||||
private static final int COLOR_KEY_RGB = 0x00FF00FF;
|
||||
private static final int MAX_COLORS_PER_PALETTE = 16;
|
||||
private static final Set<String> SUPPORTED_MIME_TYPES = Set.of(
|
||||
"image/png"
|
||||
);
|
||||
@ -69,58 +68,37 @@ public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphB
|
||||
));
|
||||
}
|
||||
|
||||
final Map<Integer, Integer> paletteIndexByRgb = new LinkedHashMap<>();
|
||||
final Map<Integer, Integer> paletteIndexByRgba = new LinkedHashMap<>();
|
||||
final byte[] paletteIndices = new byte[image.getWidth() * image.getHeight()];
|
||||
boolean partialAlphaFound = false;
|
||||
boolean exceededPaletteLimit = false;
|
||||
|
||||
int offset = 0;
|
||||
for (var y = 0; y < image.getHeight(); y++) {
|
||||
for (var x = 0; x < image.getWidth(); x++) {
|
||||
final int argb = image.getRGB(x, y);
|
||||
final int alpha = (argb >>> 24) & 0xFF;
|
||||
final int rgb = argb & 0x00FFFFFF;
|
||||
final int rgba = argbToRgba(argb);
|
||||
|
||||
if (alpha == 0 || rgb == COLOR_KEY_RGB) {
|
||||
paletteIndices[offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alpha < 0xFF) {
|
||||
partialAlphaFound = true;
|
||||
}
|
||||
|
||||
Integer paletteIndex = paletteIndexByRgb.get(rgb);
|
||||
Integer paletteIndex = paletteIndexByRgba.get(rgba);
|
||||
if (paletteIndex == null) {
|
||||
if (paletteIndexByRgb.size() >= MAX_COLORS_PER_PALETTE) {
|
||||
if (paletteIndexByRgba.size() >= MAX_COLORS_PER_PALETTE) {
|
||||
exceededPaletteLimit = true;
|
||||
paletteIndices[offset++] = 0;
|
||||
continue;
|
||||
}
|
||||
paletteIndex = paletteIndexByRgb.size() + 1;
|
||||
paletteIndexByRgb.put(rgb, paletteIndex);
|
||||
paletteIndex = paletteIndexByRgba.size();
|
||||
paletteIndexByRgba.put(rgba, paletteIndex);
|
||||
}
|
||||
|
||||
paletteIndices[offset++] = paletteIndex.byteValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (partialAlphaFound) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
PackerDiagnosticCategory.HYGIENE,
|
||||
"Tile contains partial alpha; flattening to RGB and ignoring alpha channel",
|
||||
fileProbe.path(),
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
if (exceededPaletteLimit) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Tile image exceeds color limit for " + fileProbe.path().getFileName()
|
||||
+ ": expected at most " + MAX_COLORS_PER_PALETTE + " colors for indices 1..15",
|
||||
+ ": expected at most " + MAX_COLORS_PER_PALETTE + " RGBA8888 colors",
|
||||
fileProbe.path(),
|
||||
true
|
||||
));
|
||||
@ -131,7 +109,7 @@ public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphB
|
||||
}
|
||||
|
||||
final var tile = new PackerTileIndexedV1(image.getWidth(), image.getHeight(), paletteIndices);
|
||||
final var palette = buildPalette(paletteIndexByRgb);
|
||||
final var palette = buildPalette(paletteIndexByRgba);
|
||||
// Disabled for now: derived tile/palette JSON artifacts are not emitted during normal walks.
|
||||
// If we want filesystem-side debug artifacts again, uncomment the call below.
|
||||
// serializeProbeArtifacts(fileProbe, assetRoot, tile, palette, diagnostics);
|
||||
@ -150,7 +128,7 @@ public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphB
|
||||
final Object width = tile.get("width");
|
||||
final Object height = tile.get("height");
|
||||
final Object indices = tile.get("paletteIndices");
|
||||
final Object colors = palette.get("originalArgb8888");
|
||||
final Object colors = palette.get("rgba8888");
|
||||
return width instanceof Number
|
||||
&& height instanceof Number
|
||||
&& indices instanceof List<?>
|
||||
@ -224,39 +202,27 @@ public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphB
|
||||
}
|
||||
|
||||
private Map<String, Object> toPalettePayload(final PackerPaletteV1 palette) {
|
||||
final List<Integer> convertedRgb565 = new ArrayList<>(palette.convertedRgb565().length);
|
||||
for (final short value : palette.convertedRgb565()) {
|
||||
convertedRgb565.add(Short.toUnsignedInt(value));
|
||||
}
|
||||
|
||||
return Map.of(
|
||||
"originalArgb8888", palette.originalArgb8888(),
|
||||
"convertedRgb565", convertedRgb565
|
||||
"rgba8888", palette.rgba8888()
|
||||
);
|
||||
}
|
||||
|
||||
private PackerPaletteV1 buildPalette(final Map<Integer, Integer> paletteIndexByRgb) {
|
||||
final int[] originalArgb8888 = new int[paletteIndexByRgb.size()];
|
||||
final short[] convertedRgb565 = new short[paletteIndexByRgb.size()];
|
||||
for (final var entry : paletteIndexByRgb.entrySet()) {
|
||||
final int rgb = entry.getKey();
|
||||
final int index = entry.getValue() - 1;
|
||||
final int argb = 0xFF000000 | rgb;
|
||||
originalArgb8888[index] = argb;
|
||||
convertedRgb565[index] = convertToRgb565(argb);
|
||||
private PackerPaletteV1 buildPalette(final Map<Integer, Integer> paletteIndexByRgba) {
|
||||
final int[] rgba8888 = new int[paletteIndexByRgba.size()];
|
||||
for (final var entry : paletteIndexByRgba.entrySet()) {
|
||||
final int rgba = entry.getKey();
|
||||
final int index = entry.getValue();
|
||||
rgba8888[index] = rgba;
|
||||
}
|
||||
return new PackerPaletteV1(originalArgb8888, convertedRgb565);
|
||||
return new PackerPaletteV1(rgba8888);
|
||||
}
|
||||
|
||||
private short convertToRgb565(final int argb) {
|
||||
final int red = (argb >> 16) & 0xFF;
|
||||
final int green = (argb >> 8) & 0xFF;
|
||||
private int argbToRgba(final int argb) {
|
||||
final int alpha = (argb >>> 24) & 0xFF;
|
||||
final int red = (argb >>> 16) & 0xFF;
|
||||
final int green = (argb >>> 8) & 0xFF;
|
||||
final int blue = argb & 0xFF;
|
||||
|
||||
final int r5 = (red >> 3) & 0x1F;
|
||||
final int g6 = (green >> 2) & 0x3F;
|
||||
final int b5 = (blue >> 3) & 0x1F;
|
||||
return (short) ((r5 << 11) | (g6 << 5) | b5);
|
||||
return (red << 24) | (green << 16) | (blue << 8) | alpha;
|
||||
}
|
||||
|
||||
private java.awt.image.BufferedImage readImage(
|
||||
|
||||
@ -1161,22 +1161,19 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
Object paletteMetadata,
|
||||
String artifactFile,
|
||||
Map<Integer, Integer> declaredPaletteIndexByRgb) {
|
||||
final int[] originalArgb8888 = originalPaletteColors(paletteMetadata);
|
||||
if (originalArgb8888.length == 0) {
|
||||
final int[] rgba8888 = paletteColors(paletteMetadata);
|
||||
if (rgba8888.length == 0) {
|
||||
throw new IllegalStateException("Selected tile artifact is missing palette metadata required for palette remapping: " + artifactFile);
|
||||
}
|
||||
final byte[] remapped = Arrays.copyOf(tile.paletteIndices(), tile.paletteIndices().length);
|
||||
for (int index = 0; index < remapped.length; index += 1) {
|
||||
final int paletteIndex = Byte.toUnsignedInt(remapped[index]);
|
||||
if (paletteIndex == 0) {
|
||||
continue;
|
||||
}
|
||||
if (paletteIndex > originalArgb8888.length) {
|
||||
if (paletteIndex >= rgba8888.length) {
|
||||
throw new IllegalStateException("Tile palette index exceeds discovered palette size for artifact: " + artifactFile);
|
||||
}
|
||||
final int originalArgb = originalArgb8888[paletteIndex - 1];
|
||||
final Integer materialIndex = declaredPaletteIndexByRgb.get(rgb24(originalArgb));
|
||||
if (materialIndex == null || materialIndex < 1 || materialIndex > 15) {
|
||||
final int rgba = rgba8888[paletteIndex];
|
||||
final Integer materialIndex = declaredPaletteIndexByRgb.get(rgba);
|
||||
if (materialIndex == null || materialIndex < 0 || materialIndex > 15) {
|
||||
throw new IllegalStateException("Unable to resolve canonical palette index for artifact color in " + artifactFile);
|
||||
}
|
||||
remapped[index] = (byte) (materialIndex & 0x0F);
|
||||
@ -1189,29 +1186,29 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
if (!(palettesNode instanceof ArrayNode palettesArray) || palettesArray.isEmpty()) {
|
||||
throw new IllegalStateException("Glyph packing requires at least one declared pipeline palette.");
|
||||
}
|
||||
final JsonNode paletteNode = palettesArray.get(0).path("palette").path("originalArgb8888");
|
||||
final JsonNode paletteNode = palettesArray.get(0).path("palette").path("rgba8888");
|
||||
if (!paletteNode.isArray() || paletteNode.isEmpty()) {
|
||||
throw new IllegalStateException("Glyph packing requires originalArgb8888 entries in the declared pipeline palette.");
|
||||
throw new IllegalStateException("Glyph packing requires rgba8888 entries in the declared pipeline palette.");
|
||||
}
|
||||
final LinkedHashMap<Integer, Integer> mapping = new LinkedHashMap<>();
|
||||
for (int index = 0; index < paletteNode.size(); index += 1) {
|
||||
final JsonNode colorNode = paletteNode.get(index);
|
||||
if (!colorNode.canConvertToInt()) {
|
||||
throw new IllegalStateException("Declared pipeline palette contains a non-integer originalArgb8888 entry.");
|
||||
throw new IllegalStateException("Declared pipeline palette contains a non-integer rgba8888 entry.");
|
||||
}
|
||||
mapping.put(rgb24(colorNode.intValue()), index + 1);
|
||||
mapping.put(colorNode.intValue(), index);
|
||||
}
|
||||
return Map.copyOf(mapping);
|
||||
}
|
||||
|
||||
private int[] originalPaletteColors(Object paletteMetadata) {
|
||||
private int[] paletteColors(Object paletteMetadata) {
|
||||
if (paletteMetadata instanceof PackerPaletteV1 palette) {
|
||||
return palette.originalArgb8888();
|
||||
return palette.rgba8888();
|
||||
}
|
||||
if (!(paletteMetadata instanceof Map<?, ?> map)) {
|
||||
return new int[0];
|
||||
}
|
||||
final Object colors = map.get("originalArgb8888");
|
||||
final Object colors = map.get("rgba8888");
|
||||
if (!(colors instanceof List<?> list)) {
|
||||
return new int[0];
|
||||
}
|
||||
@ -1226,10 +1223,6 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return values;
|
||||
}
|
||||
|
||||
private int rgb24(int argb) {
|
||||
return argb & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
private PackedAsset packSceneBank(PackerRuntimeAsset runtimeAsset, PackerAssetDeclaration declaration) {
|
||||
final ScenePackRequest request = runtimeAsset.walkProjection().buildCandidateFiles().stream()
|
||||
.map(file -> file.metadata().get("scenePackRequest"))
|
||||
|
||||
@ -109,17 +109,12 @@ public final class PackerReadMessageMapper {
|
||||
"paletteIndices", List.copyOf(paletteIndices));
|
||||
}
|
||||
if (value instanceof PackerPaletteV1 palette) {
|
||||
final List<Integer> originalArgb8888 = new java.util.ArrayList<>(palette.originalArgb8888().length);
|
||||
for (int argb : palette.originalArgb8888()) {
|
||||
originalArgb8888.add(argb);
|
||||
}
|
||||
final List<Integer> convertedRgb565 = new java.util.ArrayList<>(palette.convertedRgb565().length);
|
||||
for (short rgb565 : palette.convertedRgb565()) {
|
||||
convertedRgb565.add(Short.toUnsignedInt(rgb565));
|
||||
final List<Integer> rgba8888 = new java.util.ArrayList<>(palette.rgba8888().length);
|
||||
for (int rgba : palette.rgba8888()) {
|
||||
rgba8888.add(rgba);
|
||||
}
|
||||
return Map.of(
|
||||
"originalArgb8888", List.copyOf(originalArgb8888),
|
||||
"convertedRgb565", List.copyOf(convertedRgb565));
|
||||
"rgba8888", List.copyOf(rgba8888));
|
||||
}
|
||||
if (value instanceof Map<?, ?> nestedMap) {
|
||||
final java.util.LinkedHashMap<String, Object> normalized = new java.util.LinkedHashMap<>();
|
||||
|
||||
@ -5,8 +5,10 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.models.PackerAssetCacheEntry;
|
||||
import p.packer.models.PackerFileCacheEntry;
|
||||
import p.packer.models.PackerTileIndexedV1;
|
||||
import p.packer.models.PackerDiagnostic;
|
||||
import p.packer.models.PackerGlyphBankRequirements;
|
||||
import p.packer.models.PackerPaletteV1;
|
||||
import p.packer.models.PackerTileIndexedV1;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -42,7 +44,7 @@ final class PackerGlyphBankWalkerTest {
|
||||
null,
|
||||
Map.of(
|
||||
"tile", Map.of("width", 16, "height", 16),
|
||||
"palette", Map.of("originalArgb8888", List.of(0xFFFFFFFF))),
|
||||
"palette", Map.of("rgba8888", List.of(0xFFFFFFFF))),
|
||||
List.of())));
|
||||
|
||||
final var result = walker.walk(assetRoot, new PackerGlyphBankRequirements(16), Optional.of(priorCache));
|
||||
@ -52,6 +54,54 @@ final class PackerGlyphBankWalkerTest {
|
||||
assertTrue(tile.paletteIndices().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesRgba8888PaletteDataAndUsesIndexZeroAsOrdinaryColor() throws Exception {
|
||||
final Path assetRoot = Files.createDirectories(tempDir.resolve("rgba"));
|
||||
final Path filePath = assetRoot.resolve("tile.png");
|
||||
final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
image.setRGB(0, 0, 0x80FF0000);
|
||||
image.setRGB(1, 0, 0x00000000);
|
||||
image.setRGB(2, 0, 0xFFFF00FF);
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 3; x < 16; x++) {
|
||||
image.setRGB(x, y, 0xFF00FF00);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", filePath.toFile());
|
||||
|
||||
final PackerGlyphBankWalker walker = new PackerGlyphBankWalker(MAPPER);
|
||||
final var result = walker.walk(assetRoot, new PackerGlyphBankRequirements(16));
|
||||
|
||||
assertTrue(result.diagnostics().stream().noneMatch(diagnostic -> diagnostic.message().contains("partial alpha")));
|
||||
final var metadata = result.probeResults().getFirst().metadata();
|
||||
final PackerTileIndexedV1 tile = (PackerTileIndexedV1) metadata.get("tile");
|
||||
final PackerPaletteV1 palette = (PackerPaletteV1) metadata.get("palette");
|
||||
|
||||
assertEquals(0, Byte.toUnsignedInt(tile.paletteIndices()[0]));
|
||||
assertEquals(1, Byte.toUnsignedInt(tile.paletteIndices()[1]));
|
||||
assertEquals(2, Byte.toUnsignedInt(tile.paletteIndices()[2]));
|
||||
assertArrayEquals(new int[]{0xFF000080, 0x00000000, 0xFF00FFFF, 0x00FF00FF}, palette.rgba8888());
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsSixteenRgbaColorsAndRejectsSeventeenth() throws Exception {
|
||||
final Path validRoot = Files.createDirectories(tempDir.resolve("sixteen"));
|
||||
writeColorCountTile(validRoot.resolve("tile.png"), 16);
|
||||
|
||||
final PackerGlyphBankWalker walker = new PackerGlyphBankWalker(MAPPER);
|
||||
final var valid = walker.walk(validRoot, new PackerGlyphBankRequirements(16));
|
||||
|
||||
assertTrue(valid.diagnostics().stream().noneMatch(PackerDiagnostic::blocking));
|
||||
|
||||
final Path invalidRoot = Files.createDirectories(tempDir.resolve("seventeen"));
|
||||
writeColorCountTile(invalidRoot.resolve("tile.png"), 17);
|
||||
final var invalid = walker.walk(invalidRoot, new PackerGlyphBankRequirements(16));
|
||||
|
||||
assertTrue(invalid.probeResults().stream()
|
||||
.flatMap(probeResult -> probeResult.diagnostics().stream())
|
||||
.anyMatch(PackerDiagnostic::blocking));
|
||||
}
|
||||
|
||||
private void writeTile(Path path) throws Exception {
|
||||
final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < 16; y++) {
|
||||
@ -61,4 +111,18 @@ final class PackerGlyphBankWalkerTest {
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
|
||||
private void writeColorCountTile(Path path, int colorCount) throws Exception {
|
||||
final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
final int index = Math.min(colorCount - 1, y * 16 + x);
|
||||
final int red = (index * 17) & 0xFF;
|
||||
final int green = (index * 31) & 0xFF;
|
||||
final int blue = (index * 47) & 0xFF;
|
||||
image.setRGB(x, y, 0xFF000000 | (red << 16) | (green << 8) | blue);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user