Skip to content

Commit 743d9a2

Browse files
committed
add support for grayscale palette generation when palette footer is absent
1 parent 11e383a commit 743d9a2

3 files changed

Lines changed: 41 additions & 10 deletions

File tree

AmigaRawImageConverter/Options.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ internal class Options
1818
[Option('p', "raw-file-pattern", Default = "*.raw", HelpText = "The filename pattern to match for RAW files.")]
1919
public string RawFilePattern { get; set; } = "*.raw";
2020

21+
[Option("require-palette", Default = true,
22+
HelpText = "Require palette footer; if false, use grayscale palette and treat all bytes as planar data.")]
23+
public bool RequirePalette { get; set; }
24+
2125
[Option("min-planes", Default = 3, HelpText = "Minimum bitplane count to evaluate when guessing geometry.")]
2226
public int MinPlanes { get; set; }
2327

AmigaRawImageConverter/Program.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,28 @@ private static int Run(Options opts)
6868
private static void ConvertOne(string inputPath, string outputPath, Options opts)
6969
{
7070
var raw = File.ReadAllBytes(inputPath);
71-
if (raw.Length < PaletteLength)
71+
ReadOnlySpan<byte> planar;
72+
Rgba32[] palette;
73+
74+
if (opts.RequirePalette)
7275
{
73-
throw new InvalidOperationException("File too small to contain palette.");
74-
}
76+
if (raw.Length < PaletteLength)
77+
{
78+
throw new InvalidOperationException("File too small to contain palette.");
79+
}
7580

76-
var paletteTail = raw.AsSpan(raw.Length - PaletteLength, PaletteLength);
77-
var palette = DecodePalette(paletteTail);
81+
var paletteTail = raw.AsSpan(raw.Length - PaletteLength, PaletteLength);
82+
palette = DecodePalette(paletteTail);
83+
planar = raw.AsSpan(0, raw.Length - PaletteLength);
84+
}
85+
else
86+
{
87+
planar = raw;
88+
var maxPlanes = Math.Max(opts.MinPlanes, opts.MaxPlanes);
89+
var paletteSize = 1 << Math.Max(1, maxPlanes);
90+
palette = BuildGrayscalePalette(paletteSize);
91+
}
7892

79-
var planar = raw.AsSpan(0, raw.Length - PaletteLength);
8093
var paletteColors = palette.Length;
8194
var candidates = GuessCandidates(planar, paletteColors, opts)
8295
.OrderBy(c => c.score)
@@ -225,6 +238,19 @@ private static Rgba32[] DecodePalette(ReadOnlySpan<byte> tail)
225238
return colors;
226239
}
227240

241+
private static Rgba32[] BuildGrayscalePalette(int size)
242+
{
243+
var palette = new Rgba32[size];
244+
var max = Math.Max(1, size - 1);
245+
for (var i = 0; i < size; i++)
246+
{
247+
var v = (byte)(i * 255 / max);
248+
palette[i] = new Rgba32(v, v, v, 255);
249+
}
250+
251+
return palette;
252+
}
253+
228254
private static byte[,] DecodePlanar(ReadOnlySpan<byte> planar, (int Width, int Height) geom, int planes)
229255
{
230256
var width = geom.Width;

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The tool auto-detects likely geometries and emits the top N PNGs per input.
99
- Uses the embedded palette (last 32 bytes) decoded as Amiga RGB4.
1010
- Detects geometry by measuring row smoothness/banding; sorts best-first.
1111
- Outputs multiple candidates with descriptive suffixes (`_candNN_<WxH>.png`).
12-
- Works on single files or whole directories; configurable max candidates, bitplane range, and geometry search ranges.
12+
- Works on single files or whole directories; configurable max candidates, bitplane range, palette requirement, and geometry search ranges.
1313
- Cross-platform (.NET 8 + ImageSharp)
1414

1515
## Requirements
@@ -51,6 +51,7 @@ Candidate PNGs are written under the chosen output directory with names based on
5151
## CLI options
5252
- `-n`, `--max-candidates` (default: 5): How many best geometry candidates to emit per file.
5353
- `-p`, `--raw-file-pattern` (default: `*.raw`): Filename pattern to match for RAW files when input is a directory.
54+
- `--require-palette` (default: true): If true, expect a palette footer; if false, use all bytes as planar data and generate a grayscale palette.
5455
- `--min-planes` (default: 3): Minimum bitplane count to evaluate when guessing geometry.
5556
- `--max-planes` (default: 6): Maximum bitplane count to evaluate when guessing geometry.
5657
- `--min-width` (default: 64): Minimum image width (pixels) to evaluate when guessing geometry.
@@ -60,7 +61,7 @@ Candidate PNGs are written under the chosen output directory with names based on
6061
- `--width-increment` (default: 16): Step (pixels) between tested widths when scanning candidates.
6162

6263
## Notes on geometry detection
63-
- Defaults to scanning 3–6 bitplanes (common Amiga modes); plane counts that need more palette entries than are present are skipped.
64+
- Defaults to scanning 3–6 bitplanes (common Amiga modes); plane counts that need more palette entries than are present are skipped. With `--require-palette false`, a grayscale palette sized to the maximum plane count is generated.
6465
- By default scans widths from 64 to 640 pixels in steps of 16 (`--min-width`, `--max-width`, `--width-increment`).
6566
- Width must be divisible by 8 and exactly consume the planar data (given height and plane count) or the candidate is skipped.
6667
- Scores candidates by row smoothness (mean + stddev of row deltas); lower is better.
@@ -71,7 +72,7 @@ The last 32 bytes are 16 big-endian words, layout `0RRR GGGG BBBB` (4 bits/chann
7172
each channel is scaled to 0–255 for PNG output.
7273

7374
## Limitations & gotchas
74-
- Expects the palette to be the final 32 bytes; extra non-planar data at the end will confuse detection.
75-
- Palette contains 16 entries (Amiga RGB4); bitplane counts requiring more colors are ignored.
75+
- When `--require-palette` is true, expects the palette to be the final 32 bytes; extra non-planar data at the end will confuse detection.
76+
- With autogenerated grayscale palette, bitplane counts requiring more colors than the generated palette size (based on max planes) are ignored.
7677
- Width/height search ranges are controlled via CLI options; exotic sizes outside the search window will not be considered.
7778
- Smoothness scoring can still pick a wrong candidate for very noisy or synthetic images—inspect the outputs.

0 commit comments

Comments
 (0)