Skip to content

wasm: enable JPEG/PNG/TIFF codecs#2044

Open
shimat wants to merge 4 commits into
mainfrom
feature/2043-wasm-jpeg-png-tiff
Open

wasm: enable JPEG/PNG/TIFF codecs#2044
shimat wants to merge 4 commits into
mainfrom
feature/2043-wasm-jpeg-png-tiff

Conversation

@shimat

@shimat shimat commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Enables -DWITH_JPEG=ON -DWITH_TIFF=ON -DWITH_PNG=ON for the wasm build (previously all OFF), and bumps the OpenCV build cache key so CI rebuilds with the new flags instead of reusing a stale cache.

Background

These codecs were disabled in .github/workflows/wasm.yml since the workflow's very first commit, apparently copied from OpenCV's own opencv.js build recipe (opencv/platforms/js/build_js.py), which disables the same set of codecs. That makes sense for opencv.js, since it decodes images via the browser's Canvas/Image API and never touches imgcodecs at all.

OpenCvSharp's wasm target has no equivalent JS-side decode path, though: Cv2.ImDecode / Mat.FromImageData are the only decode entry points exposed to .NET code. With the codecs disabled, these silently returned an empty Mat instead of throwing, which was hard to diagnose (confirmed in #2043).

find_package(JPEG/PNG/TIFF) fails to find a system library under the Emscripten cross-compile toolchain, so WITH_*=ON alone is enough to fall back to the bundled 3rdparty/libjpeg-turbo / libpng / libtiff sources (opencv/cmake/OpenCVFindLibsGrfmt.cmake). libjpeg-turbo's SIMD detection also degrades gracefully to a non-SIMD build for unrecognized CPU types rather than failing.

Fixes #2043

Test plan

  • CI wasm build passes with the new cache key (forces a full OpenCV rebuild)
  • Cv2.GetBuildInformation() on the resulting wasm build shows JPEG/PNG/TIFF as available under Media I/O
  • Cv2.ImDecode / Mat.FromImageData successfully decode a JPEG/PNG (not TIFF, not empty) byte array on wasm

Summary by CodeRabbit

  • Bug Fixes
    • Improved WebAssembly build caching to automatically refresh cached OpenCV builds when configuration changes.
    • Aligned WebAssembly exception/long-jump handling for more reliable OpenCV wasm behavior.
  • New Features
    • Enabled additional OpenCV WebAssembly image support, including JPEG, PNG, and TIFF.
  • Tests
    • Strengthened the WebAssembly smoke test to perform JPEG/PNG/TIFF encode/decode round-trips, validate results, and more reliably catch silent failures.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0814eeef-d948-4b8f-8347-4b6df34f5960

📥 Commits

Reviewing files that changed from the base of the PR and between 63c3f02 and 9151453.

📒 Files selected for processing (1)
  • .github/workflows/wasm.yml

📝 Walkthrough

Walkthrough

The wasm workflow refreshes the OpenCV cache, enables JPEG/TIFF/PNG codecs, and aligns exception handling flags across wasm builds. The smoke test validates encode/decode round trips for all three formats.

Changes

Wasm codec support

Layer / File(s) Summary
Cache and codec build configuration
.github/workflows/wasm.yml
Updates OPENCV_CACHE_VERSION from 2 to 4, enables JPEG, TIFF, and PNG codecs, and adds matching wasm exception and longjmp flags to the OpenCV and OpenCvSharpExtern builds.
Codec round-trip smoke validation
test/OpenCvSharp.Tests.Wasm/App.razor
Encodes and decodes JPEG, PNG, and TIFF images, rejecting empty buffers, empty decoded images, or size mismatches.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WasmWorkflow
  participant OpenCVBuild
  participant OpenCvSharpExternBuild
  participant WasmSmokeTest
  WasmWorkflow->>OpenCVBuild: Build OpenCV wasm with codecs and matching exception flags
  WasmWorkflow->>OpenCvSharpExternBuild: Build extern wasm with matching exception flags
  OpenCVBuild-->>WasmSmokeTest: Provide codec-enabled runtime
  WasmSmokeTest->>OpenCVBuild: Encode and decode test image
  OpenCVBuild-->>WasmSmokeTest: Return encoded data and decoded Mat
  WasmSmokeTest->>WasmSmokeTest: Validate non-empty output and matching dimensions
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling JPEG/PNG/TIFF codecs for wasm.
Linked Issues check ✅ Passed The PR enables the requested codecs and adds wasm round-trip tests that catch the silent empty-Mat decode failure.
Out of Scope Changes check ✅ Passed The cache bump, codec flags, exception/longjmp flags, and test updates all support the wasm codec work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/2043-wasm-jpeg-png-tiff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/wasm.yml:
- Around line 128-130: Add an OpenCV codec validation step in the workflow
before actions/cache/save, asserting JPEG, PNG, and TIFF support using the
existing BuildConfigurationTest coverage or an equivalent decode smoke test.
Ensure the validation also runs on cache-hit paths before cached artifacts are
reused, preventing partial codec builds from passing silently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2301aee0-e7f1-4bb6-84f5-f265ad0a75b1

📥 Commits

Reviewing files that changed from the base of the PR and between 5c9eced and 5a8818e.

📒 Files selected for processing (1)
  • .github/workflows/wasm.yml

Comment on lines +128 to +130
-DWITH_JPEG=ON \
-DWITH_TIFF=ON \
-DWITH_PNG=ON \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
nl -ba .github/workflows/wasm.yml | sed -n '90,180p'

echo
echo "== search for codec/build-info checks in workflows =="
rg -n "getBuildInformation|BUILD_INFORMATION|imread|decode|JPEG|PNG|TIFF|WITH_JPEG|WITH_PNG|WITH_TIFF|opencv_version" .github/workflows . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

echo
echo "== locate OpenCV version pin =="
rg -n "OPENCV_VERSION|opencv/opencv|opencv" .github/workflows . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: shimat/opencvsharp

Length of output: 219


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .github/workflows/wasm.yml (120-170) =="
nl -ba .github/workflows/wasm.yml | sed -n '120,170p'

echo
echo "== references to the cache step in wasm workflow =="
rg -n "cache|actions/cache|save cache|restore cache|ccache|sccache" .github/workflows/wasm.yml

echo
echo "== references to OpenCV build info / smoke tests in repo =="
rg -n "getBuildInformation|imread|imdecode|decode.*smoke|jpeg|png|tiff" . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: shimat/opencvsharp

Length of output: 239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .github/workflows/wasm.yml (120-170) =="
sed -n '120,170p' .github/workflows/wasm.yml | cat -n

echo
echo "== cache / build / smoke-test references in wasm workflow =="
rg -n "cache|actions/cache|ccache|sccache|getBuildInformation|BUILD_INFORMATION|imread|imdecode|decode|JPEG|PNG|TIFF|WITH_JPEG|WITH_PNG|WITH_TIFF|opencv_version" .github/workflows/wasm.yml

echo
echo "== any codec/build-info checks elsewhere in repo =="
rg -n "getBuildInformation|BUILD_INFORMATION|imread|imdecode|decode.*smoke|WITH_JPEG|WITH_PNG|WITH_TIFF|JPEG|PNG|TIFF" . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: shimat/opencvsharp

Length of output: 27231


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .github/workflows/wasm.yml (1-120) =="
sed -n '1,120p' .github/workflows/wasm.yml | cat -n

echo
echo "== BuildConfigurationTest.cs relevant part =="
sed -n '1,220p' test/OpenCvSharp.Tests/core/BuildConfigurationTest.cs | cat -n

Repository: shimat/opencvsharp

Length of output: 17013


Add a codec smoke test before caching OpenCV
test/OpenCvSharp.Tests/core/BuildConfigurationTest.cs already checks JPEG/PNG/TIFF via Cv2.GetBuildInformation(), but this workflow never runs it. Add a build-info assertion or JPEG/PNG/TIFF decode smoke test before actions/cache/save, and on cache-hit paths, so a partial OpenCV build can’t be reused silently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/wasm.yml around lines 128 - 130, Add an OpenCV codec
validation step in the workflow before actions/cache/save, asserting JPEG, PNG,
and TIFF support using the existing BuildConfigurationTest coverage or an
equivalent decode smoke test. Ensure the validation also runs on cache-hit paths
before cached artifacts are reused, preventing partial codec builds from passing
silently.

These were disabled since the workflow's original commit, copied from
opencv.js's own build recipe. That recipe assumes image decoding happens
via the browser's Canvas/Image API, bypassing imgcodecs entirely, but
OpenCvSharp's wasm target has no such path: Cv2.ImDecode / Mat.FromImageData
are the only decode entry points, so disabling these codecs made them
silently return an empty Mat instead of throwing. Bump the OpenCV build
cache key so CI doesn't reuse a stale cache built without these codecs.

Fixes #2043
@shimat shimat force-pushed the feature/2043-wasm-jpeg-png-tiff branch from 5a8818e to 04fcbe8 Compare July 12, 2026 14:14
Addresses CodeRabbit feedback on PR #2044: enabling WITH_JPEG/TIFF/PNG in
the cmake build doesn't by itself prove the codecs actually work on wasm,
and a broken build would otherwise be cached and pass CI silently, same
failure mode as #2043 itself.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/OpenCvSharp.Tests.Wasm/App.razor (1)

29-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Solid round-trip validation.

The empty-buffer and wrong-size checks correctly guard against codecs that build but silently no-op under wasm, matching the PR objectives. The using on decoded properly disposes the decoded Mat.

One optional note: ImEncode returns a bool success flag (per the upstream contract in Cv2_imgcodecs.cs:486-498) that is currently ignored. The downstream ImDecode size check provides a safety net, so this is not a functional gap, but checking the return value directly would give a clearer failure message on encode failure.

♻️ Optional: check ImEncode return value for clearer diagnostics
-                Cv2.ImEncode(ext, mat, out var buf);
-                if (buf.Length == 0)
-                    throw new InvalidOperationException($"ImEncode({ext}) produced an empty buffer");
+                if (!Cv2.ImEncode(ext, mat, out var buf) || buf.Length == 0)
+                    throw new InvalidOperationException($"ImEncode({ext}) produced an empty buffer");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/OpenCvSharp.Tests.Wasm/App.razor` around lines 29 - 38, Optionally
update the round-trip loop in App.razor to capture and validate the boolean
result returned by Cv2.ImEncode before inspecting buf. Preserve the existing
empty-buffer and decode validation, and throw a clear extension-specific error
when encoding reports failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/OpenCvSharp.Tests.Wasm/App.razor`:
- Around line 29-38: Optionally update the round-trip loop in App.razor to
capture and validate the boolean result returned by Cv2.ImEncode before
inspecting buf. Preserve the existing empty-buffer and decode validation, and
throw a clear extension-specific error when encoding reports failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0e52ef63-4efc-485b-86a9-21307c650dd2

📥 Commits

Reviewing files that changed from the base of the PR and between 04fcbe8 and ee9db4b.

📒 Files selected for processing (1)
  • test/OpenCvSharp.Tests.Wasm/App.razor

@shimat shimat self-assigned this Jul 12, 2026
shimat added 2 commits July 12, 2026 23:49
…default

libjpeg-turbo (and libpng) use setjmp/longjmp for error handling. Building
OpenCV/OpenCvSharpExtern with plain emcc left those calls lowered with the
legacy JS-based invoke_ wrapper mechanism, while the .NET wasm SDK links its
own runtime using the newer wasm exception-handling proposal by default
(WasmEnableExceptionHandling=true). Mixing the two in the final link fails
with "invoke_ functions exported but exceptions and longjmp are both
disabled". Compile both OpenCV and OpenCvSharpExtern with -fwasm-exceptions
-sSUPPORT_LONGJMP=wasm to match.
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.

wasm: JPEG/PNG/TIFF codecs are disabled, causing silent empty-Mat decode failures

1 participant