. "$BST_LIB" # gd (libgd 2.3.3, meta-oe recipes-support/gd) -- the plain-C 2D drawing library # PHP's image functions and a long tail of CGI-era tooling are built on, ported # here as a static library plus the converters it installs. # # WHAT THIS FILE ASSERTS, AND WHY IT IS SPLIT IN TWO. # # The converters gd installs all speak PNG, GIF or gd's own native format: # pngtogd, gdtopng, pngtogd2, gd2topng, gdparttopng, gd2copypal, gd2togif, # giftogd2, gdcmpgif, webpng. NONE of them reads a JPEG or a TIFF -- yet jpeg # and tiff are two of the five DEPENDS. So driving the converters, however # thoroughly, cannot say whether those two codecs were compiled in or merely # listed in the recipe. That half is carried by gd-probe (built in the recipe # against the archive that ships; see recipes-support/gd/gd_%.bbappend), and the # link itself is the assertion -- libgd guards gd_jpeg.c and gd_tiff.c with # HAVE_LIBJPEG / HAVE_LIBTIFF around the whole file, so an archive missing # either cannot produce the binary this file runs. # # THE ROUND TRIP IS COMPARED IN GD'S NATIVE FORMAT, NOT IN PNG, and that is the # point rather than a convenience. PNG -> .gd -> PNG -> .gd, with the two .gd # files compared byte for byte: the native format is an uncompressed dump of the # in-memory image, so identical bytes mean every pixel and every palette entry # survived gd's PNG writer AND its PNG reader. Comparing the re-encoded PNG # against the original file instead would assert that libpng's encoder is # deterministic -- true, already covered by 57-libpng, and not a statement about # gd at all. # # THE FIXTURES ARE BORROWED ON PURPOSE. pngcheck-rgb.png (32x24 truecolour) and # pngcheck-pal.png (16x16 palette) belong to 57-pngcheck, # libjpeg-turbo-ref-q90.jpg (64x64) to 57-libjpeg-turbo, and tiff-fixture.tif # (64x64 RGB8 baseline, built by hand from the TIFF spec) to 57-tiff. Each is # already in data/, each is documented where it lives, and the two colour types # are exactly the pair this suite needs. A second set would be a second set to # keep correct for no extra coverage; their absence is its own case below rather # than an obscure failure several cases later. require pngtogd gd/present tmp=${TMPDIR:-/tmp}/bst-gd.$$ mkdir -p "$tmp" 2>/dev/null PNG_RGB=$BST_DATA/pngcheck-rgb.png PNG_PAL=$BST_DATA/pngcheck-pal.png JPG=$BST_DATA/libjpeg-turbo-ref-q90.jpg TIF=$BST_DATA/tiff-fixture.tif PROBE=/usr/libexec/gd-tests/gd-probe # --------------------------------------------------------------------------- # 0. The fixtures, asserted before anything depends on them. check gd/fixture-png-rgb test -f "$PNG_RGB" check gd/fixture-png-pal test -f "$PNG_PAL" check gd/fixture-jpeg test -f "$JPG" check gd/fixture-tiff test -f "$TIF" # --------------------------------------------------------------------------- # 1. The converters are present. Named one per case so a package that installed # only some of them says which, rather than failing later under a name that # describes a conversion. for t in pngtogd gdtopng pngtogd2 gd2topng gdparttopng gd2copypal \ gd2togif giftogd2 gdcmpgif webpng annotate; do if command -v "$t" >/dev/null 2>&1; then bst_ok "gd/tool-$t" else bst_notok "gd/tool-$t" "not on PATH" fi done # bdftogd is REMOVED by this image's bbappend: it is a perl script and perl is # not ported to this arm. Its absence is asserted so that a future image which # gains perl -- and with it the stock recipe's RDEPENDS -- shows up here as a # deliberate decision to revisit rather than as a silent change. if command -v bdftogd >/dev/null 2>&1; then bst_notok gd/no-bdftogd "bdftogd is present; this image has no perl to run it" else bst_ok gd/no-bdftogd fi # --------------------------------------------------------------------------- # 2. THE ROUND TRIP (truecolour). PNG -> a.gd -> b.png -> c.gd, a.gd == c.gd. check gd/rgb-pngtogd pngtogd "$PNG_RGB" "$tmp/a.gd" check gd/rgb-gd-nonempty test -s "$tmp/a.gd" check gd/rgb-gdtopng gdtopng "$tmp/a.gd" "$tmp/b.png" check gd/rgb-png-nonempty test -s "$tmp/b.png" check gd/rgb-repngtogd pngtogd "$tmp/b.png" "$tmp/c.gd" check gd/rgb-roundtrip cmp -s "$tmp/a.gd" "$tmp/c.gd" # The re-encoded PNG really is a PNG, by its signature rather than by its name. check_out gd/rgb-png-magic "PNG" head -c 4 "$tmp/b.png" # 2b. THE PALETTE PATH, WHERE BYTE-IDENTITY IS THE WRONG ASSERTION AND SAYING SO # IS PART OF THE RESULT. # # The same trip over a palette PNG does NOT produce identical .gd files, and it # never converges: passes 1 and 2 differ, so do 2 and 3, and so do 3 and 4. The # header, the colour count and the transparent index all match; what moves is # the colour TABLE, from entry 2 onwards, permuted by the writer/reader pair on # every pass. Measured on an x86_64 control of this same tarball before it was # run here, so it is gd's behaviour and not this arm's -- which is why it is # pinned as a property rather than reported as a failure. # # So the invariants that DO hold are asserted here (geometry, colour count and # the transparent index survive every pass), and the claim that the IMAGE # survives -- the one a permuted palette must not be allowed to satisfy -- is # made pixel by pixel by gd-probe in section 6 below. check gd/pal-pngtogd pngtogd "$PNG_PAL" "$tmp/p-a.gd" check gd/pal-gd-nonempty test -s "$tmp/p-a.gd" check gd/pal-gdtopng gdtopng "$tmp/p-a.gd" "$tmp/p-b.png" check gd/pal-repngtogd pngtogd "$tmp/p-b.png" "$tmp/p-c.gd" # Same length every pass: a permutation of the table, not a change of shape. check_eq gd/pal-size-stable "$(wc -c < "$tmp/p-a.gd")" "$(wc -c < "$tmp/p-c.gd")" # --------------------------------------------------------------------------- # 3. webpng READS BACK WHAT GD WROTE (the ticket's second requirement). # # `webpng -d` prints exactly three lines: # # Width: 32 Height: 24 Colors: 0 # First 100% transparent index: none # Interlaced: no # # `Colors:` is gdImageColorsTotal(), which is 0 for a truecolour image and the # palette size for a palette one -- so it IS the colour type, reported by the # tool rather than inferred. It is read off b.png / p-b.png, the files gd # itself wrote, so the pair of cases says gd's PNG writer preserved both the # geometry and the colour type it was handed. det=$(webpng -d "$tmp/b.png" 2>&1) check_eq gd/webpng-rgb-dims "Width: 32 Height: 24" \ "$(printf '%s\n' "$det" | awk '/^Width:/{print $1, $2, $3, $4}')" check_eq gd/webpng-rgb-truecolor "Colors: 0" \ "$(printf '%s\n' "$det" | awk '/^Width:/{print $5, $6}')" check_eq gd/webpng-rgb-interlaced "Interlaced: no" \ "$(printf '%s\n' "$det" | awk '/^Interlaced:/{print $1, $2}')" detp=$(webpng -d "$tmp/p-b.png" 2>&1) check_eq gd/webpng-pal-dims "Width: 16 Height: 16" \ "$(printf '%s\n' "$detp" | awk '/^Width:/{print $1, $2, $3, $4}')" # The palette fixture has eight colours and one fully transparent index, and gd # preserves both across the trip -- measured on the x86_64 control over four # successive passes. Pinned rather than merely bounded: a count that moved would # mean gd re-quantised the image, which is exactly the kind of change a # "non-zero" test would wave through. check_eq gd/webpng-pal-colors "Colors: 8" \ "$(printf '%s\n' "$detp" | awk '/^Width:/{print $5, $6}')" check_eq gd/webpng-pal-transparent "First 100% transparent index: 0" \ "$(printf '%s\n' "$detp" | sed -n 's/^\(First 100% transparent index: .*\)$/\1/p')" # webpng -l reads the colour type a SECOND way, off a different branch than -d: # on a palette image it prints the table header and one row per entry, and on a # truecolour one a fixed sentence saying there is nothing to list. Two files, # two branches, so a build that got the colour type wrong cannot satisfy both. check_out gd/webpng-pal-list "Index" webpng -l "$tmp/p-b.png" check_out gd/webpng-rgb-list "Truecolor image" webpng -l "$tmp/b.png" # --------------------------------------------------------------------------- # 4. THE gd2 FORMAT AND ITS PARTIAL READER. # # pngtogd2 takes a chunk size and a format (1 = raw, 2 = compressed); both are # exercised, because the compressed path is the only one in gd that runs pixel # data through zlib. check gd/gd2-raw-write pngtogd2 "$PNG_RGB" "$tmp/raw.gd2" 64 1 check gd/gd2-comp-write pngtogd2 "$PNG_RGB" "$tmp/comp.gd2" 64 2 check gd/gd2-raw-read gd2topng "$tmp/raw.gd2" "$tmp/raw.png" check gd/gd2-comp-read gd2topng "$tmp/comp.gd2" "$tmp/comp.png" # Both formats must decode to the same image: compression is a storage choice, # not a colour one. Compared in .gd again, for the reason given at the top. check gd/gd2-raw-togd pngtogd "$tmp/raw.png" "$tmp/raw-r.gd" check gd/gd2-comp-togd pngtogd "$tmp/comp.png" "$tmp/comp-r.gd" check gd/gd2-formats-agree cmp -s "$tmp/raw-r.gd" "$tmp/comp-r.gd" # gdparttopng extracts a rectangle WITHOUT decoding the whole image -- that is # the one thing the gd2 format exists for. Its usage string says "filename.gd"; # it calls gdImageCreateFromGd2Part and needs a .gd2. Fed the right file it # works, which is what the case pins. check gd/gd2-part gdparttopng "$tmp/comp.gd2" "$tmp/part.png" 0 0 8 8 det2=$(webpng -d "$tmp/part.png" 2>&1) check_eq gd/gd2-part-dims "Width: 8 Height: 8" \ "$(printf '%s\n' "$det2" | awk '/^Width:/{print $1, $2, $3, $4}')" # --------------------------------------------------------------------------- # 5. THE GIF PATH, INCLUDING gd's OWN COMPARATOR. # # gd 2.3.3 carries its own GIF reader and writer (no giflib dependency), so # this exercises code that ships inside libgd.a rather than a dependency. check gd/gif-write gd2togif "$tmp/comp.gd2" "$tmp/x.gif" check_out gd/gif-magic "GIF8" head -c 4 "$tmp/x.gif" check gd/gif-read giftogd2 "$tmp/x.gif" "$tmp/y.gd2" 64 2 check gd/gif-rewrite gd2togif "$tmp/y.gd2" "$tmp/z.gif" # gdcmpgif prints "%: OK" when two GIFs match and an ERROR/WARNING line # when they do not; it reports through stdout rather than through its exit # status, so the case matches the text. check_out gd/gif-compare ": OK" gdcmpgif "$tmp/x.gif" "$tmp/z.gif" # gd2copypal imposes one gd2's palette on another; both operands here are # palette images derived from the same source, so it must succeed. cp "$tmp/comp.gd2" "$tmp/pal-target.gd2" 2>/dev/null check gd/gd2-copypal gd2copypal "$tmp/y.gd2" "$tmp/pal-target.gd2" # --------------------------------------------------------------------------- # 6. THE LIBRARY ITSELF, THROUGH THE PROBE. # # Everything above drives binaries. This drives /usr/lib/libgd.a: the JPEG and # TIFF decoders that have no CLI, the in-memory round trips, the drawing # primitives, the resampler's floating point, and whether FreeType is wired in. if [ ! -x "$PROBE" ]; then bst_notok gd/probe-present "$PROBE is not executable — the recipe did not ship it" else bst_ok gd/probe-present out=$("$PROBE" "$PNG_RGB" "$PNG_PAL" "$JPG" "$TIF" 2>&1); rc=$? # The probe's own count of internal contract violations, and its exit # status, which must agree. A probe that printed nothing still lands as a # red here rather than contributing no cases at all. check_eq gd/probe-rc 0 "$rc" check_eq gd/probe-fails "GD-PROBE-FAILS 0" \ "$(printf '%s\n' "$out" | grep '^GD-PROBE-FAILS' | head -1)" # The three decoders. Geometry is the fixture's, known independently of gd. check_eq gd/probe-png-read "PNG-READ 32 24 1" "$(printf '%s\n' "$out" | grep '^PNG-READ' | head -1)" check_eq gd/probe-jpeg-read "JPEG-READ 64 64 1" "$(printf '%s\n' "$out" | grep '^JPEG-READ' | head -1)" check_eq gd/probe-tiff-read "TIFF-READ 64 64 1" "$(printf '%s\n' "$out" | grep '^TIFF-READ' | head -1)" # The palette fixture must come back as a PALETTE image (trailing 0), not # silently promoted to truecolour -- that is the distinction section 2b # rests on. check_eq gd/probe-pal-read "PAL-READ 16 16 0" "$(printf '%s\n' "$out" | grep '^PAL-READ' | head -1)" # In-memory native round trip: the two serialisations must be identical. check_eq gd/probe-gd-roundtrip "same" \ "$(printf '%s\n' "$out" | awk '/^GD-ROUNDTRIP/{print $2}')" # THE PALETTE ROUND TRIP, JUDGED ON PIXELS. Every one of the 256 pixels must # resolve to the same RGBA after the trip; the count of disagreeing pixels # is reported so a partial corruption says how partial. This is the claim # section 2b deliberately does not make with cmp, and a permuted-but- # equivalent colour table passes it exactly as it should. check_eq gd/probe-pal-roundtrip "PAL-ROUNDTRIP same 256 0" \ "$(printf '%s\n' "$out" | grep '^PAL-ROUNDTRIP' | head -1)" check_eq gd/probe-pal-colors "PAL-COLORS 8 8" \ "$(printf '%s\n' "$out" | grep '^PAL-COLORS' | head -1)" # The same trip through the PNG codec, in memory rather than through files. check_eq gd/probe-png-mem "PNG-MEM 32 24" \ "$(printf '%s\n' "$out" | awk '/^PNG-MEM /{print $1, $2, $3}')" # The pixel at (1,1) must survive that trip unchanged. Both values are # extracted from the transcript, so an absent line would make the two agree # on the empty string; the guard is there because a vacuous pass is worse # than a red -- it reports coverage that never ran. px_file=$(printf '%s\n' "$out" | awk '/^PNG-PIXEL/{print $4}') px_mem=$(printf '%s\n' "$out" | awk '/^PNG-MEM-PIXEL/{print $4}') if [ -z "$px_file" ] || [ -z "$px_mem" ]; then bst_notok gd/probe-png-mem-pixel "probe printed no pixel line (file='$px_file' mem='$px_mem')" else check_eq gd/probe-png-mem-pixel "$px_file" "$px_mem" fi # Drawing: fixed geometry, fixed colours, exact answers. check_eq gd/probe-draw "DRAW 16 16 1" "$(printf '%s\n' "$out" | grep '^DRAW ' | head -1)" check_eq gd/probe-draw-diag "DRAW-DIAG 7 7 ff0000" "$(printf '%s\n' "$out" | grep '^DRAW-DIAG' | head -1)" check_eq gd/probe-draw-off "DRAW-OFF 15 0 000000" "$(printf '%s\n' "$out" | grep '^DRAW-OFF' | head -1)" check_eq gd/probe-draw-set "DRAW-SET 4 12 ff0000" "$(printf '%s\n' "$out" | grep '^DRAW-SET' | head -1)" # The resampler ran and produced the requested geometry. Its four diagonal # samples are reported by the probe and read in the transcript rather than # pinned here: bilinear averages are the arithmetic under test, and pinning # them without an x86-64 reference measured from this same tarball would be # pinning this arm's answer to itself. check_eq gd/probe-resample "RESAMPLE 4 4" "$(printf '%s\n' "$out" | grep '^RESAMPLE ' | head -1)" check_eq gd/probe-resample-samples 4 \ "$(printf '%s\n' "$out" | grep -c '^RESAMPLE-PIXEL')" # FreeType. A libgd built without it answers every gdImageStringFT call with # the fixed string below; one built WITH it gets as far as asking FreeType # to open the (deliberately absent) font and returns FreeType's own error. # So the assertion is that the reply is NOT the not-compiled-in message -- # which is testable on an image that ships no TTF, unlike a positive render. ft=$(printf '%s\n' "$out" | grep '^FREETYPE' | head -1) case "$ft" in *"not built with FreeType"*) bst_notok gd/probe-freetype "libgd was built without FreeType: $ft" ;; FREETYPE*) bst_ok gd/probe-freetype ;; *) bst_notok gd/probe-freetype "no FREETYPE line in probe output" ;; esac # The probe's version string, so a silent SRCREV bump is visible in the # transcript next to the results it produced. check_out gd/probe-version "GD-VERSION 2." printf '%s\n' "$out" fi rm -rf "$tmp"