Files
2026-05-27 23:29:53 +01:00

229 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: image-quantisation
**Package**: `@suaveplan/image-quantisation`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## ADDED Requirements
### Requirement: Package provides image complexity analysis
The package SHALL analyse raw RGBA pixel buffers and classify image complexity into one of four tiers: pixel-art, vector-clean, vector-aa, or photographic.
#### Scenario: Solid-colour image classified as pixel-art
- **WHEN** a solid-colour 32×32 buffer is passed to `analyseComplexity`
- **THEN** the returned `tier` is `"pixel-art"`, `uniqueColours` is 1, and `recommendedColours` ≤ 32
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies solid colour as pixel-art"`
#### Scenario: Gradient classified as photographic
- **WHEN** a smooth gradient 64×64 buffer is passed to `analyseComplexity`
- **THEN** the returned `tier` is `"photographic"`, `uniqueColours` > 100, and `recommendDithering` is `true`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies gradient as photographic"`
#### Scenario: Vector-clean tier detected
- **WHEN** a 32×32 image with 64 colours in 4×4 blocks is analysed
- **THEN** the returned `tier` is `"vector-clean"`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies vector-clean for moderate colours with medium pools"`
#### Scenario: Vector-aa tier detected
- **WHEN** a 64×64 image with 256 colours in 4×4 blocks is analysed
- **THEN** the returned `tier` is `"vector-aa"`
**Verified by:** `src/analysis/classify/classify.test.ts::"classifies vector-aa for high colour count with structure"`
---
### Requirement: Package provides pool detection and distribution
The package SHALL detect contiguous same-colour regions via 4-connected flood fill and compute pool size distribution statistics.
#### Scenario: Single pool for solid colour
- **WHEN** `detectPools` is called on an 8×8 solid red buffer
- **THEN** it returns exactly one pool with `size` 64 and colour `{r:255,g:0,b:0,a:255}`
**Verified by:** `src/analysis/pools/pools.test.ts::"finds 1 pool for a solid colour"`
#### Scenario: Pool distribution computed correctly
- **WHEN** `poolDistribution` is called on pools from a checkerboard
- **THEN** `singletonRatio` is 1 and `mean` is 1
**Verified by:** `src/analysis/pools/pools.test.ts::"detects high singleton ratio in checkerboard"`
---
### Requirement: Package provides edge analysis
The package SHALL analyse edges between adjacent pixels and return sharpness metrics.
#### Scenario: Solid image has zero hard edges
- **WHEN** `analyseEdges` is called on a solid-colour buffer
- **THEN** `hardEdges` is 0 and `meanEdgeDistance` is 0
**Verified by:** `src/analysis/edges/edges.test.ts::"solid image has zero hard edges"`
#### Scenario: 1×1 image has zero edges
- **WHEN** `analyseEdges` is called on a 1×1 buffer
- **THEN** `totalEdges` is 0, `sharpnessRatio` is 0, and `meanEdgeDistance` is 0
**Verified by:** `src/analysis/edges/edges.test.ts::"1x1 image has zero edges"`
---
### Requirement: Package provides colour quantisation
The package SHALL quantise RGBA pixel buffers to a target palette size using wrapped `image-q` algorithms.
#### Scenario: Gradient reduced to target colours
- **WHEN** `quantise` is called on a 32×32 gradient with `colours: 16`
- **THEN** `paletteSize` ≤ 16
**Verified by:** `src/quantise/quantise.test.ts::"reduces a gradient to target colour count"`
#### Scenario: All three algorithms supported
- **WHEN** `quantise` is called with `algorithm: "wuquant"`, `"neuquant"`, and `"rgbquant"`
- **THEN** each result has `paletteSize` ≤ 8
**Verified by:** `src/quantise/quantise.test.ts::"supports different algorithms"`
#### Scenario: Invalid dimensions throw structured error
- **WHEN** `quantise` is called with `width: 0`
- **THEN** it throws `QuantisationError` with code `QUANTISATION.INVALID_DIMENSIONS`
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid dimensions"`
---
### Requirement: Package provides spatial AA cleanup
The package SHALL merge small pools into nearest spatial neighbours below a colour distance threshold.
#### Scenario: Near-colour AA pixels merged
- **WHEN** `spatialCleanup` is called on an AA edge buffer with default options
- **THEN** the unique colour count drops from 3 to 2
**Verified by:** `src/spatial/cleanup/cleanup.test.ts::"merges near-colour AA pixels into spatial neighbours"`
#### Scenario: Pool size threshold respected
- **WHEN** `spatialCleanup` is called with `maxPoolSize: 0`
- **THEN** no merge occurs and colour count stays 3
**Verified by:** `src/spatial/cleanup/cleanup.test.ts::"does not merge pools above maxPoolSize"`
---
### Requirement: Package provides adaptive pipeline
The package SHALL orchestrate analysis → spatial cleanup → quantisation in a single call, selecting strategy based on complexity tier.
#### Scenario: Pixel-art skips quantisation
- **WHEN** `adaptivePipeline` is called on a solid-colour buffer
- **THEN** `quantisation` is `null` and `finalColours` is 1
**Verified by:** `src/pipeline/pipeline.test.ts::"skips quantisation for pixel-art"`
#### Scenario: Photographic images are quantised
- **WHEN** `adaptivePipeline` is called on a gradient
- **THEN** `quantisation` is not `null` and `finalColours` ≤ 256
**Verified by:** `src/pipeline/pipeline.test.ts::"quantises photographic images"`
#### Scenario: Spatial cleanup applied for vector-aa
- **WHEN** `adaptivePipeline` is called on a vector-aa image
- **THEN** `spatialCleanupApplied` is `true`
**Verified by:** `src/pipeline/pipeline.test.ts::"applies spatial cleanup for vector-aa images"`
---
### Requirement: Package validates options via Zod schemas
All public entry points that accept options SHALL validate them through exported Zod schemas before processing.
#### Scenario: Invalid options rejected by quantise
- **WHEN** `quantise` is called with `colours: 1`
- **THEN** it throws `QuantisationError` with code `QUANTISATION.PROCESSING_FAILED`
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid options"`
---
### Requirement: Package emits OpenTelemetry instruments
The package SHALL emit a counter and histogram for `quantise` and `adaptivePipeline` invocations.
#### Scenario: Instruments exist and are lazily initialised
- **WHEN** `quantise` is called
- **THEN** internal `getCounter()` and `getDuration()` functions return valid OTel instruments
**Verified by:** `src/quantise/quantise.test.ts::"reduces a gradient to target colour count"` (implicit via execution path)
---
### Requirement: Package defines stable error codes
The package SHALL declare error codes via `defineCodes("QUANTISATION", [...])` and throw `QuantisationError` with those codes.
#### Scenario: Every code is reachable
- **WHEN** invalid inputs are provided to `quantise` and `adaptivePipeline`
- **THEN** each of `INVALID_PIXEL_BUFFER`, `INVALID_DIMENSIONS`, and `PROCESSING_FAILED` is thrown at least once
**Verified by:** `src/quantise/quantise.test.ts::"throws on invalid dimensions"` AND `src/quantise/quantise.test.ts::"throws on mismatched buffer size"` AND `src/pipeline/pipeline.test.ts::"throws on invalid options"`
---
### Requirement: Package provides property-based tests
The package SHALL include fast-check property tests that verify invariants across random inputs.
#### Scenario: Palette size invariant
- **WHEN** random RGBA buffers and colour counts are quantised
- **THEN** `result.paletteSize` ≤ requested colours
**Verified by:** `src/property.test.ts::"quantise palette size <= requested colours"`
#### Scenario: Output dimensions invariant
- **WHEN** random RGBA buffers are quantised
- **THEN** output `width`, `height`, and `pixels.length` match input
**Verified by:** `src/property.test.ts::"quantise output has same dimensions as input"`
---
### Requirement: Package includes benchmarks
The package SHALL include mitata benchmarks for `quantise`, `analyseComplexity`, and `adaptivePipeline`.
#### Scenario: Benchmark file exists and compiles
- **WHEN** the benchmark file is inspected
- **THEN** `src/__benchmarks__/quantisation.bench.ts` exists and imports the public API
**Verified by:** manual inspection of `src/__benchmarks__/quantisation.bench.ts`