Skip to content

fix(images): choose backgrounds for transparent image conversions - #3

Merged
leoisadev1 merged 6 commits into
mainfrom
leoplayz317/cnv-5-transparent-images-turn-black-when-converted-to-jpg-add-a
Oct 6, 2026
Merged

leoisadev1 merged 6 commits into
mainfrom
leoplayz317/cnv-5-transparent-images-turn-black-when-converted-to-jpg-add-a

Conversation

@leoisadev1

@leoisadev1 leoisadev1 commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Transparent images converted to JPG kept hidden RGB values instead of showing a white background. Add --background white|black|transparent|#rrggbb, preset support, and a Background dropdown in Quick convert. JPG and PPM default to white; alpha-capable images retain transparency unless a color is chosen. PDF pages can render with a transparent or colored background.

Transparent pixel → output
  JPG / PPM, default       → white
  Alpha-capable, default  → transparency preserved
  Chosen color            → composited onto that color
  Transparent + JPG       → clear error

Evidence

Before, the actual transparent PNG → JPG CLI run on Linux and macOS produced (12, 128, 128) at the transparent corner. After, the same fixture produces:

JPG default       (255, 255, 255, 255)
JPG black         (0, 0, 0, 255)
JPG #ff8800       (255, 136, 0, 255)
WebP white        (255, 255, 255, 255)
WebP default      alpha 0
PDF → PNG clear   alpha 0
PDF → PNG black   (0, 0, 0, 255)
PDF → PNG orange  (255, 136, 0, 255)
SVG → JPG default (255, 255, 255, 255)
Transparent JPG   exit 1: JPEG can't store transparency;
                  choose a background color such as white or black
  • Real CLI pixel checks passed on Linux and Leo's Mac, including a colored preset and PPM's white default. PNG/SVG/PDF target lists match the base binary on both machines.
  • cargo test, bun run rs:check, and all 101 cargo test -p convt-app headless tests passed on Linux. Both Quick convert background tests passed. All 13 background integration tests passed on macOS in round 2, including colored PNG and JPEG pixel-aspect checks.
  • Independent GPT-6.1 Sol review found a video-frame aspect-ratio regression; the fix now preserves the extracted PNG pixel aspect when encoding JPG. Unscaled, scaled, and multi-stream cases preserve display proportions; large ratios fit JFIF's 16-bit fields. Final independent review found no remaining issues.
  • Round 2 fixes preserve PNG pHYs, gamma, chromaticities, cICP, sRGB and ICC metadata when applying a color; add Automatic without resetting other controls; retain preset custom colors; and reject Transparent for JPEG/PPM regardless of source alpha. Regression checks failed before the fixes and passed afterward.
  • A neighboring-format regression check also found that colored opaque grayscale PNG frames dropped from 16 to 8 bits. A separate fix preserves grayscale and its original bit depth; real CLI output and a registry regression test pass.
  • BT.709-tagged JPEG patch colors match a direct FFmpeg reference with explicit BT.709-to-BT.601 and full-range conversion. The suspected JPEG color regression was not reproduced. A Standard-tier GPT-6.1 Sol independent review found no remaining issues.
  • Desktop GUI visuals were not checked because launching the GUI was expressly prohibited. Linux HEIC routes were not exercised because the system libheif lacks the required patch.
  • GitHub CI on current main cannot start jobs: GitHub reports an account payment or spending-limit problem. This is an infrastructure blocker, separate from CNV-7's known macOS symlink and Linux disk failures.

Merge Danger

Door: two-way. Reverting restores the previous conversion behavior; no migration or network behavior changes.

Blast radius: image backgrounds through the shared conversion engines, CLI presets, and Quick convert. Video-to-JPG encoding also changes to preserve alpha compositing and pixel aspect ratios.

Fixes CNV-5


Devin Review

Created with GPT-6.1 Sol in T3 Code.

leoisadev1 and others added 4 commits October 6, 2026 15:42
…lack

Converting a transparent image to a format without transparency
dropped the alpha channel, so transparent areas came out black (PNG,
WebP or SVG to JPG). A new background option decides what they become:

- Unset: formats that can store transparency keep it; JPEG and PPM
  get white.
- A color (`white`, `black`, `#rrggbb` or `#rgb`): transparency is
  flattened onto it, for any image output. Half-transparent pixels
  blend with it, and 16-bit and float images keep their precision.
- `transparent`: kept where possible; refused with a clear message for
  JPEG and PPM.

It applies to the image engine, PDF pages (rendered onto the color, or
a clear page background for PNG and other formats with transparency),
SVG (drawn onto the color) and HEIC and AVIF output. The CLI takes
--background, presets take `background = "..."`, and Quick convert
shows a Background control for image targets.

Fixes CNV-5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the CNV-5 review:
- Video to PNG or JPEG ignored the background: FFmpeg's JPEG encoder
  dropped alpha to black. Still frames now come out of FFmpeg as PNG and
  go through the image encoder like any other image. Video to GIF with
  a background color is refused with a clear message rather than
  ignored.
- macOS's sips path ignored a chosen color: the PNG is flattened
  before sips encodes HEIC, and after it decodes to PNG.
- BMP kept RGBA alpha but dropped gray-alpha's; gray-alpha images are
  now written as RGBA.
- Quick convert showed Transparent as the default for PDF to PNG, but
  PDF pages render on white unless Transparent is picked. The default
  label now follows the source, and the control is hidden for video to
  GIF.

Refs CNV-5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the second CNV-5 review:
- Video to PNG re-encoded FFmpeg's frame and lost its color (cICP,
  cHRM) and pixel-aspect (pHYs) tags. PNG frames keep FFmpeg's file
  again; only JPEG goes through the image encoder, and a chosen color
  flattens the PNG.
- Quick convert's default label now comes from each file's route:
  White when it renders through PDF (documents too) or the format can't
  keep transparency, Transparent otherwise, and Automatic when the
  selected files differ.
- A preset's background no longer applies where the control is hidden
  (video to GIF), where it could only make the conversion fail.

Refs CNV-5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
CNV-5 Transparent images turn black when converted to JPG; add a background option

Problem

Converting an image with transparency to a format without it (PNG or WebP to JPG, SVG to JPG) turns the transparent areas black. The encoder drops the alpha channel, and fully transparent pixels are stored as black. People expect white, and some want a different background.

Goal

Let people choose what transparent areas become, with a sensible default.

Acceptance criteria

  • New option background: white, black, transparent, or any #rrggbb color. Available as --background in the CLI, background = "..." in presets, and in the desktop app's Quick convert.
  • Targets without transparency (JPG, PPM) default to a white background.
  • Targets with transparency (PNG, WebP, AVIF, GIF, TIFF, ICO, QOI, TGA, EXR, HEIC) keep it by default; a chosen color flattens them onto that color.
  • transparent with a target that can't store it fails with a clear message instead of silently picking a color.
  • PDF pages render onto the chosen background, so PDF to PNG can be transparent.
  • Tests cover the default, each choice, and the error.

How to verify

convt logo.png --to jpg                          # white background
convt logo.png --to jpg --background black       # black
convt logo.png --to jpg --background "#ff8800"   # orange
convt logo.png --to webp --background white      # flattened WebP
convt logo.png --to jpg --background transparent # error

Then pick a background in Quick convert in the app.

Review in Linear

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Devin Review

Comment thread crates/convt-engines/src/image.rs Resolved
Comment thread crates/convt-engines/src/image.rs Resolved
Comment thread crates/convt-engines/src/ffmpeg.rs Resolved
@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds background color option for image conversions.

The PR appears safe to merge; no new actionable issue remains.

What we checked:

  • Automatic keeps other settings: Automatic clears self.background. conversion_options replaces the preset background with that cleared value and leaves other controls alone.
  • Grayscale keeps sixteen bits: flatten leaves opaque images unchanged. background_png writes ImageLuma16 as 16-bit grayscale.

Summary

Adds background choices to the CLI, presets, and Quick convert. JPEG and PPM default to white; formats that support transparency keep it unless a color is chosen.

  • The latest changes preserve PNG pixel proportions, color tags, and 16-bit grayscale.
  • Quick convert now offers Automatic and keeps a preset’s custom color available.
  • All three previous findings are addressed in the current code. Their supplied threads are unnumbered, so they have no previousFindings entries.
  • No new actionable issues were found. Tests were not run during this review, and desktop visuals were not checked because launching the app requires explicit permission.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[CLI or Quick convert] --> B[Background option]
  B --> C{Chosen background}
  C -->|Color| D[Place image over color]
  C -->|Automatic| E[Use route default]
  C -->|Transparent| F{Output supports transparency}
  F -->|Yes| G[Keep transparency]
  F -->|No| H[Return clear error]
  D --> I[Encode output]
  E --> I
  G --> I
Loading

Reviews (2) · Last reviewed commit: "fix: keep grayscale PNG frame precision ..."

Comment thread crates/convt-engines/src/ffmpeg.rs Resolved
Comment thread crates/convt-app/src/ui/quick.rs Resolved
Comment thread crates/convt-app/src/ui/quick.rs Resolved

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

Adds a background option for transparent areas: --background takes black, white, a hex color or transparent (refused for formats that can't store it), and JPEG and PPM fall back to white by default. Quick convert gets a Background select for image targets that shows the engine default (or "Automatic" when files would differ) and keeps a preset's custom color pickable. The control is hidden for video-to-GIF, where the engines don't take a color yet, so a preset's background is dropped there instead of failing the conversion.

Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 507 functions depend on the 501 functions this change touches.

Health — this change adds coupling hotspots:

  • new: validate() — 5 callers, 6 callees
  • new: encoded_fixture() — 6 callers, 3 callees
  • new: encode_depth() — 2 callers, 7 callees
  • new: rgb_to_srgb_matrix() — 3 callers, 4 callees
  • new: run() — 1 callers, 10 callees
  • new: pack() — 1 callers, 6 callees
  • new: a_document_offers_the_pack_and_downloads_only_after_the_click() — 0 callers, 10 callees
  • new: full_matrix() — 0 callers, 10 callees
  • …and 32 more — each is listed as a finding

Verification — 507 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 507 function(s) in the blast radius were not formally verified this run

Docs that may be stale (advisory)

…and 10 more.

· 40 more finding(s) on lines outside this diff (see the check run).

@leoisadev1
leoisadev1 merged commit 27bd399 into main Oct 6, 2026
1 of 5 checks passed
@leoisadev1
leoisadev1 deleted the leoplayz317/cnv-5-transparent-images-turn-black-when-converted-to-jpg-add-a branch October 6, 2026 20:43
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.

1 participant