packer (WIP)

This commit is contained in:
bQUARKz 2026-03-20 09:19:40 +00:00
parent d9911a63dc
commit db8c17864f
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
12 changed files with 815 additions and 406 deletions

View File

@ -58,8 +58,39 @@ Contexto atual de código:
- o snapshot atual já consegue expor arquivos candidatos e metadata de walk;
- ainda não existe materialização final de payload de `tile bank` dentro de `assets.pa`.
Consumer baseline now confirmed in `../runtime`:
- `TILES` now uses `codec = NONE` as the runtime-facing v1 baseline;
- serialized tile pixels are packed `u4` palette indices in payload order;
- the runtime expands those packed indices into one `u8` logical index per pixel in memory after decode;
- tile-bank palettes are serialized as `RGB565` `u16` values in little-endian order;
- runtime-facing v1 uses `64` palettes per tile bank;
- the runtime loader currently requires the following metadata fields for tile banks:
- `tile_size`
- `width`
- `height`
- `palette_count`
- for v1, `palette_count` must be `64`;
- for v1, the serialized tile-bank payload is:
1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes;
2. one palette block of `64 * 16 * 2 = 2048` bytes;
- for v1, the runtime-side size expectations are:
- `size = ceil(width * height / 2) + 2048`
- `decoded_size = (width * height) + 2048`
- for the producer-side contract discussed here, `tile_id = 0` remains valid and must not be reserved away by the packer.
Relevant confirmed runtime references:
- [`../../../runtime/docs/runtime/specs/15-asset-management.md`](../../../runtime/docs/runtime/specs/15-asset-management.md)
- [`../../../runtime/docs/runtime/specs/04-gfx-peripheral.md`](../../../runtime/docs/runtime/specs/04-gfx-peripheral.md)
- [`../../../runtime/crates/console/prometeu-drivers/src/asset.rs`](../../../runtime/crates/console/prometeu-drivers/src/asset.rs)
- [`../../../runtime/crates/console/prometeu-hal/src/tile_bank.rs`](../../../runtime/crates/console/prometeu-hal/src/tile_bank.rs)
- tilemap empty-cell semantics remain under active runtime discussion and must not currently force the packer to reserve `tile_id = 0`:
[`../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md`](../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md)
Isso significa que o problema agora não é descoberta de arquivos.
É fechamento do contrato `tile bank -> runtime asset entry + payload bytes`.
O lado consumidor já está suficientemente claro.
O problema agora é fechar o contrato produtor `tile bank -> runtime asset entry + payload bytes` no packer sem contradizer esse baseline runtime.
## Options
@ -104,10 +135,17 @@ O `tile bank` deve produzir um payload binário único por asset incluído no bu
Regras recomendadas:
- o payload é derivado apenas dos artifacts selecionados que realmente entram no build atual;
- a ordem de agregação dos artifacts deve ser determinística;
- a ordem de agregação dos artifacts deve ser determinística by `artifacts[*].index`;
- for v1, `1 artifact = 1 tile`;
- for the current target, the canonical tile-bank sheet is always `256 x 256`;
- o payload final do asset deve ter fronteiras e interpretação definidas pelo próprio contrato do formato, não por convenção incidental de concatenação;
- para `TILES/indexed_v1`, o payload v1 já deve assumir:
1. plano de pixels packed `u4`;
2. bloco de paletas `64 * 16 * u16`;
- palettes must always be materialized to `RGB565` during pack emission;
- `size` deve representar o tamanho emitido no payload region;
- `decoded_size` deve representar o tamanho lógico relevante para o runtime quando o codec exigir distinção entre payload armazenado e materialização decodificada.
- `decoded_size` deve seguir a convenção runtime já confirmada:
tamanho expandido dos indices em memória mais o bloco de paletas runtime-facing.
### Runtime Entry Recommendation
@ -124,6 +162,18 @@ Cada `tile bank` emitido para o runtime deve preencher, no mínimo:
O contrato de `bank_type`, `codec` e `decoded_size` não deve ser deixado implícito no packer implementation detail.
Baseline now fixed by the runtime consumer:
- `bank_type = TILES`
- `codec = NONE`
- metadata mínima obrigatória:
- `tile_size`
- `width`
- `height`
- `palette_count = 64`
- `width` and `height` are bank-sheet helpers, not per-artifact dimensions
- with the current v1 target, the emitted bank sheet is fixed at `256 x 256`
### Metadata Recommendation
`AssetEntry.metadata` deve receber apenas os campos runtime-consumable e format-relevant.
@ -131,7 +181,18 @@ O contrato de `bank_type`, `codec` e `decoded_size` não deve ser deixado implí
Direção inicial recomendada:
- metadados declarativos como `tile_size` entram no sink runtime;
- metadados derivados necessários para leitura correta do runtime também entram no sink runtime;
- metadados derivados necessários para leitura correta do runtime entram no sink runtime, pelo menos:
- `width`
- `height`
- `palette_count`;
- `AssetEntry.metadata` should aggregate normalized maps derived from:
- `asset.json.output.metadata`
- `asset.json.output.codec_configuration`
- `asset.json.output.pipeline`;
- bank palettes are declared in `asset.json.output.pipeline.palettes` using explicit `{ index, palette }` entries and emitted in ascending numeric `index` order;
- any tile in the bank may be rendered with any palette in the bank;
- palette assignment is therefore not a per-artifact packing contract and remains a runtime draw-time concern;
- the packer must nevertheless validate whether the declared bank palette set safely covers the indices used by packed tiles.
- detalhes de pipeline úteis apenas para inspeção e tooling não devem dominar `AssetEntry.metadata`;
- quando um detalhe interno for necessário apenas para tooling, ele deve preferir companion tooling data em vez de inflar o contrato runtime.
@ -145,6 +206,13 @@ O build de `tile bank` deve falhar quando qualquer uma das seguintes condições
4. houver colisão ou ambiguidade ao convergir metadata runtime-facing;
5. o packer não conseguir derivar de forma determinística os campos exigidos para a entry runtime.
Additional first-wave diagnostic expectations:
- bank without declared palettes in `asset.json.output.pipeline.palettes` must emit a diagnostic;
- tile banks whose declared palette list exceeds `64` must fail;
- tiles that use fragile indices, meaning indices not represented safely across the full declared bank palette set, should emit a `WARNING`;
- that fragile-index warning is advisory in the first wave and should not block pack by itself unless later promoted by decision.
### Packing Boundary Recommendation
O seletor de packing para `tile bank` deve operar sobre os probes já descobertos pelo walker family-oriented.
@ -155,13 +223,21 @@ Regras:
- a seleção do que entra no payload final acontece na policy/materialization layer de packing;
- somente probes do asset registrado, incluído no build, e efetivamente selecionados pelo contrato do formato entram na materialização final;
- quando o `PackerRuntimeMaterializationConfig` estiver em `PACKING`, esses probes relevantes devem carregar bytes opcionais preenchidos para congelar o input do pack.
- palette declarations in `asset.json.output.pipeline.palettes` carry explicit semantic identity through `index`;
- palette order is ascending numeric `index`, never raw array position;
- palette ids are the normalized declared `index` values from that pipeline palette list;
- all tiles in the bank may use any palette declared in the bank;
- palette selection is a runtime draw concern, not a tile-payload embedding concern.
## Open Questions
1. Qual é o payload canônico inicial de `TILES/indexed_v1`: tiles brutos indexados, tiles mais tabela auxiliar, ou outro envelope mínimo?
2. Quais campos derivados além de `tile_size` precisam entrar em `AssetEntry.metadata` já na primeira versão?
3. `decoded_size` para `tile bank` deve refletir bytes lógicos pós-codec, bytes efetivos em bank, ou ambos via combinação `decoded_size + metadata`?
4. O formato inicial do payload precisa carregar múltiplos sub-blocos internos explícitos ou um stream único já é suficiente para `v1`?
1. Given the fixed `256 x 256` bank target, what is the canonical tile placement rule inside that sheet for `tile_size = 8`, `16`, and `32`?
2. Which normalization diagnostics should be `blocking` versus `warning` specifically for:
- artifact index gaps or duplicates
- sheet capacity overflow
- palette list overflow beyond `64`
- banks without declared palettes
- fragile tile indices across the declared bank palette set?
## Expected Follow-up

View File

@ -0,0 +1,113 @@
# PR-32 Palette Declarations with Explicit Index Contract
Domain Owner: `docs/packer`
Cross-Domain Impact: `docs/studio`, `../runtime`
## Briefing
The current authoring shape for `asset.json.output.pipeline.palettes` is still too dependent on list order as semantic identity.
That is fragile:
- editorial reordering can silently change meaning;
- merges become harder to review safely;
- the packer has to infer identity from position instead of reading it explicitly.
The repository already depends on stable palette ordering for `tile bank` packing.
That ordering should therefore be explicit in the authoring contract.
## Decisions de Origem
- [`../decisions/Pack Wizard Pack Execution Semantics Decision.md`](../decisions/Pack%20Wizard%20Pack%20Execution%20Semantics%20Decision.md)
- ongoing agenda:
[`../agendas/Tile Bank Packing Materialization Agenda.md`](../agendas/Tile%20Bank%20Packing%20Materialization%20Agenda.md)
## Objective
Replace list-position-defined palette identity with an explicit index-bearing declaration contract for `asset.json.output.pipeline.palettes`.
Recommended shape:
```json
"palettes": [
{
"index": 0,
"palette": { ... }
},
{
"index": 1,
"palette": { ... }
}
]
```
## Dependencies
- tile-bank materialization agenda in `docs/packer/agendas`
- runtime-side tile-bank consumer contract already aligned around stable palette ids
## Scope
- define the normative authoring shape for `output.pipeline.palettes`
- make `index` the semantic identity of each declared palette
- require packer normalization to sort by `index`, not by list position
- define diagnostics for duplicate, missing, invalid, or malformed palette entries
- prepare propagation into:
- packer specs
- parser/runtime-facing normalization code
- Studio editing surfaces if they expose palette management
## Non-Goals
- no immediate migration of `artifacts` in this PR
- no tile-bank payload implementation in this PR
- no runtime code changes in this PR
- no speculative keyed-object JSON contract for palettes in this wave
## Execution Method
1. Update the packer-side documentation/decision chain to make palette identity explicit.
2. Define `output.pipeline.palettes` as a list of records with:
- `index`
- `palette`
3. State that semantic ordering is ascending numeric `index`, never raw list position.
4. Define structural diagnostics:
- duplicate `index` => blocking
- negative or malformed `index` => blocking
- missing `palette` payload => blocking
- palette count above the runtime-facing v1 limit => blocking
5. Define whether index gaps are:
- blocking
- or warning/advisory
6. Prepare follow-up implementation PRs for parser, validation, and Studio surfaces.
## Contract Direction
First-wave packer contract direction:
1. `output.pipeline.palettes` remains a JSON array for authoring ergonomics;
2. each entry must declare an explicit `index`;
3. each entry must declare a `palette` object payload;
4. packer meaning comes from numeric `index`, not from the physical array position;
5. the runtime-facing `palette_id` consumed later by tile/sprite draw paths is the normalized numeric palette `index`.
## Acceptance Criteria
- the packer docs/spec path no longer relies on raw list order as palette identity
- the explicit-index palette shape is documented clearly enough for parser and Studio implementation
- duplicate or malformed palette indices are identified as structural diagnostics
- the contract leaves no ambiguity about whether sorting happens by array order or by `index`
## Validation
- editorial validation across packer agenda/decision/spec material
- parser-oriented review proving the contract is implementable without hidden heuristics
- Studio-facing review confirming the editing UX can preserve stable palette identity
## Affected Artifacts
- `docs/packer/agendas/**`
- `docs/packer/decisions/**`
- `docs/packer/specs/3. Asset Declaration and Virtual Asset Contract Specification.md`
- `docs/packer/specs/4. Build Artifacts and Deterministic Packing Specification.md`
- future parser/runtime materialization code under `prometeu-packer-v1`

View File

@ -96,6 +96,40 @@ Rules:
- `output.pipeline` carries pipeline-injected metadata kept separate at authoring time;
- codec must remain explicit and must not be hidden inside format naming.
### Explicit Index Collections
When `output.pipeline` carries ordered collections whose members need stable semantic identity, that identity must not depend on raw list position alone.
Rules:
- collections such as `output.pipeline.palettes` may remain JSON arrays for authoring ergonomics;
- when a collection member has runtime- or packing-relevant identity, each entry must carry an explicit `index`;
- `output.pipeline.palettes` entries must use the shape `{ "index": <int>, "palette": { ... } }`;
- the packer must sort such entries by numeric `index`, not by physical array position;
- duplicate or malformed indices are structural errors;
- missing or malformed `palette` payloads are structural errors;
- editorial reordering of the JSON array alone must not change semantic meaning.
Example:
```json
{
"output": {
"pipeline": {
"palettes": [
{
"index": 0,
"palette": {
"originalArgb8888": [4294901760, 4278255360],
"convertedRgb565": [63488, 2016]
}
}
]
}
}
}
```
## Metadata Source Segmentation and Runtime Sink
`asset.json` may keep metadata segmented by concern during authoring.

View File

@ -981,16 +981,19 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
final ObjectNode outputNode = mutableObject(manifest, "output");
final ObjectNode pipelineNode = mutableObject(outputNode, "pipeline");
final ArrayNode palettesNode = pipelineNode.putArray("palettes");
for (Map<String, Object> palette : request.selectedPalettes()) {
for (int paletteOrder = 0; paletteOrder < request.selectedPalettes().size(); paletteOrder += 1) {
final Map<String, Object> palette = request.selectedPalettes().get(paletteOrder);
final List<Integer> originalArgb8888 = integerList(palette.get("originalArgb8888"));
final List<Integer> convertedRgb565 = integerList(palette.get("convertedRgb565"));
if (originalArgb8888.isEmpty() || convertedRgb565.isEmpty()) {
throw new IllegalArgumentException("Each selected palette must contain originalArgb8888 and convertedRgb565 entries.");
}
final ObjectNode paletteNode = palettesNode.addObject();
final ArrayNode originalNode = paletteNode.putArray("originalArgb8888");
paletteNode.put("index", paletteIndex(palette, paletteOrder));
final ObjectNode payloadNode = paletteNode.putObject("palette");
final ArrayNode originalNode = payloadNode.putArray("originalArgb8888");
originalArgb8888.forEach(originalNode::add);
final ArrayNode convertedNode = paletteNode.putArray("convertedRgb565");
final ArrayNode convertedNode = payloadNode.putArray("convertedRgb565");
convertedRgb565.forEach(convertedNode::add);
}
if (palettesNode.isEmpty()) {
@ -998,6 +1001,14 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
}
}
private int paletteIndex(Map<String, Object> palette, int fallbackIndex) {
final Object rawIndex = palette.get("index");
if (rawIndex instanceof Number number && number.intValue() >= 0) {
return number.intValue();
}
return fallbackIndex;
}
private List<Integer> integerList(Object value) {
if (!(value instanceof Iterable<?> iterable)) {
return List.of();

View File

@ -18,6 +18,7 @@ import java.util.*;
public final class PackerAssetDeclarationParser {
private static final Set<Integer> SUPPORTED_SCHEMA_VERSIONS = Set.of(1);
private static final int MAX_PIPELINE_PALETTES = 64;
private final ObjectMapper mapper;
@ -270,11 +271,96 @@ public final class PackerAssetDeclarationParser {
true));
return;
}
if ("palettes".equals(key)) {
pipelineMetadata.put(key, validatePaletteDeclarations(valueNode, diagnostics, manifestPath));
return;
}
pipelineMetadata.put(key, valueNode.deepCopy());
});
return Map.copyOf(pipelineMetadata);
}
private JsonNode validatePaletteDeclarations(
final JsonNode node,
final List<PackerDiagnostic> diagnostics,
final Path manifestPath) {
if (!node.isArray()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.pipeline.palettes' must be an array of palette declarations.",
manifestPath,
true));
return node.deepCopy();
}
final Set<Integer> seenIndices = new HashSet<>();
int paletteCount = 0;
for (JsonNode entry : node) {
paletteCount += 1;
if (!entry.isObject()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Each palette declaration must be an object with non-negative integer 'index' and object 'palette'.",
manifestPath,
true));
continue;
}
final JsonNode indexNode = entry.path("index");
final JsonNode paletteNode = entry.path("palette");
if (!indexNode.isInt() || indexNode.intValue() < 0) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Each palette declaration must define a non-negative integer 'index'.",
manifestPath,
true));
} else if (!seenIndices.add(indexNode.intValue())) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.pipeline.palettes' contains duplicate palette index " + indexNode.intValue() + ".",
manifestPath,
true));
}
if (!paletteNode.isObject()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Each palette declaration must define an object 'palette' payload.",
manifestPath,
true));
continue;
}
final JsonNode originalNode = paletteNode.path("originalArgb8888");
final JsonNode convertedNode = paletteNode.path("convertedRgb565");
if (!originalNode.isArray() || !convertedNode.isArray()) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Each palette payload must contain array fields 'originalArgb8888' and 'convertedRgb565'.",
manifestPath,
true));
continue;
}
}
if (paletteCount > MAX_PIPELINE_PALETTES) {
diagnostics.add(new PackerDiagnostic(
PackerDiagnosticSeverity.ERROR,
PackerDiagnosticCategory.STRUCTURAL,
"Field 'output.pipeline.palettes' cannot declare more than " + MAX_PIPELINE_PALETTES + " palettes.",
manifestPath,
true));
}
return node.deepCopy();
}
private void rejectLegacyInputs(
final JsonNode node,
final List<PackerDiagnostic> diagnostics,

View File

@ -179,6 +179,7 @@ public final class PackerAssetDetailsService {
palettes.add(normalized);
}
}
palettes.sort(Comparator.comparingInt(palette -> ((Number) palette.get("index")).intValue()));
return List.copyOf(palettes);
}
@ -186,8 +187,13 @@ public final class PackerAssetDetailsService {
if (!(paletteNode instanceof ObjectNode paletteObject)) {
return Map.of();
}
final JsonNode originalNode = paletteObject.path("originalArgb8888");
final JsonNode convertedNode = paletteObject.path("convertedRgb565");
final JsonNode indexNode = paletteObject.path("index");
final JsonNode payloadNode = paletteObject.path("palette");
if (!indexNode.isInt() || indexNode.intValue() < 0 || !(payloadNode instanceof ObjectNode payloadObject)) {
return Map.of();
}
final JsonNode originalNode = payloadObject.path("originalArgb8888");
final JsonNode convertedNode = payloadObject.path("convertedRgb565");
if (!originalNode.isArray() || !convertedNode.isArray()) {
return Map.of();
}
@ -206,6 +212,7 @@ public final class PackerAssetDetailsService {
convertedRgb565.add(colorNode.intValue());
}
return Map.of(
"index", indexNode.intValue(),
"originalArgb8888", List.copyOf(originalArgb8888),
"convertedRgb565", List.copyOf(convertedRgb565));
}

View File

@ -505,8 +505,10 @@ final class FileSystemPackerWorkspaceServiceTest {
final var manifest = MAPPER.readTree(projectRoot.resolve("assets/ui/atlas/asset.json").toFile());
assertEquals(2, manifest.path("output").path("pipeline").path("palettes").size());
assertEquals(0xFFFF0000, manifest.path("output").path("pipeline").path("palettes").get(0).path("originalArgb8888").get(0).asInt());
assertEquals(0x001F, manifest.path("output").path("pipeline").path("palettes").get(1).path("convertedRgb565").get(0).asInt());
assertEquals(0, manifest.path("output").path("pipeline").path("palettes").get(0).path("index").asInt());
assertEquals(0xFFFF0000, manifest.path("output").path("pipeline").path("palettes").get(0).path("palette").path("originalArgb8888").get(0).asInt());
assertEquals(1, manifest.path("output").path("pipeline").path("palettes").get(1).path("index").asInt());
assertEquals(0x001F, manifest.path("output").path("pipeline").path("palettes").get(1).path("palette").path("convertedRgb565").get(0).asInt());
}
@Test

View File

@ -91,6 +91,83 @@ final class PackerAssetDeclarationParserTest {
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("output.pipeline")));
}
@Test
void parsesPaletteDeclarationsUsingExplicitIndexContract() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-palette-contract",
"name": "palette_asset",
"type": "tile_bank",
"output": {
"format": "TILES/indexed_v1",
"codec": "NONE",
"pipeline": {
"palettes": [
{
"index": 1,
"palette": {
"originalArgb8888": [4294901760],
"convertedRgb565": [63488]
}
}
]
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertTrue(result.valid());
assertTrue(result.declaration().outputPipelineMetadata().get("palettes").isArray());
assertEquals(1, result.declaration().outputPipelineMetadata().get("palettes").get(0).path("index").asInt());
assertEquals(63488, result.declaration().outputPipelineMetadata().get("palettes").get(0).path("palette").path("convertedRgb565").get(0).asInt());
}
@Test
void rejectsPaletteDeclarationsWithDuplicateIndices() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-palette-duplicate",
"name": "palette_asset",
"type": "tile_bank",
"output": {
"format": "TILES/indexed_v1",
"codec": "NONE",
"pipeline": {
"palettes": [
{
"index": 0,
"palette": {
"originalArgb8888": [4294901760],
"convertedRgb565": [63488]
}
},
{
"index": 0,
"palette": {
"originalArgb8888": [4278255360],
"convertedRgb565": [2016]
}
}
]
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("duplicate palette index 0")));
}
@Test
void rejectsMalformedJsonWithStructuralDiagnostic() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));

View File

@ -63,9 +63,11 @@ final class PackerAssetDetailsServiceTest {
final ObjectNode manifest = (ObjectNode) mapper.readTree(manifestPath.toFile());
final ObjectNode pipeline = ((ObjectNode) manifest.path("output")).putObject("pipeline");
final var palettes = pipeline.putArray("palettes");
palettes.addObject()
final ObjectNode declaration = palettes.addObject();
declaration.put("index", 3);
declaration.putObject("palette")
.putArray("originalArgb8888").add(0xFFFF0000).add(0xFF00FF00);
((ObjectNode) palettes.get(0))
((ObjectNode) declaration.path("palette"))
.putArray("convertedRgb565").add(0xF800).add(0x07E0);
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
@ -73,6 +75,7 @@ final class PackerAssetDetailsServiceTest {
final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1)));
assertEquals(1, result.details().pipelinePalettes().size());
assertEquals(3, result.details().pipelinePalettes().getFirst().get("index"));
assertEquals(
List.of(0xFFFF0000, 0xFF00FF00),
result.details().pipelinePalettes().getFirst().get("originalArgb8888"));

View File

@ -1,4 +1,104 @@
[ {
"source" : "Assets",
"message" : "7 assets loaded",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bla",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bbb2",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: Bigode",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "7 assets loaded",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bla",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bbb2",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: Bigode",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
@ -2398,104 +2498,4 @@
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bbb2",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: Bigode",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "7 assets loaded",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bla",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: one-more-atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bbb2",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: ui_atlas",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: Bigode",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Studio",
"message" : "Project ready",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Studio",
"message" : "Project loading started",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Studio",
"message" : "Project opened",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "7 assets loaded",
"severity" : "SUCCESS",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Asset scan diagnostics updated.",
"severity" : "INFO",
"sticky" : false
}, {
"source" : "Assets",
"message" : "Discovered asset: bla",
"severity" : "INFO",
"sticky" : false
} ]

File diff suppressed because it is too large Load Diff

View File

@ -12,11 +12,11 @@
},
"pipeline" : {
"palettes" : [ {
"originalArgb8888" : [ -265674, -1736296, -13905598, -11518505, -14439577, -2467509 ],
"convertedRgb565" : [ 65414, 58387, 11912, 20986, 9548, 56009 ]
}, {
"originalArgb8888" : [ -265674 ],
"convertedRgb565" : [ 65414 ]
"index" : 0,
"palette" : {
"originalArgb8888" : [ -265674, -1736296, -13905598, -11518505, -14439577, -2467509 ],
"convertedRgb565" : [ 65414, 58387, 11912, 20986, 9548, 56009 ]
}
} ]
}
},