Skip to content

feat(images): pixelate or blur parts of a picture before sharing it - #684

Merged
KnifeLemon merged 3 commits into
mainfrom
feat/hide-areas
Oct 27, 2026
Merged

KnifeLemon merged 3 commits into
mainfrom
feat/hide-areas

Conversation

@KnifeLemon

Copy link
Copy Markdown
Owner

What does this change?

A new tool, Pixelate / blur, covers parts of a picture (names, faces, phone numbers in a screenshot) before it is shared.

Dropping pictures on its slice opens them one at a time in a window drawn like the Crop window (same stage, file chip, strip of files, toolbar pill, keycaps, summary card):

  • Drag on the picture to draw a frame. Each frame shows the picture already pixelated or blurred inside it while it is being drawn or dragged (the preview is covered once per choice and drawn clipped to the frames, so dragging costs nothing).
  • Draw as many frames as needed. The selected one has Crop's accent outline, corner dots, edge pills and size chip; the others a lighter outline that turns full under the pointer. Drag a frame to move it (it snaps to the centre lines like Crop's), drag a handle to size it, or use the mouse wheel.
  • Pixelate (squares of the average colour) or Blur, and a strength slider. The choice is kept in the tool, so the window opens with it next time.
  • Keys: Enter saves, Esc closes, Delete removes the selected frame, Tab / Shift+Tab go from frame to frame, the arrow keys move it (Shift × 10), R clears every frame, P / B pick pixelate or blur, + / − change the strength, Ctrl+Z / Ctrl+Y undo and redo adding, moving, sizing, removing and clearing.
  • A picture without frames shows "Drag over what you want to hide", and Save stays off until there is a frame.
  • Several pictures work like Crop: a strip of thumbnails, Hide & Next / Skip, a summary at the end when a picture couldn't be opened (animated and multi-page pictures, as in Crop). The frames carry on to the next picture, as Crop carries the ratio: screenshots of one app put names in the same place.

Each picture is written by an ordinary conversion (like Cut a clip), in its own format, or PNG when that can't be written (camera RAW, DICOM …), named {name}_hidden. The toast, cancelling, the output rules and the history work as for any other preset.

Destroying what is covered

  • Pixelate averages squares laid on a grid from the picture's corner (moving a frame never changes the grid, two frames that meet look like one). Averaging is many-to-one.
  • Blur averages into squares of a third of its width first, then blurs with a Gaussian about a line of text wide. A light blur can be undone by sharpening, so it is never used.
  • Both are sized from the picture's shorter side: 2 % at the weakest to 6.5 % at the strongest (4 % by default), at least 8 px. At the default a square is as tall as a line of a desktop's or a phone's screenshot text. Squares much smaller than the letters leave text guessable, because text in a known font can be pixelated the same way and compared.
  • The job queue covers the parts once, in the first step whose engine applies image options (as for rotation). A route without such an engine fails with "Only pictures can be pixelated or blurred" instead of writing the picture uncovered. "Only if smaller" never puts the original back, and a failed job keeps no Retry (an older Filee would run it without the parts).
  • The small previews a file carries of the picture as it was (EXIF thumbnail, Photoshop's 8BIM, XMP thumbnails) are removed.
  • Applied with Magick.NET on the parts only, upright (after the EXIF orientation), before any turn or resize.

Donut placement and settings schema

The images donut's front has 10 of 12 slices on main (#617 adds Edit pictures). The tool goes on the front after Half size, after Edit pictures when that follows it, so the front has 12 once both are merged.

Settings schema 52 adds it to existing libraries: after Crop in the library, and on the front after Half size when the front has room. A full front puts it at the end of the back face when that has room. A front without Half size (laid out by the user) is left alone. Like schema 45 did for the clip tool, it repairs the tool if Filee 2.0 saved it as a crop. Main is at 50 and #617 takes 51, so 52 may need renumbering when these are merged.

Compatibility with 2.1.0

presets.json stays readable by Filee 2.1.0. Pixelate / blur and the strength are new fields of their own (hideStyle with LenientEnumJsonConverter, hideStrength), so no existing enum gets a new value. 2.1.0 skips the fields and greys out the tool, as a tool it doesn't know. The parts themselves (hiddenAreas) only exist on the conversion a window starts, never on a saved preset.

Small changes around it

  • CropCanvas shares its stage, handle and easing drawing (internal, no change in what it draws).
  • CropQueueItem takes the name of its done state.

Type of change

  • Bug fix
  • New feature or improvement
  • New format or engine
  • Translation
  • Documentation
  • Build, CI or dependencies

How was it tested?

  • dotnet build Filee.slnx -c Release, dotnet format --verify-no-changes
  • dotnet test: Core 678, Engines 1833 and CLI 39 pass. The App suite passes, with the images donut's slice count and the palette's Edit chips updated for the new slice.
    • HideToolTests (Core): effect size per strength and picture size, part to pixels (rounded outwards, clamped), the square a pixel falls in, the conversion, the remembered choice, JSON (saved by name, unknown style read leniently, no parts on a saved tool, existing values unchanged for 2.1.0), no Retry, one-time covering on a two-step route, refusal without an engine that covers, "only if smaller", and the schema 50 → 52 migration (front, after Edit pictures, full front → back, both full, user's own place, repair after 2.0).
    • HideAreasTests (Engines, real ImageMagick): squares are their exact average and nothing outside changes; even the weakest blur leaves random noise within a few levels; a 1.5 px blur keeps 4 px strokes while the weakest real blur leaves at most a level; JPEG stays JPEG; parts land on the upright picture of an EXIF-turned photo; the window's preview equals the conversion; Photoshop/XMP previews go while EXIF data stays.
    • HideWindowTests (App, headless): drawing, moving, sizing, selecting with the mouse, live cover while dragging; every key; Enter starts one conversion with the parts; Esc saves nothing; the choice is remembered; batch with carried frames, Skip, a failing animated GIF and the summary; at the window's smallest width the toolbar drops its labels so every button stays inside (en, de, ru, ne, ar).
  • Tried it in the app: not tried on a desktop yet

Screenshots (UI changes)

Rendered by the headless tests (hide-*.png in tests/Filee.App.Tests/bin/Release/net10.0/screenshots): empty, pixelate and blur in en, ko, de, ar, ru, hi, ne, ja, light and dark, Material You and a theme.json, several files and summary, hover, drag, and the narrowest window.

Checklist

  • New UI strings are in all src/Filee.App/Assets/i18n/*.json files (17 keys, 17 languages; short donut names for long ones)
  • No new third-party code
  • The PR does one thing

🤖 Generated with Claude Code

A "Pixelate / blur" tool on the images donut opens the pictures one at a time in a window drawn like the crop
overlay: drag frames over names, faces or numbers, each one showing the picture already pixelated or blurred, move
and size them, pick Pixelate or Blur and a strength, then Hide & Save (or Hide & Next). Each picture is written by an
ordinary conversion in its own format (PNG when it can't be written), so the toast, the output rules and the history
work as usual; the frames carry on to the next picture of a batch.

Pixelate averages squares on a grid laid from the picture's corner; blur averages into smaller squares first and
then blurs about a line of text wide, so a light, reversible blur is never used. Both are sized from the picture's
shorter side. The job queue covers the parts once, in the first step whose engine applies image options, refuses a
route that can't, never puts the original back for "only if smaller", and keeps no Retry for them. The previews of
the uncovered picture a file carries (EXIF, Photoshop, XMP thumbnails) are dropped.

Settings schema 52 adds the tool after Crop and on the images donut after Half size (or on the back face when the
front is full). Pixelate or blur and the strength are remembered in the tool, in fields of their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the feature New features label Oct 27, 2026
KnifeLemon and others added 2 commits October 28, 2026 03:52
The toolbar gave up its names once they didn't fit, judged against widths that happened to fit at the window's
default size with Windows' fonts (German with Skip: 978 of 982 px). With CI's Noto fonts they didn't, so the test
that expected the names back at 1040 px failed there. The toolbar now measures each level with the fonts in use and
takes the first that fits: all names; then without the names of Clear all and Skip and the keycaps; then icons only.
The test checks that rule against the measured widths instead of assuming where names fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te / blur (52) side by side

Both tools sit after "Half size" on the images donut and after "Crop" in the library, Edit pictures first; the v52
step puts Pixelate / blur after Edit pictures when that is there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KnifeLemon
KnifeLemon merged commit bf73fe6 into main Oct 27, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant