uttrflow / uttrflow/uttrflow-swift

Every copied picture is expanded to an uncompressed TIFF and re-encoded as PNG, a transient ~190 MB for one 5K screenshot

Open
#577 0 comments 0 reactions 0 assignees View on GitHub
area:clipboard enhancement P2 performance
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## What happens

`SystemClipboardSource.image()` (`Sources/UttrflowClipboard/ClipboardSource+System.swift:28-37`) reads the picture as follows:

```swift
NSPasteboard.general.readObjects(forClasses: [NSImage.self], options: nil)?.first as? NSImage
item.tiffRepresentation // uncompressed
NSBitmapImageRep(data: tiff)
bitmap.representation(using: .png, properties: [:])
```

The pasteboard usually already holds PNG bytes: a screenshot copied with the keyboard puts `public.png` there (`Docs/panel.md` says so). Those bytes are decoded, written out as an uncompressed TIFF, decoded again and compressed again. This happens on the watcher for every picture copy, and again inside `PasteboardWatcher.claims` when a picture write was announced (`PasteboardWatcher.swift:99`). No size bound applies: `ClipboardBudget.largestClip` counts text only.

## Measured

Throwaway harness, Release build. It takes a 5120 × 2880 PNG of about 0.5 MB, turns it into an `NSImage`, and then runs the same three steps:

| step | time | size |
|---|---|---|
| `tiffRepresentation` | 56 ms | 59 MB |
| decode + PNG encode | 74 ms | 0.5 MB out |
| peak resident memory | +193 MB (131 → 324 MB) | |

## Why it matters

On an 8 GB M1 Air, each screenshot copied adds a spike of about 190 MB and more than 100 ms of processor time, even if the user never opens the panel. Copying a few screenshots in a row stacks these spikes.

## Acceptance criteria

- When the pasteboard carries PNG data, those bytes are used as they are, and the pixel size is read from the image header (for example with `CGImageSourceCopyPropertiesAtIndex`) without decoding.
- Other flavours (TIFF, HEIC and so on) are converted once, without an uncompressed TIFF in between where avoidable.
- A decision is written in `Docs/clipboard-budget.md` about whether a picture above some size is still recorded.
- The PR states before and after peak memory for one 5K screenshot, measured headlessly.

Contributor guide

Open the contributing guide

Research direction

Read Sources/UttrflowClipboard/ClipboardSource+System.swift:28-37 and PasteboardWatcher.swift:99, then compare the clipboard formats described in Docs/panel.md. Trace both image paths and measure the current headless 5K screenshot case before changing them. Done means PNG bytes avoid unnecessary decoding, other formats avoid an intermediate TIFF where possible, the size decision is recorded in Docs/clipboard-budget.md, and before/after peak memory is reported.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, swift
Domain
desktop-dev, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.