packer (WIP)
This commit is contained in:
parent
d9911a63dc
commit
db8c17864f
@ -58,8 +58,39 @@ Contexto atual de código:
|
|||||||
- o snapshot atual já consegue expor arquivos candidatos e metadata de walk;
|
- 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`.
|
- 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.
|
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
|
## Options
|
||||||
|
|
||||||
@ -104,10 +135,17 @@ O `tile bank` deve produzir um payload binário único por asset incluído no bu
|
|||||||
Regras recomendadas:
|
Regras recomendadas:
|
||||||
|
|
||||||
- o payload é derivado apenas dos artifacts selecionados que realmente entram no build atual;
|
- 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;
|
- 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;
|
- `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
|
### 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.
|
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
|
### Metadata Recommendation
|
||||||
|
|
||||||
`AssetEntry.metadata` deve receber apenas os campos runtime-consumable e format-relevant.
|
`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:
|
Direção inicial recomendada:
|
||||||
|
|
||||||
- metadados declarativos como `tile_size` entram no sink runtime;
|
- 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`;
|
- 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.
|
- 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;
|
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.
|
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
|
### Packing Boundary Recommendation
|
||||||
|
|
||||||
O seletor de packing para `tile bank` deve operar sobre os probes já descobertos pelo walker family-oriented.
|
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;
|
- 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;
|
- 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.
|
- 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
|
## 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?
|
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. Quais campos derivados além de `tile_size` precisam entrar em `AssetEntry.metadata` já na primeira versão?
|
2. Which normalization diagnostics should be `blocking` versus `warning` specifically for:
|
||||||
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`?
|
- artifact index gaps or duplicates
|
||||||
4. O formato inicial do payload precisa carregar múltiplos sub-blocos internos explícitos ou um stream único já é suficiente para `v1`?
|
- sheet capacity overflow
|
||||||
|
- palette list overflow beyond `64`
|
||||||
|
- banks without declared palettes
|
||||||
|
- fragile tile indices across the declared bank palette set?
|
||||||
|
|
||||||
## Expected Follow-up
|
## Expected Follow-up
|
||||||
|
|
||||||
|
|||||||
@ -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`
|
||||||
@ -96,6 +96,40 @@ Rules:
|
|||||||
- `output.pipeline` carries pipeline-injected metadata kept separate at authoring time;
|
- `output.pipeline` carries pipeline-injected metadata kept separate at authoring time;
|
||||||
- codec must remain explicit and must not be hidden inside format naming.
|
- 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
|
## Metadata Source Segmentation and Runtime Sink
|
||||||
|
|
||||||
`asset.json` may keep metadata segmented by concern during authoring.
|
`asset.json` may keep metadata segmented by concern during authoring.
|
||||||
|
|||||||
@ -981,16 +981,19 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
|||||||
final ObjectNode outputNode = mutableObject(manifest, "output");
|
final ObjectNode outputNode = mutableObject(manifest, "output");
|
||||||
final ObjectNode pipelineNode = mutableObject(outputNode, "pipeline");
|
final ObjectNode pipelineNode = mutableObject(outputNode, "pipeline");
|
||||||
final ArrayNode palettesNode = pipelineNode.putArray("palettes");
|
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> originalArgb8888 = integerList(palette.get("originalArgb8888"));
|
||||||
final List<Integer> convertedRgb565 = integerList(palette.get("convertedRgb565"));
|
final List<Integer> convertedRgb565 = integerList(palette.get("convertedRgb565"));
|
||||||
if (originalArgb8888.isEmpty() || convertedRgb565.isEmpty()) {
|
if (originalArgb8888.isEmpty() || convertedRgb565.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Each selected palette must contain originalArgb8888 and convertedRgb565 entries.");
|
throw new IllegalArgumentException("Each selected palette must contain originalArgb8888 and convertedRgb565 entries.");
|
||||||
}
|
}
|
||||||
final ObjectNode paletteNode = palettesNode.addObject();
|
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);
|
originalArgb8888.forEach(originalNode::add);
|
||||||
final ArrayNode convertedNode = paletteNode.putArray("convertedRgb565");
|
final ArrayNode convertedNode = payloadNode.putArray("convertedRgb565");
|
||||||
convertedRgb565.forEach(convertedNode::add);
|
convertedRgb565.forEach(convertedNode::add);
|
||||||
}
|
}
|
||||||
if (palettesNode.isEmpty()) {
|
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) {
|
private List<Integer> integerList(Object value) {
|
||||||
if (!(value instanceof Iterable<?> iterable)) {
|
if (!(value instanceof Iterable<?> iterable)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import java.util.*;
|
|||||||
|
|
||||||
public final class PackerAssetDeclarationParser {
|
public final class PackerAssetDeclarationParser {
|
||||||
private static final Set<Integer> SUPPORTED_SCHEMA_VERSIONS = Set.of(1);
|
private static final Set<Integer> SUPPORTED_SCHEMA_VERSIONS = Set.of(1);
|
||||||
|
private static final int MAX_PIPELINE_PALETTES = 64;
|
||||||
|
|
||||||
private final ObjectMapper mapper;
|
private final ObjectMapper mapper;
|
||||||
|
|
||||||
@ -270,11 +271,96 @@ public final class PackerAssetDeclarationParser {
|
|||||||
true));
|
true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ("palettes".equals(key)) {
|
||||||
|
pipelineMetadata.put(key, validatePaletteDeclarations(valueNode, diagnostics, manifestPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
pipelineMetadata.put(key, valueNode.deepCopy());
|
pipelineMetadata.put(key, valueNode.deepCopy());
|
||||||
});
|
});
|
||||||
return Map.copyOf(pipelineMetadata);
|
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(
|
private void rejectLegacyInputs(
|
||||||
final JsonNode node,
|
final JsonNode node,
|
||||||
final List<PackerDiagnostic> diagnostics,
|
final List<PackerDiagnostic> diagnostics,
|
||||||
|
|||||||
@ -179,6 +179,7 @@ public final class PackerAssetDetailsService {
|
|||||||
palettes.add(normalized);
|
palettes.add(normalized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
palettes.sort(Comparator.comparingInt(palette -> ((Number) palette.get("index")).intValue()));
|
||||||
return List.copyOf(palettes);
|
return List.copyOf(palettes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,8 +187,13 @@ public final class PackerAssetDetailsService {
|
|||||||
if (!(paletteNode instanceof ObjectNode paletteObject)) {
|
if (!(paletteNode instanceof ObjectNode paletteObject)) {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
final JsonNode originalNode = paletteObject.path("originalArgb8888");
|
final JsonNode indexNode = paletteObject.path("index");
|
||||||
final JsonNode convertedNode = paletteObject.path("convertedRgb565");
|
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()) {
|
if (!originalNode.isArray() || !convertedNode.isArray()) {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
@ -206,6 +212,7 @@ public final class PackerAssetDetailsService {
|
|||||||
convertedRgb565.add(colorNode.intValue());
|
convertedRgb565.add(colorNode.intValue());
|
||||||
}
|
}
|
||||||
return Map.of(
|
return Map.of(
|
||||||
|
"index", indexNode.intValue(),
|
||||||
"originalArgb8888", List.copyOf(originalArgb8888),
|
"originalArgb8888", List.copyOf(originalArgb8888),
|
||||||
"convertedRgb565", List.copyOf(convertedRgb565));
|
"convertedRgb565", List.copyOf(convertedRgb565));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -505,8 +505,10 @@ final class FileSystemPackerWorkspaceServiceTest {
|
|||||||
|
|
||||||
final var manifest = MAPPER.readTree(projectRoot.resolve("assets/ui/atlas/asset.json").toFile());
|
final var manifest = MAPPER.readTree(projectRoot.resolve("assets/ui/atlas/asset.json").toFile());
|
||||||
assertEquals(2, manifest.path("output").path("pipeline").path("palettes").size());
|
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(0, manifest.path("output").path("pipeline").path("palettes").get(0).path("index").asInt());
|
||||||
assertEquals(0x001F, manifest.path("output").path("pipeline").path("palettes").get(1).path("convertedRgb565").get(0).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
|
@Test
|
||||||
|
|||||||
@ -91,6 +91,83 @@ final class PackerAssetDeclarationParserTest {
|
|||||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("output.pipeline")));
|
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
|
@Test
|
||||||
void rejectsMalformedJsonWithStructuralDiagnostic() {
|
void rejectsMalformedJsonWithStructuralDiagnostic() {
|
||||||
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));
|
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));
|
||||||
|
|||||||
@ -63,9 +63,11 @@ final class PackerAssetDetailsServiceTest {
|
|||||||
final ObjectNode manifest = (ObjectNode) mapper.readTree(manifestPath.toFile());
|
final ObjectNode manifest = (ObjectNode) mapper.readTree(manifestPath.toFile());
|
||||||
final ObjectNode pipeline = ((ObjectNode) manifest.path("output")).putObject("pipeline");
|
final ObjectNode pipeline = ((ObjectNode) manifest.path("output")).putObject("pipeline");
|
||||||
final var palettes = pipeline.putArray("palettes");
|
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);
|
.putArray("originalArgb8888").add(0xFFFF0000).add(0xFF00FF00);
|
||||||
((ObjectNode) palettes.get(0))
|
((ObjectNode) declaration.path("palette"))
|
||||||
.putArray("convertedRgb565").add(0xF800).add(0x07E0);
|
.putArray("convertedRgb565").add(0xF800).add(0x07E0);
|
||||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
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)));
|
final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1)));
|
||||||
|
|
||||||
assertEquals(1, result.details().pipelinePalettes().size());
|
assertEquals(1, result.details().pipelinePalettes().size());
|
||||||
|
assertEquals(3, result.details().pipelinePalettes().getFirst().get("index"));
|
||||||
assertEquals(
|
assertEquals(
|
||||||
List.of(0xFFFF0000, 0xFF00FF00),
|
List.of(0xFFFF0000, 0xFF00FF00),
|
||||||
result.details().pipelinePalettes().getFirst().get("originalArgb8888"));
|
result.details().pipelinePalettes().getFirst().get("originalArgb8888"));
|
||||||
|
|||||||
@ -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",
|
"source" : "Assets",
|
||||||
"message" : "Asset scan diagnostics updated.",
|
"message" : "Asset scan diagnostics updated.",
|
||||||
"severity" : "INFO",
|
"severity" : "INFO",
|
||||||
@ -2398,104 +2498,4 @@
|
|||||||
"message" : "Discovered asset: one-more-atlas",
|
"message" : "Discovered asset: one-more-atlas",
|
||||||
"severity" : "INFO",
|
"severity" : "INFO",
|
||||||
"sticky" : false
|
"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
@ -12,11 +12,11 @@
|
|||||||
},
|
},
|
||||||
"pipeline" : {
|
"pipeline" : {
|
||||||
"palettes" : [ {
|
"palettes" : [ {
|
||||||
"originalArgb8888" : [ -265674, -1736296, -13905598, -11518505, -14439577, -2467509 ],
|
"index" : 0,
|
||||||
"convertedRgb565" : [ 65414, 58387, 11912, 20986, 9548, 56009 ]
|
"palette" : {
|
||||||
}, {
|
"originalArgb8888" : [ -265674, -1736296, -13905598, -11518505, -14439577, -2467509 ],
|
||||||
"originalArgb8888" : [ -265674 ],
|
"convertedRgb565" : [ 65414, 58387, 11912, 20986, 9548, 56009 ]
|
||||||
"convertedRgb565" : [ 65414 ]
|
}
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user