oxedyne/fe2o3/fe2o3_graphics/tests/jpeg/gen.sh
3.9 KiB, 5 runs, executable
created by r1870400018:17597, 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 JPEG fixtures in this directory. |
| 3 | # |
| 4 | # Nothing here is a photograph: every source image is generated by this script, written out as a |
| 5 | # PPM, and handed to ImageMagick to compress. The JPEGs are therefore ImageMagick's work and not |
| 6 | # ours, which is the point -- a codec that agrees only with itself has been tested against nothing. |
| 7 | # |
| 8 | # Requires ImageMagick's `convert`. Run from this directory. |
| 9 | set -eu |
| 10 | cd "$(dirname "$0")" |
| 11 | tmp=$(mktemp -d) |
| 12 | trap 'rm -rf "$tmp"' EXIT |
| 13 | |
| 14 | # ---- The sources, each written as a binary PPM by a short Python program. ---- |
| 15 | python3 - "$tmp" <<'PYEOF' |
| 16 | import sys, os, math |
| 17 | d = sys.argv[1] |
| 18 | |
| 19 | def ppm(name, w, h, fn): |
| 20 | px = bytearray() |
| 21 | for y in range(h): |
| 22 | for x in range(w): |
| 23 | r, g, b = fn(x, y) |
| 24 | px += bytes((r & 255, g & 255, b & 255)) |
| 25 | with open(os.path.join(d, name + ".ppm"), "wb") as f: |
| 26 | f.write(b"P6\n%d %d\n255\n" % (w, h)) |
| 27 | f.write(px) |
| 28 | |
| 29 | # A smooth two-axis gradient: the easy case, where every block is nearly flat. |
| 30 | ppm("gradient", 64, 48, lambda x, y: (x * 4, y * 5, (x + y) * 2)) |
| 31 | |
| 32 | # Deterministic noise: the hard case, where every coefficient carries something. |
| 33 | def noise(x, y): |
| 34 | s = (x * 1103515245 + y * 12345 + 7) & 0x7FFFFFFF |
| 35 | s = (s ^ (s >> 13)) * 2654435761 & 0x7FFFFFFF |
| 36 | return (s & 255, (s >> 8) & 255, (s >> 16) & 255) |
| 37 | ppm("noise", 33, 17, noise) |
| 38 | |
| 39 | # Saturated primaries in hard-edged blocks: the case chroma subsampling is worst at. |
| 40 | def prim(x, y): |
| 41 | c = [(255,0,0), (0,255,0), (0,0,255), (255,255,0), |
| 42 | (0,255,255), (255,0,255), (255,255,255), (0,0,0)] |
| 43 | return c[((x // 8) + (y // 8) * 3) % 8] |
| 44 | ppm("primaries", 48, 32, prim) |
| 45 | |
| 46 | # A single pixel, where every edge case in an upsampler meets at once. |
| 47 | ppm("tiny", 1, 1, lambda x, y: (200, 40, 90)) |
| 48 | |
| 49 | # Odd dimensions, so that neither axis is a multiple of a block or an MCU. |
| 50 | ppm("odd", 17, 13, lambda x, y: ( |
| 51 | int(127 + 120 * math.sin(x * 0.7)), |
| 52 | int(127 + 120 * math.sin(y * 0.9)), |
| 53 | int(127 + 120 * math.sin((x + y) * 0.5)), |
| 54 | )) |
| 55 | |
| 56 | # Four pixels across: the chrominance plane is then two samples wide, which is where the triangle |
| 57 | # filter has no interior to work over and libjpeg falls back to replicating. |
| 58 | ppm("narrow", 4, 6, lambda x, y: (255 if x < 2 else 0, 0 if x < 2 else 255, (x * 60 + y * 20) % 256)) |
| 59 | |
| 60 | # A grey ramp, for the single-component path. |
| 61 | ppm("ramp", 40, 24, lambda x, y: ((x * 6) % 256,) * 3) |
| 62 | PYEOF |
| 63 | |
| 64 | # ---- The JPEGs. ---- |
| 65 | c() { convert "$tmp/$1.ppm" "${@:3}" "$2.jpg"; } |
| 66 | |
| 67 | c gradient gradient_q90_444 -quality 90 -sampling-factor 1x1 |
| 68 | c gradient gradient_q75_420 -quality 75 -sampling-factor 2x2 |
| 69 | c gradient gradient_q50_422 -quality 50 -sampling-factor 2x1 |
| 70 | c gradient gradient_q80_prog -quality 80 -sampling-factor 2x2 -interlace JPEG |
| 71 | c noise noise_q95_444 -quality 95 -sampling-factor 1x1 |
| 72 | c noise noise_q60_420 -quality 60 -sampling-factor 2x2 |
| 73 | c noise noise_q85_prog -quality 85 -sampling-factor 1x1 -interlace JPEG |
| 74 | c primaries primaries_q92_444 -quality 92 -sampling-factor 1x1 |
| 75 | c primaries primaries_q70_420 -quality 70 -sampling-factor 2x2 |
| 76 | c primaries primaries_q70_422 -quality 70 -sampling-factor 2x1 |
| 77 | c tiny tiny_q90_420 -quality 90 -sampling-factor 2x2 |
| 78 | c odd odd_q88_444 -quality 88 -sampling-factor 1x1 |
| 79 | c odd odd_q64_420 -quality 64 -sampling-factor 2x2 |
| 80 | c odd odd_q88_prog -quality 88 -sampling-factor 2x2 -interlace JPEG |
| 81 | c gradient gradient_q80_1x2 -quality 80 -sampling-factor 1x2 |
| 82 | c narrow narrow_q90_420 -quality 90 -sampling-factor 2x2 |
| 83 | c narrow narrow_q90_422 -quality 90 -sampling-factor 2x1 |
| 84 | c ramp ramp_q90_grey -quality 90 -colorspace Gray -type Grayscale |
| 85 | c ramp ramp_q90_prog -quality 90 -colorspace Gray -type Grayscale -interlace JPEG |
| 86 | |
| 87 | # Restart markers, which nothing above exercises. |
| 88 | convert "$tmp/noise.ppm" -quality 80 -sampling-factor 2x2 -define jpeg:restart-interval=2 \ |
| 89 | noise_q80_rst.jpg |
| 90 | |
| 91 | # The reference decodings, as ImageMagick reads its own files back. |
| 92 | for f in *.jpg; do |
| 93 | convert "$f" "${f%.jpg}.ppm" |
| 94 | done |
| 95 | |
| 96 | ls -l |