DEV Community

星TAP
星TAP

Posted on

I Wrote a Rust Image Compressor That Survives WeChat's Brutal Re-Compression

I Wrote a Rust Image Compressor That Survives WeChat's Brutal Re-Compression

If you've ever sent a photo through WeChat and watched it come out looking like it went through a blender, you know the feeling. The colors shift, gradients turn into visible banding, and fine detail just evaporates. Instagram and WhatsApp do the same thing, just with different thresholds.

I got annoyed enough to actually dig into what these platforms do to images, and then I built a tool to fight back. It's written in Rust, uses mozjpeg, keeps full 4:4:4 chroma, and — the part I'm personally most happy with — it exposes a clean JSON interface so AI agents can drive it directly.

Here's the whole story: what the platforms are actually doing, what I built to counter it, and why I bothered with Rust.

What platforms actually do to your images

Most people assume "compression" just means "the file got smaller." It's worse than that. Re-compression is three separate things happening at once:

  1. Downscaling. Anything wider than roughly 1080–1440px gets shrunk to fit.
  2. Chroma subsampling. This is the sneaky one. Your image goes from 4:4:4 (full color resolution) down to 4:2:0 — half the color information gets discarded. You don't notice it on a phone screen, but red edges and smooth skies turn into blocks.
  3. Low-quality re-encode. Usually JPEG at Q50–Q70, which quietly quantizes away the detail you cared about.

The realization that changed how I built this: the platform compresses unconditionally. If your file is over its threshold, it compresses again. No exceptions, no mercy. So "preventing re-compression" was never about stopping them — it's about pre-processing your image so that after they compress it, it still looks good.

My approach: 4:4:4 + CAS

The default mode in my tool (which I just call "quality first") plays four cards:

1. Keep 4:4:4 chroma. Most compressed images look muddy because they've already been subsampled to 4:2:0. My encoder outputs 4:4:4 — color stays at full resolution right alongside luminance. No more color blocking, no more weird edges on red text.

2. Start at Q96. Platforms re-encode at Q50–70. I encode at Q96, which is near-lossless. So when they compress it again, the floor is already high and the result stays sharp. The tradeoff is file size — but in this mode, size is just a safety threshold, not something I'm actively trying to minimize.

3. CAS sharpening. Downscaling makes everything feel soft. I use Contrast-Adaptive Sharpening (CAS) with a strength of 0–1, defaulting to 0.35 in quality mode. It sharpens landscapes more, faces less, and skips high-noise areas entirely. The result looks like the original's natural clarity, not an over-sharpened mess with halos around every edge.

4. Per-platform presets. WeChat, Xiaohongshu (RED), Instagram, and a generic pipeline each have different safety thresholds. The engine picks the right max dimension, size cap, and quality automatically when you select a platform. You don't have to memorize parameters.

Preset Platform What it handles
wechat WeChat / Moments Anti-recompression sizing for chat & moments
xiaohongshu Xiaohongshu (RED) 1660px long edge, feed-optimized quality
instagram Instagram Portrait / square feed sizing
general Any pipeline Balanced default, no forced sRGB conversion

Why Rust + mozjpeg

Three reasons that mattered to me:

  • Speed. Parallel processing, around 89ms per image on 8 cores.
  • Single-file distribution. About 4.5MB, zero dependencies. Double-click on Windows, unzip on macOS. No runtime, no installer.
  • Memory safety. It doesn't crash on a weird input file.

For the actual JPEG encoding I use a pure-Rust mozjpeg wrapper, which holds up better at the same file size than stock libjpeg.

The architectural choice I'm proudest of: the GUI and the CLI are just two skins over one compression core. So the quality a human sees by dragging a file in is exactly what an AI agent gets when it calls the same engine. No drift between "the pretty demo" and "what the API actually does."

The part AI agents will care about: a JSON interface

Most compression tools stop at a GUI. This one also speaks Agent-First JSON — an AI agent doesn't need to learn CLI flags, it just throws a JSON envelope at it:

echo '{"files":["a.jpg","b.jpg"],"platform":"wechat","quality_mode":"max"}' \
  | ./image-compressor --json
Enter fullscreen mode Exit fullscreen mode

A few design decisions that make it agent-friendly:

  • Idempotent resume. Existing outputs are skipped automatically; re-running won't double-compress. --force overrides when you really mean it.
  • Exit codes that mean something. 0 = all good, 1 = some failed, 2 = bad arguments. The agent knows whether to retry or bail.
  • Streaming JSONL. For big batches it emits one JSON line per file as it goes, then a summary envelope at the end. No blocking, no waiting for the whole job.
  • Fully local. Images never leave the machine. Drop it into any AI workflow without a privacy review.

Benchmarks (default mode, nothing tuned)

Metric Result
Per-image time ~89ms (8-core parallel)
Typical ratio 2–8x in social mode; quality mode prioritizes quality over size
Peak memory ~15MB
Distributable size ~4.5MB single file (Windows)
Chroma 4:4:4 preserved

What an independent model thought

I threw the design at Qwen 3.8 for an outside read. Its verdict: top-tier in imaging expertise, engineering architecture, and scenario insight. I'll let you judge that claim for yourself rather than parrot the praise — but it was a useful sanity check that the architecture holds up under scrutiny.

Where to get it

Everything runs locally. Windows: double-click. macOS: unzip and run (if it won't open, here's the fix).

Wrapping up

"Anti re-compression" isn't magic. It's reverse-engineering what the platform does, then satisfying it first. 4:4:4 keeps the color, Q96 keeps the detail, CAS keeps the sharpness, and platform presets keep the size in bounds. Stack those and the platform can't degrade your photo no matter how many times it compresses.

If WeChat, Instagram, or WhatsApp compression has been eating your photos too — or if you want a local, offline, JSON-callable image compressor in your AI workflow — give it a try. PRs are welcome.

Top comments (0)