Skip to content

Add progressive JPEG encoding - #161

Merged
skal65535 merged 5 commits into
mainfrom
skal/prog
Aug 25, 2026
Merged

skal65535 merged 5 commits into
mainfrom
skal/prog

Conversation

@skal65535

@skal65535 skal65535 commented Aug 21, 2026 •

Copy link
Copy Markdown
Collaborator

Adds progressive JPEG encoding

the code can be disabled by defining SJPEG_NO_PROGRESSIVE


  • 1 interleaved DC scan, followed by 1 or 2 non-interleaved AC scans/component,

  • luma/chroma scans are split at a configurable spectral position (Ss/Se),

  • use EOBn run-length coding across empty blocks (<- that's the real win!)

  • No successive approximation (Ah/Al are always 0). Not worth the code complexity.

  • Off by default (default sequential coding is bit-exact and unchanged in speed)

  • Can also be stripped from the build entirely with -DSJPEG_HAVE_PROGRESSIVE=0
    (CMake option or Makefile/Android.mk variable) -- src/prog.cc is excluded
    from the build outright in that case, not just its symbols.

  • -prog x[,y] sets the luma/chroma split points directly

  • -progressive is a shortcut for -prog 2,8

  • this default split values were jpegtran-cross-validated with a corpus sweep

  • -progressive: -2.5% mean / -3.4% total-corpus size, winning on 95% of cases


  • src/prog.cc: plane allocation and the DC/AC scan drivers
    (EncodeProgressive, EncodeProgAC).
  • src/entropy.cc: windowed AC coefficient coding and EOBn run-length
    coding for the progressive scans.
  • src/headers.cc: WriteOneDHT/WriteProgSOS for per-scan tables and
    explicit-component SOS markers.
  • examples/sjpeg.cc: -prog/-progressive CLI options.
  • fixed and updated man/sjpeg.1, man/vjpeg.1 (was gone stale)

Two real bugs found by fuzzing CLI option combinations against exotic inputs
under ASan/UBSan, both fixed: progressive + -trellis crashed (a missing
InitCodes(true) call left ac_codes_[] uninitialized), and a malformed PPM
header could overflow the size computation in examples/utils.cc's reader.

Bumped version to 0.1.2

Fixes #124

@skal65535 skal65535 mentioned this pull request Aug 21, 2026
@skal65535
skal65535 force-pushed the skal/prog branch 2 times, most recently from aa6996b to 9f528a8 Compare August 21, 2026 15:46
@skal65535
skal65535 marked this pull request as ready for review August 21, 2026 15:47
@skal65535
skal65535 force-pushed the skal/prog branch 7 times, most recently from 9d0ee8c to 6903e7b Compare August 21, 2026 16:29
@skal65535 skal65535 self-assigned this Aug 21, 2026
@skal65535
skal65535 marked this pull request as draft August 21, 2026 17:24
@skal65535 skal65535 changed the title Add progressive (spectral-split-only) JPEG encoding Add progressive JPEG encoding Aug 21, 2026
@skal65535
skal65535 force-pushed the skal/prog branch 4 times, most recently from 00c608f to ce01cf6 Compare August 21, 2026 23:24
Adds a spectral-selection-only progressive mode, opted into with the new
-prog x[,y] CLI option / EncoderParam::progressive_luma_split,chroma_split.
Each component gets one interleaved DC scan and one or two non-interleaved
AC scans split at position x (luma) or y (chroma), with EOBn run-length
coding across empty blocks. No successive approximation. x=64 (the
default) keeps the encoder on the untouched baseline path, bit-exact and
at no extra cost. -progressive is a shortcut for -prog 2,8, the split
found by a jpegtran-cross-validated corpus sweep to be the best practical
default: -0.71% mean / -0.90% total-corpus size at q=85, pixel-exact
round-trip verified on 1863 real photos.

-prog is incompatible with the multi-pass target-size/PSNR search
(-pass/-size/-psnr) and is silently ignored there, still yielding a valid
baseline encode.

SJPEG_HAVE_PROGRESSIVE (CMake option, and a Makefile/Android.mk variable,
on by default) strips the feature's code out entirely for size-constrained
builds -- src/prog.cc is excluded from the build, and the compiler define
SJPEG_NO_PROGRESSIVE guards the rest. -prog/-progressive and the
EncoderParam fields stay in the public API regardless of build config,
becoming a no-op rather than an error when the feature isn't compiled in.
EncodeProgressive() unconditionally re-ran TransformMCU() for every block
even when CollectHistograms() (adaptive quantization, on by default) had
already transformed and cached the whole image; now honors have_coeffs_
like SinglePassScan() does. CheckBuffers()/CheckProgBuffers() had verbatim-
duplicated slab-sizing logic; factored into ReserveSlab().

Also: -prog's CLI argument now gets the same range validation as -q/-r,
man/sjpeg.1's -prog entry gained the multi-pass caveat already in the CLI
help text and dropped the same JPEG-spec jargon trimmed elsewhere, and
EncoderParam::progressive_luma_split/chroma_split's in-class defaults
(duplicating what Init() already sets) were removed for consistency with
the rest of the struct.
@skal65535
skal65535 marked this pull request as ready for review August 23, 2026 16:28
@skal65535
skal65535 requested a review from jzern August 23, 2026 16:32
@hasinoff

Copy link
Copy Markdown
Contributor

Didn't look at the code in detail, but I can report it's passing Google's global presubmits and is performance neutral with default settings (as expected).

Comment thread src/sjpeg.h Outdated
Comment thread examples/sjpeg.cc
Comment thread examples/utils.cc Outdated
Comment thread man/sjpeg.1 Outdated
Comment thread man/sjpeg.1 Outdated
Comment thread man/sjpeg.1 Outdated
Comment thread src/enc.cc Outdated
Comment thread tests/unit_test.cc Outdated
Comment thread CMakeLists.txt Outdated
- ReadPPM: use a 64-bit intermediate for W*H*3, avoids overflow on 32-bit size_t
- man/sjpeg.1: bold -no_limit/-progressive refs, fix informational-messages wording
- enc.cc: clarify the neither/either comment in SetProgressive
- CMakeLists.txt: rename SJPEG_HAVE_PROGRESSIVE to SJPEG_ENABLE_PROGRESSIVE
- unit_test.cc: use a C-style cast for consistency
Resolves the Android.mk/CMakeLists.txt/Makefile/enc.cc conflicts against
main's AVX2 support and Encode()/SinglePassEncode() refactor: the
progressive branch now lives inside SinglePassEncode().
@skal65535
skal65535 merged commit f8eba93 into main Aug 25, 2026
13 checks passed
@skal65535
skal65535 deleted the skal/prog branch August 25, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

progressive coding

3 participants