Skip to content

Optimize bitmap operations#78

Merged
meesoft merged 10 commits into
mainfrom
features/OptimizeBitmapOps
Apr 17, 2026
Merged

Optimize bitmap operations#78
meesoft merged 10 commits into
mainfrom
features/OptimizeBitmapOps

Conversation

@meesoft
Copy link
Copy Markdown
Owner

@meesoft meesoft commented Apr 5, 2026

Summary by CodeRabbit

  • UI Improvements

    • Refined crop control border color threshold for improved visibility.
  • Performance Enhancements

    • Optimized multiple bitmap processing and resampling paths for faster, lower-overhead image operations.
    • Reduced allocations and improved inner-loop efficiency in bitmap conversions and resizing.
  • Bug Fixes

    • Improved handling of single-frame max accumulation to produce correct results.
  • Tests

    • Expanded and parameterized bitmap and max-frame tests to cover more formats and scenarios.

@meesoft meesoft force-pushed the features/OptimizeBitmapOps branch from 97d643c to 33997d4 Compare April 8, 2026 20:49
@meesoft meesoft force-pushed the features/OptimizeBitmapOps branch from c69f683 to 3ad7470 Compare April 10, 2026 20:48
@meesoft meesoft marked this pull request as ready for review April 15, 2026 19:16
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Warning

Rate limit exceeded

@meesoft has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 41 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 34 minutes and 41 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c8077df8-e218-4612-bec1-679c8a138fe1

📥 Commits

Reviewing files that changed from the base of the PR and between 09be5b4 and fe8c91b.

📒 Files selected for processing (1)
  • PhotoLocatorTest/BitmapOperations/ColorToneAdjustOperationTest.cs

Walkthrough

Refactors bitmap processing and tests: color-tone HSI math adjusted, float-bitmap conversion and statistics inner loops optimized, Lanczos resampler weight storage and fixed-point apply rewritten, MaxFramesOperation adds single-frame fast path, CropControl contrast threshold tweaked, and tests expanded/parameterized.

Changes

Cohort / File(s) Summary
Color & Tone
PhotoLocator/BitmapOperations/ColorToneAdjustOperation.cs
Rewrote RGB→HSI math: use Math.Clamp, compute saturation from explicit min, switched sqrt operand formulation for hue.
Float bitmap & pixel conversions
PhotoLocator/BitmapOperations/FloatBitmap.cs
Renamed row pointers to dstRow/srcRow; use local width/size locals in inner loops; changed LUT lookup in ToPixels8 to explicit index clamping; CreateDeGammaLookup made internal static; Min/MinMax/Mean now process floats in unrolled 8-element blocks via pointers.
Resampling / Lanczos
PhotoLocator/BitmapOperations/LanczosResizeOperation.cs
Precomputed taps refactored from struct array to parallel SourceIndices[]/SourceWeights[]; weights rounded to fixed-point ints; Apply rewritten to use split arrays with unrolled accumulation and fixed-point normalization; input buffer rented from ArrayPool<byte>.Shared and returned after BitmapSource.Create.
Frame accumulation
PhotoLocator/BitmapOperations/MaxFramesOperation.cs
Added special-case fast path when ProcessedImages == 1 that parallel-copies prepared pixels into accumulator and returns early.
UI
PhotoLocator/Controls/CropControl.xaml.cs
Adjusted CropBorderColor selection threshold: pixelMean > 0.20pixelMean > 0.25.
Tests
PhotoLocatorTest/BitmapOperations/FloatBitmapTest.cs, PhotoLocatorTest/BitmapOperations/MaxFramesOperationTest.cs
Parameterised and added tests for FloatBitmap.Assign, Bgr32 handling, ToBitmapSource, Mean, and reworked Min/Max; added MaxFramesOperation test cases exercising multi-frame max accumulation and result readiness.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Optimize bitmap operations' accurately describes the primary focus of the pull request, which contains multiple performance optimizations across bitmap operation files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch features/OptimizeBitmapOps

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
PhotoLocator/BitmapOperations/ColorToneAdjustOperation.cs (1)

128-128: Optional hot-path cleanup: precompute channel deltas.

On Line 128, you can reduce repeated arithmetic and make the expression easier to scan by precomputing deltas.

♻️ Optional refactor
-                    var a = 0.5 * (r - g + (r - b)) / Math.Sqrt((r - g) * (r - g) + (r - b) * (g - b));
+                    var rg = r - g;
+                    var rb = r - b;
+                    var gb = g - b;
+                    var a = 0.5 * (rg + rb) / Math.Sqrt(rg * rg + rb * gb);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PhotoLocator/BitmapOperations/ColorToneAdjustOperation.cs` at line 128, In
ColorToneAdjustOperation (the method that computes 'a'), precompute channel
deltas like dr = r - g, db = r - b, dg = g - b and use those variables in the
expression for var a to avoid repeating (r - g), (r - b), and (g - b); update
the numerator and denominator to use dr, db, dg and ensure the Math.Sqrt uses
dr*dr + db*dg so behavior is unchanged while the expression becomes clearer and
faster.
PhotoLocator/BitmapOperations/LanczosResizeOperation.cs (1)

232-237: Always write the clamped pixel value.

Line 232 currently makes correctness depend on dest being zero-initialized by the caller. Writing 0 explicitly keeps LineResamplerFixedPoint.Apply self-contained if the destination buffer strategy changes later.

Suggested tweak
-                    if (sum > 0)
-                    {
-                        sum >>= 16;
-                        if (sum > 255)
-                            sum = 255;
-                        dest[dstOffset + i * dstSampleDistance] = (byte)sum;
-                    }
+                    if (sum > 0)
+                    {
+                        sum >>= 16;
+                        if (sum > 255)
+                            sum = 255;
+                        dest[dstOffset + i * dstSampleDistance] = (byte)sum;
+                    }
+                    else
+                    {
+                        dest[dstOffset + i * dstSampleDistance] = 0;
+                    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PhotoLocator/BitmapOperations/LanczosResizeOperation.cs` around lines 232 -
237, The loop in LineResamplerFixedPoint.Apply (LanczosResizeOperation.cs) only
writes dest[dstOffset + i * dstSampleDistance] when sum>0, relying on dest being
pre-zeroed; change it to always compute sum >>= 16, clamp sum to the 0..255
range (if sum < 0 set to 0, if sum > 255 set to 255) and then unconditionally
assign dest[dstOffset + i * dstSampleDistance] = (byte)sum so the method is
self-contained regardless of caller buffer initialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PhotoLocator/BitmapOperations/LanczosResizeOperation.cs`:
- Around line 312-318: The code currently rents srcPixels and may return it to
the ArrayPool before creating the BitmapSource, which causes a use-after-return
if Apply returns the same buffer (and also leaks on exceptions); fix by wrapping
the rent/CopyPixels/Apply/Return sequence in a try/finally so Return always
runs, detect aliasing between srcPixels and dstPixels after Apply (compare
references) and if they alias allocate a new buffer, copy the pixel data into
it, and pass that new buffer to BitmapSource.Create; ensure you only Return
buffers you rented (srcPixels) and do not Return the buffer passed into
BitmapSource.Create if you copied it for lifetime reasons (or otherwise document
ownership), referencing srcPixels, dstPixels, Apply, BitmapSource.Create,
ArrayPool<byte>.Shared.Rent/Return and CopyPixels so the changes are applied in
the correct spots.

---

Nitpick comments:
In `@PhotoLocator/BitmapOperations/ColorToneAdjustOperation.cs`:
- Line 128: In ColorToneAdjustOperation (the method that computes 'a'),
precompute channel deltas like dr = r - g, db = r - b, dg = g - b and use those
variables in the expression for var a to avoid repeating (r - g), (r - b), and
(g - b); update the numerator and denominator to use dr, db, dg and ensure the
Math.Sqrt uses dr*dr + db*dg so behavior is unchanged while the expression
becomes clearer and faster.

In `@PhotoLocator/BitmapOperations/LanczosResizeOperation.cs`:
- Around line 232-237: The loop in LineResamplerFixedPoint.Apply
(LanczosResizeOperation.cs) only writes dest[dstOffset + i * dstSampleDistance]
when sum>0, relying on dest being pre-zeroed; change it to always compute sum
>>= 16, clamp sum to the 0..255 range (if sum < 0 set to 0, if sum > 255 set to
255) and then unconditionally assign dest[dstOffset + i * dstSampleDistance] =
(byte)sum so the method is self-contained regardless of caller buffer
initialization.
🪄 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: f80389db-2416-48ed-b874-b21a525f0a50

📥 Commits

Reviewing files that changed from the base of the PR and between ad8641a and d011ae4.

📒 Files selected for processing (7)
  • PhotoLocator/BitmapOperations/ColorToneAdjustOperation.cs
  • PhotoLocator/BitmapOperations/FloatBitmap.cs
  • PhotoLocator/BitmapOperations/LanczosResizeOperation.cs
  • PhotoLocator/BitmapOperations/MaxFramesOperation.cs
  • PhotoLocator/Controls/CropControl.xaml.cs
  • PhotoLocatorTest/BitmapOperations/FloatBitmapTest.cs
  • PhotoLocatorTest/BitmapOperations/MaxFramesOperationTest.cs

Comment thread PhotoLocator/BitmapOperations/LanczosResizeOperation.cs
@meesoft meesoft merged commit fc14fa9 into main Apr 17, 2026
5 checks passed
@meesoft meesoft deleted the features/OptimizeBitmapOps branch April 17, 2026 19:44
@coderabbitai coderabbitai Bot mentioned this pull request Apr 26, 2026
@coderabbitai coderabbitai Bot mentioned this pull request May 10, 2026
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