Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_graphics/tests/png/gen.sh

7.9 KiB, 1 run, executable

created by r1870400018:18098, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1#!/bin/bash
2# Regenerates the depth, colour-type and interlace fixtures in this directory.
3#
4# Nothing here is a photograph, and nothing here was written by the codec under test: a short Python
5# program writes each source image as a PPM or a PAM, ImageMagick turns that into a PNG of the
6# requested bit depth, colour type and interlace method, and ImageMagick reads its own PNG back out
7# as the PAM the test compares against. A codec that agrees only with itself has been tested against
8# nothing.
9#
10# ImageMagick will quietly ignore `png:bit-depth` and `png:color-type` when the image it holds does
11# not suit them, so the recipes below shape the image first -- quantising, greying, forcing a
12# channel depth -- and the test re-reads each fixture's IHDR to check that what came out is what the
13# fixture's name claims. A silently downgraded fixture is a hole in the matrix, not a passing test.
14#
15# The `tRNS` fixtures come out of ImageMagick's `PNG8:` writer, which is the only route it offers to
16# a palette image with transparency at a sub-byte depth, and which needs a source holding few enough
17# colours that the palette fits the depth. It will not write greyscale transparency at a sub-byte
18# depth at all, so that combination is covered by a hand-built unit test in `png.rs` instead.
19#
20# The files `grey_trns.png`, `palette_trns.png`, `rgb_plain.png`, `rgb_trns.png` and
21# `rgba_plain.png` are older, come from Pillow by way of `gen.py`, and are not touched here.
22#
23# Requires ImageMagick's `convert` and Python 3. Run from this directory.
24set -eu
25cd "$(dirname "$0")"
26tmp=$(mktemp -d)
27trap 'rm -rf "$tmp"' EXIT
28
29# The sizes: one at which every Adam7 pass is non-empty and partial, and a handful small enough that
30# most passes hold nothing at all.
31FULL=17x13
32SMALL="1x1 3x2 5x1 1x5 9x9"
33
34# ---- The sources, several per size: 8- and 16-bit, with and without alpha, and four palettes. ----
35python3 - "$tmp" $FULL $SMALL <<'PYEOF'
36import os
37import sys
38
39d = sys.argv[1]
40
41def pix(x, y):
42 """A deterministic colour and alpha, chosen to leave no two channels equal."""
43 r = (x * 47 + y * 11 + 3) % 256
44 g = (x * 13 + y * 61 + 71) % 256
45 b = (x * 29 + y * 89 + 151) % 256
46 a = (x * 37 + y * 101 + 19) % 256
47 return r, g, b, a
48
49# The colours the palette sources draw on, in the order they are taken.
50PAL = [
51 (255, 80, 20), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255), (255, 0, 255),
52 (10, 20, 30), (200, 100, 50), (60, 180, 90), (90, 30, 220), (240, 240, 10), (5, 120, 200),
53]
54
55def write(name, header, px):
56 open(os.path.join(d, name), "wb").write(header + bytes(px))
57
58for spec in sys.argv[2:]:
59 w, h = (int(v) for v in spec.split("x"))
60 p6_8 = b"P6\n%d %d\n255\n" % (w, h)
61 p6_16 = b"P6\n%d %d\n65535\n" % (w, h)
62 p7_8 = b"P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n" % (w, h)
63 p7_16 = b"P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 65535\nTUPLTYPE RGB_ALPHA\nENDHDR\n" % (w, h)
64
65 # Eight bits, no alpha.
66 px = bytearray()
67 for y in range(h):
68 for x in range(w):
69 px += bytes(pix(x, y)[:3])
70 write("rgb8_%s.ppm" % spec, p6_8, px)
71
72 # Sixteen bits, no alpha. The samples are not multiples of 257, so the low byte carries
73 # something a reduction to eight bits has to decide what to do with.
74 px = bytearray()
75 for y in range(h):
76 for x in range(w):
77 for i, c in enumerate(pix(x, y)[:3]):
78 v = (c * 257 + x * 7 + y * 13 + i * 3) % 65536
79 px += bytes((v >> 8, v & 255))
80 write("rgb16_%s.ppm" % spec, p6_16, px)
81
82 # Eight bits with alpha.
83 px = bytearray()
84 for y in range(h):
85 for x in range(w):
86 px += bytes(pix(x, y))
87 write("rgba8_%s.pam" % spec, p7_8, px)
88
89 # Sixteen bits with alpha.
90 px = bytearray()
91 for y in range(h):
92 for x in range(w):
93 for i, c in enumerate(pix(x, y)):
94 v = (c * 257 + x * 7 + y * 13 + i * 3) % 65536
95 px += bytes((v >> 8, v & 255))
96 write("rgba16_%s.pam" % spec, p7_16, px)
97
98 # Two colours far enough apart that a quantisation to two keeps both. The general source
99 # above is smooth enough that ImageMagick maps all of it onto one entry, which would make a
100 # 1-bit palette fixture a picture of nothing.
101 px = bytearray()
102 for y in range(h):
103 for x in range(w):
104 px += bytes((250, 40, 10) if (x + y) % 2 == 0 else (5, 30, 240))
105 write("duo_%s.ppm" % spec, p6_8, px)
106
107 # A few saturated colours, one pixel in five fully transparent: the shape ImageMagick's PNG8
108 # writer turns into a palette with a tRNS chunk. One source per palette size, because the
109 # palette has to fit the bit depth asked for.
110 for n in (1, 3, 6, 12):
111 px = bytearray()
112 for y in range(h):
113 for x in range(w):
114 c = PAL[(x + 2 * y) % n]
115 a = 0 if (x + 3 * y) % 5 == 0 else 255
116 px += bytes((c[0], c[1], c[2], a))
117 write("pal%d_%s.pam" % (n, spec), p7_8, px)
118PYEOF
119
120# ---- The fixtures. ----
121# $1 is the combination's name, $2 the size, $3 `n` or `i` for the interlace method, and the rest
122# the recipe, with a leading `@` on the source's name standing for the temporary directory.
123make() {
124 local name=$1 size=$2 lace=$3
125 shift 3
126 local il=none
127 [ "$lace" = i ] && il=PNG
128 local args=()
129 for a in "$@"; do
130 args+=("${a/@/$tmp/}")
131 done
132 convert "${args[0]}" +dither -strip -interlace "$il" "${args[@]:1}"
133 mv out.png "${name}_${size}_${lace}.png"
134 convert "${name}_${size}_${lace}.png" -alpha set -depth 8 "pam:${name}_${size}_${lace}.pam"
135}
136
137# One combination at one size, in both interlace methods.
138both() {
139 local name=$1 size=$2
140 shift 2
141 make "$name" "$size" n "$@"
142 make "$name" "$size" i "$@"
143}
144
145# Every legal depth and colour type, at a size where all seven Adam7 passes hold pixels.
146s=$FULL
147both g1 "$s" "@rgb8_$s.ppm" -colorspace Gray -type Bilevel png:out.png
148both g2 "$s" "@rgb8_$s.ppm" -colorspace Gray -type Grayscale -depth 2 \
149 -define png:color-type=0 -define png:bit-depth=2 png:out.png
150both g4 "$s" "@rgb8_$s.ppm" -colorspace Gray -type Grayscale -depth 4 \
151 -define png:color-type=0 -define png:bit-depth=4 png:out.png
152both g8 "$s" "@rgb8_$s.ppm" -colorspace Gray -type Grayscale -depth 8 \
153 -define png:color-type=0 -define png:bit-depth=8 png:out.png
154both g16 "$s" "@rgb16_$s.ppm" -alpha off -colorspace Gray -type Grayscale -depth 16 \
155 -define png:color-type=0 -define png:bit-depth=16 png:out.png
156both p1 "$s" "@duo_$s.ppm" -define png:bit-depth=1 PNG8:out.png
157both p2 "$s" "@rgb8_$s.ppm" -type Palette -colors 3 -depth 2 \
158 -define png:color-type=3 -define png:bit-depth=2 png:out.png
159both p4 "$s" "@rgb8_$s.ppm" -type Palette -colors 12 -depth 4 \
160 -define png:color-type=3 -define png:bit-depth=4 png:out.png
161both p8 "$s" "@rgb8_$s.ppm" -type Palette -colors 32 -depth 8 \
162 -define png:color-type=3 -define png:bit-depth=8 png:out.png
163both pt1 "$s" "@pal1_$s.pam" -define png:bit-depth=1 PNG8:out.png
164both pt2 "$s" "@pal3_$s.pam" -define png:bit-depth=2 PNG8:out.png
165both pt4 "$s" "@pal6_$s.pam" -define png:bit-depth=4 PNG8:out.png
166both pt8 "$s" "@pal12_$s.pam" -define png:bit-depth=8 PNG8:out.png
167both rgb8 "$s" "@rgb8_$s.ppm" PNG24:out.png
168both rgb16 "$s" "@rgb16_$s.ppm" -alpha off -depth 16 \
169 -define png:color-type=2 -define png:bit-depth=16 png:out.png
170both ga8 "$s" "@rgba8_$s.pam" -colorspace Gray -type GrayscaleAlpha -depth 8 \
171 -define png:color-type=4 -define png:bit-depth=8 png:out.png
172both ga16 "$s" "@rgba16_$s.pam" -colorspace Gray -type GrayscaleAlpha -depth 16 \
173 -define png:color-type=4 -define png:bit-depth=16 png:out.png
174both rgba8 "$s" "@rgba8_$s.pam" PNG32:out.png
175both rgba16 "$s" "@rgba16_$s.pam" -depth 16 \
176 -define png:color-type=6 -define png:bit-depth=16 png:out.png
177
178# The sizes at which Adam7 passes fall empty, in the narrowest and the widest pixel the format has:
179# one bit of greyscale, and four channels of eight and of sixteen bits.
180for s in $SMALL; do
181 both g1 "$s" "@rgb8_$s.ppm" -colorspace Gray -type Bilevel png:out.png
182 both rgba8 "$s" "@rgba8_$s.pam" PNG32:out.png
183 both rgba16 "$s" "@rgba16_$s.pam" -depth 16 \
184 -define png:color-type=6 -define png:bit-depth=16 png:out.png
185done
186
187ls -l