Skip to content

refactor: remove OpenCvSharp dependency and replace with native implementations#1642

Open
yuto-trd wants to merge 10 commits intomainfrom
yuto-trd/remove-opencv
Open

refactor: remove OpenCvSharp dependency and replace with native implementations#1642
yuto-trd wants to merge 10 commits intomainfrom
yuto-trd/remove-opencv

Conversation

@yuto-trd
Copy link
Copy Markdown
Member

@yuto-trd yuto-trd commented Mar 31, 2026

Description

Remove the OpenCvSharp NuGet dependency entirely from the project, replacing all usages with SkiaSharp-based or native implementations:

  • YUV Conversion: Replaced Mat/Cv2.CvtColor in MFReader with a new YuvConversion utility class that performs YUY2→BGRA conversion using direct pixel manipulation.
  • CubeFile: Removed Image.ToMat() dependency; LUT application now uses direct SKBitmap pixel access.
  • FrameCacheManager: Replaced OpenCV-backed cache compression with SkiaSharp SKBitmap encode/decode for memory-efficient frame caching.
  • Filter Effects: Removed the OpenCv filter effects namespace (Blur, GaussianBlur, MedianBlur) and their node/library registrations, as these are superseded by existing SkiaSharp-based effects.
  • CI/Build: Removed the libOpenCvSharpExtern.so download step from build-executable.yml and daily-build.yml.
  • Packages: Removed OpenCvSharp4, OpenCvSharp4.runtime.osx.10.15-x64, and OpenCvSharp4.runtime.win from Directory.Packages.props.
  • Third-party notices: Removed OpenCV-related license entries from THIRD_PARTY_NOTICES.md.

Breaking changes

  • Beutl.Graphics.FilterEffects.OpenCv.Blur, GaussianBlur, and MedianBlur filter effects have been removed. Users of these effects in saved projects will need to switch to the built-in SkiaSharp blur effects.
  • Image.ToMat() extension method has been removed.

Fixed issues

N/A

…tive implementations

Replace OpenCV-based blur filters (Blur, GaussianBlur, MedianBlur) with
SkiaSharp shader implementations, replace OpenCV YUV conversion in
MFReader with custom YuvConversion utility, and remove CubeFile's
OpenCV dependency by using direct pixel manipulation. Remove
OpenCvSharp packages from Directory.Packages.props, CI workflows,
and third-party notices.
OpenCv namespace under FilterEffects (Blur, GaussianBlur, MedianBlur) is
no longer needed after the OpenCvSharp dependency removal. Remove the
effect files and their node/library registrations.
Copilot AI review requested due to automatic review settings March 31, 2026 07:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR removes the OpenCvSharp dependency across the project, replacing prior OpenCV-based image processing and caching paths with SkiaSharp and native pixel conversion utilities.

Changes:

  • Removed OpenCvSharp NuGet packages, CI native dependency download steps, and OpenCV third-party notices.
  • Replaced OpenCV-based frame read/conversion and frame-cache storage with a new native YuvConversion utility and byte[]-backed caching.
  • Removed OpenCV filter effect implementations and their library/node registrations.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
THIRD_PARTY_NOTICES.md Removes OpenCvSharp/OpenCV license blocks from notices.
src/Beutl/Services/LibraryRegistrar.cs Removes OpenCV filter effect group registration.
src/Beutl/Models/FrameCacheManager.CacheEntry.cs Replaces Mat storage with byte[] cache data + Skia resize + YUV conversion.
src/Beutl.NodeGraph/Nodes/NodesRegistrar.cs Removes OpenCV nodes from node registry.
src/Beutl.Extensions.MediaFoundation/Decoding/MFReader.cs Replaces OpenCV color conversion with native YUY2→BGRA conversion via YuvConversion.
src/Beutl.Engine/Media/YuvConversion.cs Adds native BGRA↔I420 and YUY2→BGRA conversion routines.
src/Beutl.Engine/Graphics/Image.cs Removes Image.ToMat() extension and OpenCvSharp usage.
src/Beutl.Engine/Graphics/FilterEffects/OpenCv/MedianBlur.cs Deletes OpenCV-based effect implementation.
src/Beutl.Engine/Graphics/FilterEffects/OpenCv/GaussianBlur.cs Deletes OpenCV-based effect implementation.
src/Beutl.Engine/Graphics/FilterEffects/OpenCv/Blur.cs Deletes OpenCV-based effect implementation.
src/Beutl.Engine/Graphics/CubeFile.cs Removes OpenCvSharp Vec3b from LUT interpolation API.
src/Beutl.Engine/Beutl.Engine.csproj Drops OpenCvSharp package references.
Directory.Packages.props Removes OpenCvSharp package versions.
.github/workflows/daily-build.yml Removes Linux libOpenCvSharpExtern.so download step.
.github/workflows/build-executable.yml Removes Linux libOpenCvSharpExtern.so download step.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

yuto-trd added 7 commits April 1, 2026 01:23
Use ceiling division for UV plane dimensions so that odd-sized inputs
are converted correctly instead of silently dropping the last row/column.
Switch cache buffers from new byte[] to ArrayPool to reduce GC pressure.
Use SKFilterMode.Linear for resize instead of Default for better quality.
…me cache

YuvConversion and FrameCacheManager assumed tightly packed pixel data
(RowBytes == width * bytesPerPixel), which could corrupt images if
SKBitmap has row padding. Add explicit stride parameters to BgraToI420,
I420ToBgra, and Yuy2ToBgra, and copy row-by-row when strides differ.
{
Buffer.MemoryCopy(
srcBase + (long)y * srcStride,
dstBase + (long)y * dstStride,

Check failure

Code scanning / CodeQL

Unvalidated local pointer arithmetic Critical

Unvalidated pointer arithmetic from virtual method
Rent
.

Copilot Autofix

AI about 3 hours ago

General approach: after renting the buffer from ArrayPool<byte>.Shared.Rent(bgraSize), validate that bgraData.Length is at least bgraSize before using it in unsafe code and pointer arithmetic. If the length is too small, either throw a descriptive exception or fall back to a safe allocation path. This ensures that regardless of the concrete ArrayPool<byte> implementation, pointer arithmetic on dstBase remains within the bounds of the underlying array.

Best specific fix for this snippet: immediately after byte[] bgraData = ArrayPool<byte>.Shared.Rent(bgraSize); on line 101, add a check:

if (bgraData.Length < bgraSize)
{
    throw new InvalidOperationException("Rented buffer is smaller than requested size.");
}

This is minimal, preserves existing behavior under normal conditions (where the check always passes), and satisfies the requirement of validating the result of the virtual Rent call before using it in unsafe pointer arithmetic in the else branch. No other logic needs to change, because the code already restricts each per-row copy to dstStride bytes, and the total requested size bgraSize is used consistently.

File/lines to change: src/Beutl/Models/FrameCacheManager.CacheEntry.cs, in the method shown, right after line 101 where bgraData is rented. Required additions: a using System; is already implicit in most C# projects; if not already present in this file or via global usings, throwing InvalidOperationException may require using System;, but we will not modify imports unless needed. Since InvalidOperationException is in System, and the file already uses other System.* namespaces, it is safe to assume System is available or can be referenced without additional using; we therefore just add the check.


Suggested changeset 1
src/Beutl/Models/FrameCacheManager.CacheEntry.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Beutl/Models/FrameCacheManager.CacheEntry.cs b/src/Beutl/Models/FrameCacheManager.CacheEntry.cs
--- a/src/Beutl/Models/FrameCacheManager.CacheEntry.cs
+++ b/src/Beutl/Models/FrameCacheManager.CacheEntry.cs
@@ -99,6 +99,10 @@
                     int dstStride = w * 4;
                     int bgraSize = dstStride * h;
                     byte[] bgraData = ArrayPool<byte>.Shared.Rent(bgraSize);
+                    if (bgraData.Length < bgraSize)
+                    {
+                        throw new InvalidOperationException("Rented buffer is smaller than requested size.");
+                    }
                     int srcStride = current.RowBytes;
 
                     if (srcStride == dstStride)
EOF
@@ -99,6 +99,10 @@
int dstStride = w * 4;
int bgraSize = dstStride * h;
byte[] bgraData = ArrayPool<byte>.Shared.Rent(bgraSize);
if (bgraData.Length < bgraSize)
{
throw new InvalidOperationException("Rented buffer is smaller than requested size.");
}
int srcStride = current.RowBytes;

if (srcStride == dstStride)
Copilot is powered by AI and may make mistakes. Always verify output.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

No TODO comments were found.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Code Coverage

Package Line Rate Branch Rate Complexity Health
Beutl.Utilities 73% 67% 358
Beutl.Language 1% 50% 1119
Beutl.Configuration 40% 20% 314
Beutl.Threading 100% 93% 122
Beutl.Core 39% 35% 3070
Beutl.Engine 20% 18% 16799
Beutl.Extensibility 12% 14% 108
Beutl.Editor 82% 76% 868
Beutl.ProjectSystem 17% 10% 926
Beutl.NodeGraph 6% 2% 2490
Summary 23% (14511 / 61780) 22% (4086 / 18491) 26174

Minimum allowed line rate is 0%

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.

3 participants