Fix icon cropping bugs, harden batch processing, add test suite - #66
Merged
Merged
Conversation
Fixes four defects in the image pipeline: - The crop used `right - left` on bounding-box indices that are inclusive, so every icon lost its rightmost column and bottom row. Now `right - left + 1`. - A fully transparent input left the bounds inverted, producing a negative crop width and an unhandled ArgumentOutOfRangeException that aborted the whole batch. ProcessImage now emits a fully transparent square instead. - The output directory was not created if it did not already exist. - Output was always PNG-encoded but reused the input file name verbatim, so a .jpg input produced a .jpg-named file containing PNG bytes. The extension is now rewritten to .png. Batch processing no longer aborts on unexpected failures. A per-file error of any type is reported with its exception type name and skipped, so a locked or malformed file costs one icon rather than the whole run. ProcessDirectory returns a BatchResult carrying the written and failed counts, and the summary line reports both. Exit codes now reflect the outcome: 0 for success, 1 for unusable arguments, 2 when the batch completed with failures. Both a failed batch and a missing required option previously exited 0. Adds IconHelper.Test with 48 MSTest tests covering argument validation, the pixel pipeline, the file I/O layer, and the exit code mapping. Includes gold master characterization tests that run committed fixtures through the real ProcessDirectory path and compare pixel-for-pixel, with regenerate.ps1 to update them and guard tests that fail if a fixture or expectation goes unreferenced. Restructuring needed to make the code testable: Run is split into internal ProcessDirectory and ProcessImage, reached via InternalsVisibleTo. Verified byte-identical output before and after the split. Also removes a dead local and its unused PointF alias, drops the unused ktsu.CaseConverter reference, and disposes the per-file Image that was leaking one instance per file. Documents the recolouring algorithm inline, including the ImageSharp BlackWhite colour matrix it depends on. Rewrites README.md, which previously described an API (IconLoader, IconConverter, IconResolution, IconColorizer) that has never existed in this repo, and adds CLAUDE.md. Claude-Session: https://claude.ai/code/session_01Ruh9nAQiuU5VGHV8uJCXtu
|
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Summary
Fixes four defects in the image pipeline, stops one bad input from killing a batch, makes exit codes meaningful, and adds the repository's first test suite.
Bug fixes
right - left, butrightandbottomare the inclusive indices of the last opaque pixel, so the span needs+ 1. Every icon was losing its rightmost column and bottom row.ArgumentOutOfRangeExceptionthat aborted the run and lost every icon queued behind it.ProcessImagenow emits a fully transparent square instead..jpginput produced a.jpg-named file containing PNG bytes. The extension is now rewritten to.png.Batch resilience
A per-file failure of any type is now reported with its exception type name and skipped, so a locked or malformed file costs one icon rather than the whole run. Previously only three ImageSharp decode exceptions were caught and anything else propagated.
ProcessDirectoryreturns aBatchResultof written and failed counts, and the summary line reports both.Exit codes
0--helpand--version12Both a failing batch and a missing required option previously exited
0.Tests
Adds
IconHelper.Testwith 48 MSTest tests covering argument validation, the pixel pipeline, the file I/O layer and the exit code mapping.The gold master tests run committed fixtures through the real
ProcessDirectorypath and compare pixel-for-pixel against committed expectations.regenerate.ps1updates them, and two guard tests fail if a fixture or expectation goes unreferenced. Comparison is pixel-based rather than byte-based so an ImageSharp version bump does not cause false failures, with PNG bit depth and colour type asserted separately.Supporting changes
Runis split into internalProcessDirectoryandProcessImage, reached viaInternalsVisibleTo, so the logic is testable. Output was verified byte-identical before and after that split. Also removes a dead local and its unusedPointFalias, drops the unusedktsu.CaseConverterreference, and disposes the per-fileImagethat was leaking one instance per file.Documents the recolouring algorithm inline, including the ImageSharp
BlackWhitecolour matrix it depends on. RewritesREADME.md, which described an API (IconLoader,IconConverter,IconResolution,IconColorizer) that has never existed in this repo, and addsCLAUDE.md.Test plan
dotnet build -c Releaseclean, 0 warnings with warnings as errors enableddotnet test -c Releasepasses 48 of 48Note for reviewers
Every gold master expectation changed, because the crop fix alters the output of every icon. Sizes grew by one pixel where the result was not already clamped to the maximum. That is expected and the images are worth a look.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ruh9nAQiuU5VGHV8uJCXtu