Skip to content

Releases: YadeWira/packPNG

packPNG 2.0 LTS

13 Jun 20:32

Choose a tag to compare

First long-term-support release. Lossless, byte-exact recompressor for the
PNG family — PNG / APNG / JNG / MNG. The default backend undoes the deflate exactly
(preflate) and re-stores the image with WebP-lossless, reconstructing the
byte-identical original file (same SHA-256).

Frozen format

From 2.0 onward the wire format is frozen — every 2.0x point release decodes
any other 2.0x's output. Magics: TCIP (default, preflate + WebP-lossless),
TVCP (-fast, kanzi+zstd), TMCP (-preflate-max, kanzi-TPAQX),
TPCL (-tpcl, preflate + LZMA2), TCIJ (JNG), TCIM (MNG).

Highlights since v1.9

  • TPCL (-tpcl): preflate + multi-threaded LZMA2 — precomp's recipe, but beats
    precomp on ratio and batch speed (newer preflate).
  • MNG Level B: parallel per-segment recompression (large MNGs ~4× faster encode).
  • libpackPNG: static library + C API (packpng.h) — make lib / make lib-win.
  • Suite-wide never-expands guarantee; clean help screen + -h/--help.

vs precomp (17 real PNGs, byte-exact both sides)

ratio encode decode
packPNG TCIP (default) 45.7 % 2.4 s 0.7 s
packPNG TPCL (-tpcl) 64.3 % 1.7 s 0.6 s
precomp 0.4.8 75.3 % 2.2 s 1.0 s

packPNG's default is ~39 % smaller than precomp — it models the image
(WebP-lossless) instead of LZMA2-ing the deflate stream. Full tables, backends and
the library API are in the wiki.

Downloads

  • Linux x86-64 (static, dependency-free): packPNG-linux-x86_64-static-v2.0
  • Windows x86-64 (mingw, OS DLLs only): packPNG-windows-x86_64-full-v2.0.exe
4d87c73f6fbef93b14a97cae511cc222d229d72f4b43d2b9403120f666b5be90  packPNG-linux-x86_64-static-v2.0
a486239e76c1d01581739ff1289fe87f93289afca29c740bf60a9cc21cee19b4  packPNG-windows-x86_64-full-v2.0.exe

The Windows .exe is unsigned; some AVs may flag a generic heuristic (false
positive). Verify the SHA-256 or build from source.### Library (libpackPNG) — SDK archives

Static library + C API for embedding the codec, shipped as per-platform SDK
bundles (library + packpng.h + README with the link line). make lib /
make lib-win reproduce them. Static only for now (a shared .so/.dll needs PIC
rebuilds of the vendored deps). API + example: wiki → Library.

  • packPNG-2.0-linux-x64-lib.tar.gz — Linux x86-64 (AVX2), libpackpng.a + header
  • packPNG-2.0-win64-lib.zip — Windows x86-64 (mingw-w64), libpackpng-win.a + header
72d2cf54b810c71238d00d9fbd805a4bbbc8efe0f32ec6f11efdbe4737682a36  packPNG-2.0-linux-x64-lib.tar.gz
55d02cb1f0040d24e09b136ec8d609b2e05a578e26b82e2002fc61e8b1c8c71e  packPNG-2.0-win64-lib.zip

packPNG 2.0a — libpackPNG shared libs + in-memory API

13 Jun 20:59

Choose a tag to compare

packPNG 2.0a

LTS point release. Wire format unchanged — 2.0a decodes any 2.0x and vice-versa. This release is about the library (libpackPNG).

Motivated by an archiver-embedding request: a DLL with buffer functions (like packJPG's pjglib) and multithreading on by default.

What's new

  • Shared libraries: libpackpng.so (Linux) and packpng.dll + import lib (libpackpng.dll.a) / packpng.def (Windows), alongside the static .a.
  • In-memory API for archivers — buffer in → buffer out, no temp files needed at the call site:
    • packpng_compress_mem / packpng_decompress_mem / packpng_free
    • packpng_set_threads(n) (0 = auto)
  • Multithreading ON by default in the library (auto = hardware threads).
  • Verified byte-exact on Linux and Windows (file + memory API, static + DLL).

Assets

  • packPNG-linux-x86_64-static-v2.0a — Linux CLI (static)
  • packPNG-windows-x86_64-full-v2.0a.exe — Windows CLI
  • packPNG-2.0a-linux-x64-lib.tar.gz — Linux SDK (static + shared + header)
  • packPNG-2.0a-win64-lib.zip — Windows SDK (static + DLL + import lib + .def + header)

Library docs: https://github.com/YadeWira/packPNG/wiki/Library

SHA-256

02354ab776343ac8ff048f7053d3cadc08ebf312a37b0183332be6b23fe878b9  packPNG-linux-x86_64-static-v2.0a
d9414456f344f23bce34688f395100c62ab45a084948f353db29a6504f92b20c  packPNG-windows-x86_64-full-v2.0a.exe
e7fe44bde9e758d8cc7c25320167079c06c64a331a992d3714908645bce1d997  packPNG-2.0a-linux-x64-lib.tar.gz
1fe718f06358978d866f5299820f3bbffd6ab109b36cbbceaa5831a06443b28e  packPNG-2.0a-win64-lib.zip

packPNG v1.9

13 Jun 05:06

Choose a tag to compare

Lossless PNG / APNG / JNG / MNG recompressor. Byte-exact, reversible to the original file (verified via SHA-256).

Highlights

  • New default backend (tovyCIP): preflate undoes the deflate byte-exact, then WebP-lossless stores the image → ≈ −54% on real PNGs with fast decode (~40–90 ms/file).
  • -fast keeps the old kanzi+zstd backend for max speed; -preflate-max adds kanzi-TPAQX for extreme ratio.
  • MNG support (Level A): whole-file preflate container with a store-raw fallback so output never bloats. The Network Graphics family is now complete: PNG / APNG / JNG / MNG.
  • Self-contained build: all deps vendored; make builds the 100%-autonomous Linux static + Windows full binaries.

v2 magic naming

Every output magic spells out its backend (this drops pre-2.0 backward compat for the solid backends; 2.0 will freeze the format):

Magic Expansion Mode
TCIP Tovy Compresor de Imágenes PNG default (preflate + WebP-lossless)
TVCP Tovy Veloz Compresor PNG -fast (kanzi + zstd)
TMCP Tovy Máximo Compresor PNG -preflate-max (kanzi-TPAQX)
TCIJ Tovy Compresor de Imágenes JNG JNG inputs
TCIM Tovy Compresor de Imágenes MNG MNG inputs

Note the swap: TCIP now means the preflate default (it was the kanzi backend in v1.x, now TVCP).

Downloads

  • Linux x86-64 (static, dependency-free): packPNG-linux-x86_64-static-v1.9
  • Windows x86-64 (mingw, OS DLLs only): packPNG-windows-x86_64-full-v1.9.exe

SHA-256

6418f485943297a1397040a8d11563e4f2a69a14654b9e353c11db11bd789567  packPNG-linux-x86_64-static-v1.9
d1f2f8b94b082391cf915f1ba398b419bf4c564a9b207cefb8533939d962daf7  packPNG-windows-x86_64-full-v1.9.exe

The Windows .exe is unsigned; some AVs may flag it as a generic heuristic (false positive). Verify the SHA-256 or build from source.

packPNG v1.8 — JNG container support

05 May 13:31

Choose a tag to compare

v1.8 — JNG (JPEG Network Graphics) container support

First release with JNG input support, plus a small build-system fix for
the kanzi Makefile target.

What's new

  • JNG inputs are recognised and processed. A .jng file is parsed into
    three sections (head / image / tail) and emitted as a .ppg with a new
    internal magic, TCIJ. Round-trip is byte-exact on the full sembiance JNG
    corpus (8/8 samples covering grayscale, color, deflate-alpha IDAT,
    JPEG-alpha JDAA, and progressive JPEG bitstreams).

  • Output stays .ppg. No new file extension was introduced. Inside the
    file, the 4-byte magic distinguishes content type:

    • TCIP — PNG/APNG (unchanged from v1.7e)
    • TCIJ — JNG (new in v1.8)
    • PPG1 / PPGS — legacy formats, still decode
  • Wire format (TCIJ v1): 4 B magic + 1 B version + 1 B flags + 2 B
    filename length + UTF-8 filename + three (raw_size, comp_size, bytes)
    sections. The image section uses zstd-19 --long=27; head and tail
    sections use plain zstd. Original chunk length / type / data / CRC bytes
    survive untouched, so byte-exact roundtrip is guaranteed regardless of how
    the source JNG was encoded.

Phase 1 vs Phase 2

This release ships the container parser + wrapper format only. The JDAT
chunks (the JPEG bitstream — typically the bulk of a JNG file) are stored
through zstd-19, which can't beat JPEG entropy. Compression on a mixed
corpus is therefore modest (≈ 90 % of original on a 70 KB sembiance batch);
small JNGs (< 2 KB) can grow slightly because the wrapper overhead exceeds
zstd savings on the head / tail.

Phase 2 (planned) will route JDAT through packJPG (already vendored in
the syc archiver), unlocking a ~25 % additional win on the JPEG portion. The
TCIJ format reserves a flags byte for that future content-type bit.

CLI

packPNG a image.jng              # → image.ppg     (TCIJ wrapper)
packPNG a -ver -dry image.jng    # round-trip verify in memory, no write
packPNG a -r -od out/ src/       # PNG/APNG/JNG inputs all dispatch correctly
packPNG x image.ppg              # decoder picks TCIP or TCIJ by magic

Build-system fix

The kanzi Makefile target now passes -DUSE_ZSTD and links -lzstd. This
is required for both the existing tovyCIP IDAT-passthrough path and the
new TCIJ wrapper. If you build manually, the recommended flag set is now:

-DUSE_KANZI -DUSE_ZSTD [-DUSE_LIBDEFLATE]
-lz -llzma -lzstd [-ldeflate] libkanzi.a -lpthread

Linux/Debian dependency: apt install libzstd-dev (already needed since
v1.6 in practice; the Makefile was just missing the flag).

Compatibility

  • PNG/APNG path unchanged from v1.7e. v1.7e .ppg files decode under
    v1.8 and vice versa.
  • v1.7e binaries fail cleanly with bad TCIP magic on a .ppg produced
    from a JNG input (by design — old binaries can't reconstruct a JNG, so
    refusing the file is safer than silently misparsing).
  • Legacy .tcip / .ppgs archives from v1.4–v1.6 still decode unchanged.

Bug fixes / housekeeping

  • Help text and banner subtitle updated to mention JNG (PNG/APNG/JNG …).
  • collect() recursion picks up .jng files in -r mode.
  • Legacy backends (-perfile / -m / -zstd / -fl2 / -kanzi / -kpng)
    reject JNG inputs cleanly with a one-line message instead of silently
    misrouting them — Phase 1 JNG support is wired exclusively through the
    default tovyCIP backend.
  • Manifest assemblyIdentity@version bumped to 1.8.0.0.

Binaries

  • packPNG-linux-x86_64-v1.8 — Linux x86-64, dynamically linked
    (zlib, liblzma, libzstd, libdeflate, libkanzi, pthread).
  • packPNG-windows-x86_64-full-v1.8.exe — Windows x86-64, statically
    linked, mingw-w64 cross-compiled. Includes the UTF-8 active-code-page
    manifest so non-ASCII filenames work without a chcp 65001 dance.

🤖 Generated with Claude Code

packPNG v1.8c - smart console-detection prompt

05 May 17:40

Choose a tag to compare

v1.8c - smart console detection: only prompt on a fresh console

Third bugfix on the v1.8 line. v1.8a fixed the double-click console-close
problem by always printing "Press to quit" on every help / error
exit. Real-world feedback: that was correct for double-click but noisy
in every other context
-- running from cmd.exe, PowerShell, Windows
Terminal, SSH, or a .bat would also show the prompt, even though the
console is shared with a parent shell that survives our exit.

The fix

Detect whether the console is fresh (only packPNG attached, will
close on our exit) before showing the prompt. On Windows, this is
GetConsoleProcessList(buf, 2) returning 1. On Linux, terminal
emulators outlive us in every realistic case, so it always returns
false -- the prompt was always noise on Linux and v1.8c retires it
there too.

Invocation console_is_fresh prompt?
Windows: double-click .exe directly yes (1 proc) yes
Windows: from cmd.exe / PowerShell / Windows Terminal no (2+ procs) no
Windows: from a .bat file no (cmd is parent) no
Windows: from SSH no (sshd shell is ancestor) no
Linux: any context no (always) no

-np and -module continue to bypass the prompt unconditionally for
scripted / machine invocations, exactly as before.

Verified

On Windows 11 (DESKTOP-6R6L470, build 26200, PowerShell 5.1.26100) via
SSH:

Test 1  no-args from cmd        no prompt    PASS
Test 2  -mZ error from cmd      no prompt    PASS
Test 3  -np regression          no prompt    PASS

The fresh-console branch is verified manually by double-clicking the
.exe in Explorer and confirming "Press <enter> to quit" still
appears -- this is the path the bat selftest cannot reach (invoking a
.bat already establishes cmd.exe as parent, defeating the fresh
detection).

What didn't change

  • Wire format unchanged: .ppg files written by v1.8 / v1.8a /
    v1.8b decode under v1.8c and vice versa. Both TCIP (PNG/APNG) and
    TCIJ (JNG) magics are intact.
  • Compression algorithm unchanged: the tovyCIP backend
    (kanzi RLT+BWT+SRT+ZRLT/FPAQ + zstd-19 --long=27) is byte-exact
    identical to every prior release on the v1.8 line.
  • All v1.8a/b behaviours preserved apart from the prompt narrowing:
    the wait_and_return helper, exit-code handling for invalid magic
    (v1.8b), JNG support (v1.8), are all untouched.
  • Manifest assemblyIdentity@version bumped 1.8.0.2 -> 1.8.0.3.

Binaries

  • packPNG-linux-x86_64-v1.8c - Linux x86-64.
  • packPNG-windows-x86_64-full-v1.8c.exe - Windows x86-64, statically
    linked, mingw-w64 cross-compiled, UTF-8 active-code-page manifest.
  • test-v1.8c.bat - drop-next-to-the-exe selftest harness, 14 tests
    (regression set + flipped prompt expectations from v1.8a/b).

🤖 Generated with Claude Code

packPNG v1.8b - audit-driven bugfix: exit code on invalid magic

05 May 15:20

Choose a tag to compare

v1.8b - bugfix: exit non-zero when input has invalid magic (audit-driven)

Second bugfix on the v1.8 line, found by an 8-phase Windows 11 audit
harness run against the v1.8a binary on a real Windows 11 box
(DESKTOP-6R6L470, build 26200, PowerShell 5.1.26100). v1.8a passed 7
of 8 phases cleanly - the holdout was Phase 4 (malformed inputs).

The bug

Files with invalid magic - a 0-byte file named empty.png, or random
bytes named garbage.png - were silently skipped with a warning
but the program exited 0, lying to any script checking exit status.

packPNG a empty.png    -> "skipped 1 file(s) with invalid magic"  exit 0  (wrong)
packPNG a garbage.png  -> "skipped 1 file(s) with invalid magic"  exit 0  (wrong)
packPNG a missing.png  -> "ERROR missing file: missing.png"       exit 1  (correct)

Cause

The legacy filter near the end of main() accumulates unprocessable
inputs into three buckets:

  • skip_missing (file does not exist)
  • skip_bad_magic (file exists but is not a PNG / .ppg / .jng)
  • skip_compress / skip_decompress (wrong direction for the chosen
    subcommand)

skip_missing has always bumped g_errors per file - the program's
final return errs ? 1 : 0 then properly exits non-zero. skip_bad_magic
was missing the same line
, so when every input had bad magic the
program exited 0 even though nothing was processed.

The fix

One line. After the verbose-skip loop for skip_bad_magic, mirror the
existing skip_missing bookkeeping:

g_errors += (int)skip_bad_magic.size();

Mixed batches (some valid + some bad-magic) now also exit non-zero,
matching how Unix cp / tar / gzip behave when any source fails.
This is stricter than v1.8a behaviour. If you have a script that
intentionally feeds packPNG a directory containing non-PNG files
and was relying on exit 0, switch to per-file invocation or pre-filter
the input list.

Audit results (Windows 11, v1.8b)

Phase What it covers v1.8a v1.8b
1. PngSuite full corpus roundtrip 162 valid files (color_type x bit_depth x interlace) 162/162 162/162
2. Path edge cases space, paren, &, [], ;, %, #, +, ' 9/10 (audit-script bug) 10/10
3. Mode coverage mode 0 default + mode 2 (-ldf) 2/2 2/2
4. Malformed inputs 0-byte / garbage / truncated / sig-only / corrupt CRC 3/5 5/5
5. -r recurse + -fs 3-deep dir tree, structure preserve 2/2 2/2
6. Output collisions + -od dup basename rename, auto-mkdir 2/2 2/2
7. Big-file stress 3.42 MB random RGB pack+verify PASS (0.25s) PASS (0.25s)
8. Multi-thread determinism -th1 == -th4 == -th0 SHA256 PASS PASS

The Phase 2 long-path test (>260 char total) is consistently SKIPPED
because the host filesystem rejects creating such a path before
packPNG ever runs - that is a system / LongPathsEnabled group-policy
setting, not a packPNG bug. The manifest already declares
longPathAware=true.

What didn't change

  • Wire format unchanged: .ppg files written by v1.8 / v1.8a decode
    under v1.8b and vice versa. Both TCIP (PNG/APNG) and TCIJ (JNG)
    magics are intact.
  • All v1.8a behaviours preserved - the wait_and_return helper for
    the help/error console-close fix (encode.su feedback) still works.
  • Manifest assemblyIdentity@version bumped 1.8.0.1 -> 1.8.0.2.

Acknowledgements

Thanks to the encode.su community for the v1.8a console paper-cut
report and the YCoCg-R / STRATA pointer that prompted the v1.9
prototype work earlier in this cycle (the latter didn't pan out in
this pipeline, but useful negative evidence).

Binaries

  • packPNG-linux-x86_64-v1.8b - Linux x86-64, dynamically linked.
  • packPNG-windows-x86_64-full-v1.8b.exe - Windows x86-64, statically
    linked, mingw-w64 cross-compiled, UTF-8 active-code-page manifest.

🤖 Generated with Claude Code

packPNG v1.8a — keep console open on help/error exits

05 May 14:52

Choose a tag to compare

v1.8a — bugfix: keep console window open on help and error exits

A small but annoying paper-cut reported on encode.su: running
packPNG.exe via double-click (or any context that creates a fresh
console — Windows shortcut, drag-and-drop, etc.) would close that console
window before the user could read the help text or error message.

Cause: only the two success-path branches of main() went through the
existing "Press to quit" wait. Every other return — help on
no-args, help on empty filelist, all argument-parsing errors, and the
"no PNG/JNG files to compress" exit — return-ed straight from main(),
so the console terminated immediately after.

Fix: extract a tiny wait_and_return(int code) helper that respects
wait_exit && !module_mode (the same predicate the existing two
success-path waits already used), and route every user-visible return
through it. -np and -module keep skipping the prompt for scripted /
machine invocations exactly as before.

What changed

  • 9 user-visible return paths in main() now flow through
    wait_and_return(...) so the console stays open for the user to read
    output, regardless of how packPNG.exe was launched.
  • Manifest assemblyIdentity@version bumped 1.8.0.01.8.0.1
    (revision tick for the bugfix-letter, per Win32 PE convention).

What didn't change

  • Wire format unchanged: .ppg files written by v1.8 decode under
    v1.8a and vice versa, both TCIP (PNG/APNG) and TCIJ (JNG) magics.
  • PNG / APNG / JNG paths: identical compression behaviour to v1.8.
  • CLI flags: -np and -module continue to bypass the wait
    exactly as before.

Verified on Linux

no-args path  → "Press <enter> to quit" shown    OK
-mZ error     → error + "Press <enter> to quit"  OK
-od no path   → error + "Press <enter> to quit"  OK
-np           → no prompt                         OK
-module       → no prompt                         OK
PNG / JNG round-trip → byte-exact                OK

Verified on Windows 11

A test-v1.8a.bat selftest harness ships alongside the Windows binary in
the release. It runs 14 tests covering every v1.7e regression check, the
4 new wait-path tests, and a JNG round-trip regression for the v1.8
feature. ASCII-only with CRLF so cmd.exe parses it correctly before
chcp 65001 applies. Just drop it next to the .exe and double-click.

Binaries

  • packPNG-linux-x86_64-v1.8a — Linux x86-64, dynamically linked
    (zlib, liblzma, libzstd, libdeflate, libkanzi, pthread).
  • packPNG-windows-x86_64-full-v1.8a.exe — Windows x86-64, statically
    linked, mingw-w64 cross-compiled, UTF-8 active-code-page manifest.

Side note: YCoCg-R prototype

Following a suggestion on encode.su, I prototyped YCoCg-R (lifting form,
mod-256, fully reversible) as an opt-in pre-BWT transform in this same
cycle. Result: +2.06% regression on a combined 42-file corpus
(PngSuite, synthetic gradients, real photos, wild PNGs). The kanzi
RLT+BWT+SRT+ZRLT/FPAQ pipeline already does enough byte-decorrelation
that adding YCoCg on top tends to disrupt rather than help. Reverted
before this release — the negative result is documented for future
re-experimentation if the pipeline architecture changes.

🤖 Generated with Claude Code

v1.7e — audit fixes (-ver/-dry in tovyCIP, strict numeric flags)

05 May 04:14

Choose a tag to compare

Audit fixes — -ver / -dry now work in tovyCIP, strict numeric flags

P0 — silent bugs

  1. -ver was silently ignored in the default tovyCIP path. Before
    v1.7e, passing -ver with a default tovyCIP encode took exactly
    the same wallclock as without it — verify ran zero work even though
    the flag was accepted. Fixed by routing the encoder output through
    an in-memory buffer (new optional out_bytes parameter on
    compress_tovycip_archive) so -ver can decompress to a temp dir
    and byte-compare against the original PNG (or pixel-compare under
    -ldf).

  2. -dry was silently ignored in the default tovyCIP path. Files
    were written to disk regardless. Same plumbing: the encoder no
    longer writes when dry_run is set, but still tallies totals from
    the in-memory buffer so the summary line stays accurate. -ver -dry
    now does a full round-trip verify without touching disk.

P1 — strict flag validation

  1. -m, -th, -nofsep=, -zl= now reject non-numeric and
    out-of-range values with a clear stderr message and exit 2.
    Previously e.g. -mZ silently became a no-op (atoi returned 0,
    branch matched but did nothing) — inconsistent with the existing
    unknown flag: X → exit 2 policy. Removed a duplicate -nofsep=
    handler.

P2 / docs / UX

  1. collect() recursion now picks up .tcip and .ppgs files
    (legacy archive extensions). Previously -r over a tree containing
    those silently skipped them, even though detect_type would have
    dispatched them correctly.

  2. Banner subtitle now reads tovyCIP backend (kanzi BWT + zstd-19-long)
    instead of brute-force zlib match + solid LZMA (the old default).

  3. Help text: -perfile no longer documented as (no-op); describes
    the legacy LZMA opt-out it actually performs.

  4. Help text: -kpng-max no longer claims archive use case
    (archives were removed in v1.7) — explains the per-file ratio /
    decode trade-off instead.

  5. Help text: -sfth mentions parallel kanzi encode in the tovyCIP
    path, not just MT-LZMA.

  6. Help text: -deep claim of ~2x slower / ~6% smaller softened to
    reflect that on libpng-default encoders it's effectively a no-op
    (match always found on candidate 0); helps a few percent on exotic
    encoders.

  7. -od without a glued path now prints
    missing value: -od<path> (glue the path: -od/some/dir) instead
    of unknown flag: -od.

  8. Single missing file now shows the actual path:
    ERROR missing file: /path/to/file.png instead of
    ERROR 1 missing file(s).

Notes

  • Windows binary not shipped this release (no Windows host available
    during the cycle). Linux x86-64 only. Windows users on v1.7d
    experience the -ver / -dry bugs too — workaround until v1.7e
    Windows lands: use -m6 (legacy LZMA path) where both flags work
    correctly.

  • Compression algorithm and wire format unchanged from v1.7d. v1.7d
    output decompresses byte-exact under v1.7e and vice versa.

v1.7 — per-file tovyCIP with rename-on-collision

30 Apr 17:07

Choose a tag to compare

Major behaviour change

packPNG returns to its roots: a per-file PNG recompressor. tovyCIP
remains the default compression algorithm (kanzi RLT+BWT+SRT+ZRLT/FPAQ
for raw pixels + zstd-19-long for IDAT-passthrough), now applied
per-input-file instead of bundling N PNGs into a single archive.

```
packPNG a image.png → image.ppg
packPNG a *.png → N .ppg files (one per input)
packPNG a -r TILIN -odout → 9,406 .ppg in out/ (with collision rename)
```

Each .ppg holds exactly one entry, in TCIP wire format.

Why the revert

packPNG was always intended as a per-file recompressor ("Compresor de
Imágenes PNG"). The v1.5 multi-PNG archive mode mixed two concerns —
codec and container — and made selective extraction awkward. v1.7
separates them cleanly: tovyCIP is the codec, each .ppg is one PNG.

Collision handling

When a flat `-od` layout would produce two .ppg files with the same
basename (e.g. `-r` over a tree with 177× `Imagen1(1).png` in
different subdirs), the second file is renamed:

```
image.ppg → image(1).ppg → image(2).ppg → ...
```

Subdir structure is preserved inside the .ppg entry metadata, so the
decoder rebuilds the original tree even when output files were
renamed.

Legacy flags

`-tcip`, `-solid`, `-tovycip`, `-perfile` parse as no-ops.
Scripts using them keep working; the multi-PNG archive behaviour
those flags used to enable in v1.5–v1.6b is gone.

TILIN benchmark (Win11 i7-12700T, 9,406 PNGs, 572 MB)

Mode Time Output size Ratio Files lost
v1.7 per-file tovyCIP 55 s 410.2 MB (9,406 .ppg) 71.7% 0
v1.6b archive tovyCIP (removed) 159 s 303.2 MB (1 .ppg) 53.0% 0
v1.1 per-file LZMA 97 s ~418 MB (9,383 .ppg) 73.1% 58
xz preset 6 -mt 39 s 358.6 MB (1 .tar.xz) 62.7% 0

v1.7 beats v1.1 on every axis: 1.8× faster, better ratio, zero
collisions
(v1.1 silently lost 58 files to basename overwrite).

For grouped/solid storage use a real archiver (tar+xz, zpaqfranz,
syc) — packPNG isn't one.

Binaries

  • `packPNG-linux-x86_64-v1.7` — 786 KB (full feature, stripped)
  • `packPNG-windows-x86_64-full-v1.7.exe` — 4.7 MB (full feature,
    mingw cross, statically linked, UTF-8 manifest)

`Unblock-File` after download on Windows to clear MOTW.

v1.6 — unified .ppg extension

30 Apr 13:50

Choose a tag to compare

What changed

Single extension for all packPNG output. The .tcip extension introduced
in v1.5 for tovyCIP archives is gone — every output is now .ppg, the same
extension used by per-file packPNG mode. The decoder selects the right path
by reading the file's magic byte (PPG1 = single-file packPNG, TCIP /
PPGS = tovyCIP archive), so the unified extension is unambiguous.

packPNG a image.png        # → image.ppg     (1-entry tovyCIP archive)
packPNG a *.png            # → archive.ppg   (multi-PNG tovyCIP archive)
packPNG x archive.ppg      # extract back to byte-exact PNGs
packPNG a -perfile *.png   # opt-out: per-file packPNG (also .ppg)

Compatibility

  • Wire format unchanged. v1.6 archives carry the same TCIP magic
    internally as v1.5; existing v1.5 readers can decode v1.6 archives if you
    feed them through (they only inspect the bytes, not the filename).
  • v1.6 still decodes everything. All historical .ppg files (v1..v15),
    every .ppgs archive (v1.4–v1.5pre), and every .tcip archive (v1.5)
    continue to round-trip byte-exact.
  • -tcip flag retained as alias of -tovycip.

Validation

Test Result
3-PNG round-trip via archive.ppg 3/3 byte-exact
1-PNG round-trip via image.ppg byte-exact, magic = TCIP
Decode legacy v1.5 .tcip archive OK
Full feature build (kanzi+zstd+libdeflate) clean

Binaries

  • packPNG-linux-x86_64-v1.6 — 770 KB, full feature build (LZMA + zstd +
    libdeflate + kanzi-cpp). Stripped.
  • packPNG-windows-x86_64-full-v1.6.exe — 2.9 MB, full feature mingw
    cross-compiled, statically linked.

The Windows .exe is unsigned (mingw cross-build) — some AVs may flag it as
a generic heuristic (Trojan:Win32/Wacatac etc.). False positive; verify on
VirusTotal or build from source.

Full changelog since v1.5c

  • `353010b` release: v1.6 — unify archive extension to .ppg
  • `1b2367e` docs: README v1.6 — unified .ppg extension