mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
fix/transform-commit-on-switch
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c7d9ade95b |
Select Subject: keep subject parts coloured like the backdrop, leave enclosed gaps open (#4462)
* Raw: measure the Nikon D90 image area (4288x2848 at (10, 10)) Aligning the decoded D90 sensor plane with the camera's own JPEG puts the image at (10, 10) in the 4352x2868 sensor plane, giving the camera's 4288x2848 frame (10-pixel margins; the right border reads the camera's flat 255 from column 4310). Add the measured ImageArea so a D90 NEF opens on the camera's frame instead of the uncropped plane, and make a measured profile leave a file that carries its own colour calibration (a DNG) untouched. * Codecs: open Cineon (.cin) film frames read-only Cineon is the 10-bit logarithmic film-scan format DPX grew from. Read it from the same Kodak header family: the generic header (big-endian, orientation at 192, channel count at 193, the channel-1 bits/dimensions), one or three channels, three 10-bit samples packed into each 32-bit word on a word boundary. The 10-bit code values open as U16 (round(c x 65535 / 1023)); the file's log black/white points are a display choice and are not applied. A depth other than 10 bits or a channel count other than 1/3 is Unsupported; truncation errors rather than panicking. Read-only (no Cineon writer), so .cin joins the open filters and Cineon read closes part of #1049's G10 gap. * PSD: write tiff blocks without copies, inflate merged ZIP once - ImageSourceData::to_bytes writes the global mask and global blocks straight from the model with 4-byte alignment instead of cloning each block (data included) to set its padding. Same bytes. - The tiff transcoder's nested regions and blocks write into the parent's buffer (taken and handed back, error or not) instead of building their own output and copying it up per nesting level. Bodies that fail or are not transcoded are truncated before being copied as stored or dropped. Same bytes. - PsdFile::from_bytes_and_merged decodes the merged image with the parse when asked: a ZIP stream is inflated once (the decode's stream check is the parse's); the parse-only check runs only when the decode fails, to keep from_bytes' errors. PSD import uses it with merged_needed. - PsdBuilder::composite documents that Gray/CMYK composites stay straight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Content-Aware Fill: measure a thin hole by its thickness, not its bounds (#1366) Removing long, thin objects (coiled guitar strings, wires) left their outline as darker and lighter blotches and copied pieces of other strings into the fill. The pyramid shrank until the hole's bounding box was four patches across: for a string across the image that is a level where the hole has swallowed its surroundings, so the matches found there copy whatever looks like the blur, and the finer levels (refine-only for wide bounds) keep them. A hole at most a quarter as thick as its bounds are long now counts as thin: the pyramid stops while it is still about two patches thick, and that level starts from a membrane across it (over a few pixels, the surroundings themselves) instead of the onion peel, whose exhaustive search is what made a shallow pyramid unaffordable. Compact holes are unchanged bit for bit. Refs #1366 * Content-Aware Fill: every part of a thin selection keeps its surroundings (#1366) Two more causes of ghosts when removing strings and the small knobs selected with them: * Auto sampling summarises the selection's surroundings by a few clusters. Around a long selection those come from the strings' surroundings, so a small part elsewhere (a tuning knob in a differently coloured corner) lost its own surroundings and was filled from far away in another colour. Around a thin selection the Auto area now always reaches as far as the selection is thick. * The membrane that starts a thin hole now starts a patch radius out, past the anti-aliased fringe or halo that a selection drawn close to the object leaves outside it. Refs #1366 * Content-Aware Fill: peel a thin hole where that stays affordable (#1366) Starting every thin hole from a membrane smoothed Spot Healing on film grain (spot_healing_keeps_film_grain_through_overlapping_strokes: grain 9.0 against 12.1 around). The onion peel copies the grain; it was only given up because its exhaustive search is too slow on the big level a long thin hole stops at. It now starts the thin hole whenever hole cells times sources stay within 2^28 comparisons, and the membrane only beyond. Refs #1366 * Select Subject: keep subject parts coloured like the backdrop, leave enclosed gaps open Users on #2788 reported Select Subject dropping clothes that are close in colour to a plain wall and bridging the gap between an arm and the torso. Both come from the same place: the saliency pass labels low-contrast parts of the subject as background, the background colour model then learns them and explains them away, and the final blind hole fill closes every enclosed gap up to 5 % of the mask. Two changes in crates/algo: - A second GrabCut pass whose background model only knows the backdrop. A colour model fitted to the image frame (minus a margin around the salient blob, in case the subject runs out of the picture) marks every pixel it explains as probable background; the blob and whatever the backdrop cannot account for start as probable foreground. Only components that touch the first pass's result survive. - Hole filling that consults the colour models: specks (<= 0.2 % of the mask) always fill, larger holes up to 5 % fill only when their pixels fit the foreground model better than the background's. Tests: two regressions (a cream-shouldered figure on a near-white wall keeps its shoulders; a ring keeps its backdrop-coloured hole open), plus ignored timing and picture-dump helpers. The 6 MP first run goes from about 100 ms to about 200 ms on one core; the P29 repeat path is untouched. * Keyboard shortcut sets: Set menu, New Set, Save, Delete, .kys Export and Summarize (K2) Edit › Keyboard Shortcuts gets Photoshop's set management. A set is a saved copy of the bindings (Preferences.shortcutSets, the one in use in shortcutSet; "" is PhotoCraft Defaults), and the Set menu shows "(modified)" when the bindings differ from it. New Set…, Save and Delete wait for OK like the rest of the dialog. Export… writes the dialog's bindings as a .kys file (commands then tools, Photoshop's macOS key spelling), each row carrying our id as pc-id so PhotoCraft imports its own sets back exactly, whatever the label; Summarize… writes Photoshop's HTML list of every bound command by menu. edit.keyboardShortcuts takes useSet (before the call's edits), saveSet, deleteSet, sets and activeSet (after them), exportKys and summarize; every reply reports the sets, the set in use and whether it is modified. Bad names, keys or counts are errors that change nothing. * .kys import: tool keys and sets saved on Windows (K3) Photoshop's <tool> rows now import: each matches our tool by name (our tool names are Photoshop's) or by pc-id in sets PhotoCraft exported, and a letter other than the tool's own becomes its override, while its own letter keeps the tool cycling with the others (#5036). A set saved on Windows spells the primary modifier Ctrl where a Mac set writes Cmd and keeps Control for the Control key. A set with no Cmd or Opt anywhere but some Ctrl is read as a Windows set, its Ctrl as our Cmd: Ctrl+L is the Cmd+L default, not a Control-key override that would also mean Control on a Mac. The import reports `windows`. * cargo fmt the extension lists Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Vikram Bhaskaran <vikram.bhaskaran@outlook.com> Co-authored-by: vangel76 <vangel1976@googlemail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Gert Pellin <gert@pellin.be> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Tanish Manku <tmanku.dev@gmail.com> |
||
|
|
de22bca760 |
Remove tool: Create a new layer, and Remove after each stroke off (#1750) (#3808)
paint.remove takes several strokes at once (`strokes`) and fills them as one job and one undo step, so the fill sees every hole together; and `newLayer` writes the fill to a new layer above the target, only where the hole is, leaving the target as it was (with Sample All Layers it fills from the composite). The options bar shows Photoshop's three options; with Remove after each stroke off, strokes collect as a tint on the canvas until Enter or the ✓ applies them and Esc or ⊘ drops them. Strokes are dropped when the tool or document changes under them. |
||
|
|
18f787f04d |
SVG: say when text had no font to use instead of opening an empty drawing (#3168)
With no font installed at all (a bare CI image, a minimal container) usvg drops every <text> while parsing, so nothing reaches the conversion and the file opened as "nothing to draw". The parser now notes when a drawing has text and the font database has no faces, and the import warns that the text needs a font and was dropped. Fixes the FreeBSD svg test, which has been red since the reader landed. |
||
|
|
d2ad238e82 |
PSD type layers without pixels are rendered on import; PostScript font names resolve to the installed family (#1406)
* PSD type layers without pixels are rendered on import; PostScript font names resolve to the installed family (#1405) ag-psd, GIMP and other writers leave a PSD type layer without image data and let Photoshop re-render it on open. The importer made those empty pixels the layer's cache, so the canvas showed nothing until the layer was edited, and the family guessed from the PostScript name ("CC Wild Words" for CCWildWords-Regular) never got corrected to the installed family ("CCWildWords"), so Missing Fonts replaced an installed font. - crates/io/src/psd_import.rs: a type layer's record pixels are the cache only when there are some. - crates/io/src/text_import.rs (new, run by import): resolves each run's PostScript name with FontDb::resolve_postscript, as the layout does, and writes the installed family, weight and italic back into the run; renders every type layer that has no cache from its model. - crates/io/tests/psd_type_bounds.rs: a type layer exported without pixels imports rendered, on the engine's own pixels; "JetBrainsMono-Regular" (guess "Jet Brains Mono") imports as the family "JetBrains Mono". Fixes #1405 * PSD import: cap the automatic type render for untrusted sizes text_import::prepare now renders a type layer without pixels only when its ink fits MAX_RENDER_PIXELS (64 Mpx); the renderer's own guard is 256 Mpx, which for an absurd type size or scale in a hostile PSD meant several GB of f32 accumulation buffers on open. Over the cap the layer keeps empty pixels until edited, as before. Test: hostile_type_sizes_without_pixels_import_without_a_huge_render (an ink between the two caps, plus 1e30 pt, a 1e12 scale and f64::MAX). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * rustfmt (module order, the hostile-size test) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PSD import: keep empty type pixels when the engine text is blank cargo xtask test-corpus: rendering every pixel-less type layer regressed the Photoshop oracle text/path-wave-open.psd (133 -> 132, below the floor). Photoshop wrote that layer with no pixels because its EngineData text is blank (the characters sit only in the descriptor's Txt ): it draws nothing, and the import must not draw it either. New photocraft_text::psd:: engine_text_is_blank; the oracle passes again (133). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |