Skip to content

fix: crop images to their full content and skip ones with nothing to crop [patch] - #201

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/image-crop-bounds-191
Sep 29, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/image-crop-bounds-191

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #191

Problem

ImageService.RecolorAndFindBounds computed the content size as right - left and bottom - top. right and bottom are inclusive pixel indices, which caused three problems:

  • Every processed image lost the last column and row of its content.
  • A fully transparent image gave a negative width, and Crop threw ArgumentOutOfRangeException.
  • A single opaque pixel gave a width of 0, and Crop threw the same exception.

ProcessFileWithErrorHandling caught only ImageSharp's exceptions, so either throw escaped ProcessAsync and the rest of the batch was never processed.

Fix

  • Bounds are now inclusive: right - left + 1 and bottom - top + 1.
  • RecolorAndFindBounds returns null when no pixel is opaque. That file is skipped with a yellow warning.
  • ProcessFileWithErrorHandling also catches ArgumentException for each file, for example padding the content can't fit. One bad file is skipped and the batch carries on.
  • The loaded Image<Rgba32> is now disposed.

Not changed here: the side issue from the report. Output keeps the input's extension but always holds PNG bytes. Switching to a .png extension could make foo.jpg and foo.png overwrite each other, so it needs a decision on naming and is best done separately.

Tests

These are new in ImageServiceTests and run end-to-end through ProcessAsync on generated PNGs:

  • A 4×4 opaque square in a 10×10 image comes out exactly 4×4, and its last pixel is still opaque.
  • A 6×3 strip comes out 6×6 (padded to square), and its last column and row survive.
  • A batch of a blank image, a 1-pixel image and a normal image: the blank one is skipped, the 1-pixel one gives a 1×1 output, and the normal one is still processed.

All three failed before the fix. The full suite passes: 273 of 273.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K185jro9zvzU5FDUdf5mWj


Generated by Claude Code

…crop [patch]

`image process` computed the content bounds as `right - left` and
`bottom - top`, but right and bottom are inclusive pixel indices, so
every image lost its last content column and row. A fully transparent
image produced a negative width and a single opaque pixel a zero width;
both made Crop throw an ArgumentOutOfRangeException that escaped
ProcessAsync and abandoned the rest of the batch.

The bounds are now inclusive, an image with no opaque pixel is skipped
with a warning, and an ArgumentException from one file skips that file
instead of ending the run. The loaded image is also disposed.

Fixes #191

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K185jro9zvzU5FDUdf5mWj
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit fe69d0e into main Sep 29, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/image-crop-bounds-191 branch September 29, 2026 01:06
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.

image process crops off the last pixel row and column, and one fully transparent or single-pixel image aborts the whole batch

1 participant