Files
whoshue/README.md
T
2026-09-30 23:13:20 +02:00

167 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# whoshue
A pixel-art color analyzer for KDE/Wayland. Capture a game/application window,
select part of its preview, and inspect the captured colors. The analyzer is a
normal desktop window; it does not flash calibration colors or hide itself.
## Run
On CachyOS/Arch with KDE, the capture backend uses `xdg-desktop-portal`,
`xdg-desktop-portal-kde`, and PipeWire. Building needs Rust, a C toolchain,
`pkgconf`, Clang/libclang, and PipeWire development libraries (`libpipewire`).
```sh
cargo run --release
# Or start with a local image, without requesting screen sharing:
cargo run --release -- /path/to/screenshot.png
```
1. Click **Choose source…**. KDE handles selecting a window, screen, or region.
Click it again to choose a different source.
2. Drag a rectangle in the source preview. Capture display pauses during the
drag, then returns to its previous live/frozen state. The crop is stored in
source pixels and resets if the source dimensions change.
3. **Freeze** holds the displayed frame and analysis; **Resume** takes the newest
available frame. **Stop sharing** closes capture and keeps the last image.
4. Inspect the nearest-neighbor zoomed crop. Zoom ranges from 0.1× to 16×,
starting at 1×. Hover for source coordinates,
RGB/hex and HSV; click to copy hex. Click palette swatches to copy them too.
5. **Save crop PNG** writes a uniquely named PNG in the system temporary directory
and displays its path. Drop an image or click **Open image…** to browse for a file in KDE's file picker.
## Color grouping and mapping
**Group similar colors** is enabled by default in the **Palette**. Nearby shades
from scaling or compression share a row showing their combined percentage and
shade count. Adjust **Tolerance** (default 8, range 0–32) to control the maximum
difference in each RGB channel from the group's most frequent color. Groups do
not chain together through intermediate shades. Turn grouping off or set tolerance
to zero for the exact palette. Hover a row to highlight its pixels in magenta in
the crop preview; this temporary highlight is never exported.
Freeze a capture or import an image, then click **Map** beside a palette row.
In **Color mapping**, click its replacement swatch to choose another color or enter
RGB values. Every original shade in that group maps to the chosen replacement.
Add multiple mappings to try a new palette. Each mapping keeps its original group
members even if you later change tolerance or crop. If a new mapping overlaps an
older one, the new mapping owns those shades; the older mapping keeps any others.
Replacements are simultaneous, so swapping two colors works without cascading.
Alpha is preserved and fully transparent pixels are left untouched.
Toggle **Preview mappings** to compare with the original palette. Remove an
individual mapping with **×** or use **Reset mappings**. Mappings survive crop
changes, pause during live capture, and return when frozen again. Opening a new
image or choosing a new capture source clears them.
Mappings apply before the preview adjustments or vision filter. Source preview,
analysis, copied pixel values, and **Save crop PNG** still use the original.
**Save mapped crop PNG** exports the crop with replacements, preserving alpha
and excluding preview adjustments and filters.
## Color analysis and vision checks
The **Color analysis** tab has four distributions:
- **Hue**: HSV hue in 36 ten-degree bins; saturation below 5% counts as neutral.
- **Saturation**: HSV saturation, from neutral to fully saturated.
- **Brightness (V)**: HSV value, the maximum RGB channel. This is not perceived
lightness: fully saturated red and green both have 100% value.
- **Luminance**: relative luminance in linear sRGB, weighted for contrast.
Saturation, value, and luminance use 20 five-percentage-point bins, including
100% in the final bin. Each has a mean. Switch between pixel frequency and equal
weight per distinct RGB color; hover bars for counts and percentages. The exact
palette is ordered by count, then RGB for deterministic ties. Fully transparent
pixels are excluded; partially transparent pixels retain their stored RGB values
and count once. Pixel inspection includes HSV and relative luminance.
Use **Filter** below the selection to apply protanopia, deuteranopia,
tritanopia, or grayscale directly to the crop preview, or select **Original**
to use the live color adjustment sliders. The first three use the
Machado–Oliveira–Fernandes full-severity model, applied in linear RGB and then
encoded back to sRGB. Grayscale uses relative luminance and tests removal of color
cues; it does not model all aspects of achromatopsia. Preview mode does not change
histograms, copied pixel values, or the exported PNG: those always use the original.
**Invert** replaces each RGB channel with its complement (255 minus the channel).
With **Original** selected, adjust **Hue shift** (−180° to +180°), **Saturation**
(−100% to +100% relative change), and **Brightness (V)** (−100 to +100 percentage
points of HSV value). Changes update the crop preview immediately, including live
captures. Saturation keeps neutral colors neutral; saturation and value clamp to
their valid ranges. Brightness adjusts HSV value, not relative luminance.
**Reset adjustments** restores the unmodified preview. Adjustments are remembered
while another filter is selected and applied again when returning to Original.
Filters preserve alpha and always start from source pixels, so edits do not
accumulate rounding errors.
**Vision check** automatically checks all four scenarios. It flags pairs whose
OKLab distance falls from at least 0.08 to at most 0.04, losing at least 50% of the
original separation. These are project-specific screening heuristics, not
validated visibility thresholds or WCAG criteria. Each scenario shows original
and simulated swatches, numeric hex values, and up to six closest candidate pairs.
The preview button opens the corresponding simulation for the current crop.
The check is bounded to the 64 most frequent **opaque** colors and reports both
color and pixel coverage. Partially/fully transparent pixels have no known
backdrop and are excluded from this check. No findings does **not** establish
accessibility. The check cannot infer adjacency, text/background relationships,
or the gameplay meaning of a color. Review important cues in context and combine
color with shapes, labels, or differences in lightness. Select a smaller region
when an important detail is outside the reported palette coverage.
References:
- [Chrome's contrast audits and vision simulations](https://developer.chrome.com/docs/chromium/cvd)
- [Machado, Oliveira & Fernandes simulation model](https://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html)
- [Published matrix coefficients in QGIS](https://api.qgis.org/api/qgsprevieweffect_8cpp_source.html)
- [OKLab color space](https://bottosson.github.io/posts/oklab/)
- [W3C: use of color](https://www.w3.org/WAI/WCAG22/Understanding/use-of-color.html)
Choosing a source always opens KDE's picker. The last portal token is saved at
`$XDG_CONFIG_HOME/whoshue/config.toml` (normally `~/.config/whoshue/config.toml`)
for the command-line capture diagnostic. No capture starts until **Choose source…**
is clicked. Cancelling the file picker leaves the current image/capture unchanged.
Portal requests share one application-lifetime async runtime. PipeWire runs on a
separate blocking worker so restarting capture or opening another portal dialog
cannot strand the cached D-Bus connection on a stopped executor.
## Limits and next work
- Window capture is preferred: screen/region capture includes overlapping windows,
including whoshue if placed inside the captured area.
- Selection follows coordinates within the source, not a moving sprite.
- Analysis uses source pixels before preview scaling, but these are rendered
colors, not necessarily original game-asset colors. HDR and color-profile
conversion have not been validated; the current path assumes ordinary SDR RGB.
- Live analysis refreshes at most five times per second. Large selections with
many unique colors are more expensive; release builds are recommended.
- Game-specific behavior on focus loss/minimization needs desktop testing.
- Perceptual OKLCH views, color/hue highlighting, and comparing saved selections
are future features; the distributions currently use HSV and relative luminance.
## Validation
```sh
cargo test --offline --locked
cargo clippy --offline --locked --all-targets -- -D warnings
cargo fmt --check
```
Tests cover hue boundaries, neutral/transparent pixel handling, weighting,
cropping, selection coordinates, pixel-format conversion, frozen-frame behavior,
resize handling, all histogram/vision UI views, file-picker cancellation/reopening, and
repeated portal requests during/after capture. The portal regression test requires
`dbus-run-session` and permission to create a local socket; it uses its own private
bus and does not open desktop dialogs. Color tests cover histogram endpoints and
weighting, sRGB gamma, simulation reference colors, alpha handling, warning
positives/negatives, palette coverage, and preserving source pixels.
Actual portal capture requires a running
Wayland desktop and permission through KDE's picker.
For a capture-only diagnostic (saves the first full source frame):
```sh
cargo run -- --selftest /tmp/whoshue-selftest.png
```