* Select and Mask: Output To New Document, with or without a layer mask (#2998) select.refineEdge takes output newDocument / newDocumentWithMask: a new document like the source (size, mode, depth, resolution, profile) with the active layer cut out by the refined edge, or with the matte as a layer mask. The source document is left untouched. The dialog's Output choice lists all six targets, translated in every catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * i18n: drop the duplicate pt-br New Layer row (main already has it) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
parent
8a72978549
commit
28874bbfad
|
|
@ -259,9 +259,10 @@ fn refine_strokes(p: &Value) -> Result<Vec<matting::EdgeStroke>> {
|
|||
}
|
||||
|
||||
/// Select and Mask: refines the selection edge, then outputs to the selection, a layer mask on
|
||||
/// the active layer, or a new layer (with or without a mask). Decontaminating colours needs a
|
||||
/// new layer, so it upgrades `selection` / `layerMask` output to `newLayerWithMask` (as
|
||||
/// Photoshop does). New-layer outputs hide the source layer.
|
||||
/// the active layer, a new layer or a new document (each with or without a mask).
|
||||
/// Decontaminating colours needs new pixels, so it upgrades `selection` / `layerMask` output to
|
||||
/// `newLayerWithMask` (as Photoshop does). New-layer outputs hide the source layer; new-document
|
||||
/// outputs leave the source document untouched.
|
||||
fn refine_edge(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let params = refine_params(p);
|
||||
let strokes = refine_strokes(p)?;
|
||||
|
|
@ -272,8 +273,8 @@ fn refine_edge(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
let default_output =
|
||||
if d.doc.selection.is_none() && active.and_then(|id| d.doc.layer(id)).and_then(|l| l.mask.as_ref()).is_some() { "layerMask" } else { "selection" };
|
||||
let mut output = p.get("output").and_then(Value::as_str).unwrap_or(default_output).to_string();
|
||||
if !matches!(output.as_str(), "selection" | "layerMask" | "newLayer" | "newLayerWithMask") {
|
||||
return Err(bad("select.refineEdge", "output must be selection|layerMask|newLayer|newLayerWithMask"));
|
||||
if !matches!(output.as_str(), "selection" | "layerMask" | "newLayer" | "newLayerWithMask" | "newDocument" | "newDocumentWithMask") {
|
||||
return Err(bad("select.refineEdge", "output must be selection|layerMask|newLayer|newLayerWithMask|newDocument|newDocumentWithMask"));
|
||||
}
|
||||
if decontaminate && matches!(output.as_str(), "selection" | "layerMask") {
|
||||
output = "newLayerWithMask".into();
|
||||
|
|
@ -315,12 +316,26 @@ fn refine_edge(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
let empty = Region { bbox: Rect::EMPTY, mask: Vec::new() };
|
||||
let reg = region.as_ref().unwrap_or(&empty);
|
||||
let pixels = if decontaminate { matting::decontaminate(src, reg, params.radius, amount) } else { src.clone() };
|
||||
let with_mask = output == "newLayerWithMask";
|
||||
let with_mask = matches!(output.as_str(), "newLayerWithMask" | "newDocumentWithMask");
|
||||
let (content, mask) = if with_mask {
|
||||
(pixels, Some(LayerMask { surface: matting::region_surface(reg), ..LayerMask::reveal_all() }))
|
||||
} else {
|
||||
(matting::masked_copy(&pixels, reg), None)
|
||||
};
|
||||
if output.starts_with("newDocument") {
|
||||
// Same canvas, colour mode, depth and profile, so the pixels need no conversion.
|
||||
let mut doc = Document::new("Untitled", d.doc.size, d.doc.mode, d.doc.depth);
|
||||
doc.resolution_dpi = d.doc.resolution_dpi;
|
||||
doc.icc_profile = d.doc.icc_profile.clone();
|
||||
doc.color_table = d.doc.color_table.clone();
|
||||
doc.duotone = d.doc.duotone.clone();
|
||||
let mut layer = Layer::new(src_layer.name.clone(), LayerContent::Raster(content));
|
||||
layer.mask = mask;
|
||||
let layer_id = layer.id;
|
||||
doc.layers.push(layer);
|
||||
let i = s.add_document(doc, None);
|
||||
return Ok(json!({ "output": output, "document": i, "layer": layer_id.0, "selected": false }));
|
||||
}
|
||||
let new_id = s.edit("Select and Mask", |doc, act| {
|
||||
let mut layer = Layer::new(name, LayerContent::Raster(content));
|
||||
layer.mask = mask;
|
||||
|
|
@ -374,7 +389,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"select.refineEdge",
|
||||
"Refine Edge",
|
||||
[],
|
||||
r##"{"radius":px=0,"smartRadius":bool=false,"smooth":0..100=0,"feather":px=0,"contrast":0..100=0,"shiftEdge":-100..100=0,"decontaminate":bool=false,"amount":0..100=100,"output":"selection|layerMask|newLayer|newLayerWithMask"="selection","sampleAllLayers":bool=false,"refineStrokes":[{"points":[[x,y],…],"size":px=30,"mode":"add|subtract"="add"},…]=[]} (refineStrokes: the Refine Edge Brush, in painting order: add refines the painted area from the image, subtract keeps the original edge there)"##,
|
||||
r##"{"radius":px=0,"smartRadius":bool=false,"smooth":0..100=0,"feather":px=0,"contrast":0..100=0,"shiftEdge":-100..100=0,"decontaminate":bool=false,"amount":0..100=100,"output":"selection|layerMask|newLayer|newLayerWithMask|newDocument|newDocumentWithMask"="selection","sampleAllLayers":bool=false,"refineStrokes":[{"points":[[x,y],…],"size":px=30,"mode":"add|subtract"="add"},…]=[]} (refineStrokes: the Refine Edge Brush, in painting order: add refines the painted area from the image, subtract keeps the original edge there)"##,
|
||||
has_selection_or_mask,
|
||||
refine_edge
|
||||
),
|
||||
|
|
@ -590,7 +605,11 @@ mod tests {
|
|||
|
||||
/// A vertical Gaussian-blurred edge at x = 40 (warm left, cool right) with the left half selected.
|
||||
fn blurred_edge_selected() -> Session {
|
||||
let mut s = session_with(80, 40, 8, |x, _| {
|
||||
blurred_edge_selected_at(8)
|
||||
}
|
||||
|
||||
fn blurred_edge_selected_at(depth: u32) -> Session {
|
||||
let mut s = session_with(80, 40, depth, |x, _| {
|
||||
let t = 1.0 / (1.0 + ((x as f32 + 0.5 - 40.0) / 1.8).exp());
|
||||
[0.9 * t + 0.1 * (1.0 - t), 0.8 * t + 0.2 * (1.0 - t), 0.2 * t + 0.6 * (1.0 - t)]
|
||||
});
|
||||
|
|
@ -665,6 +684,47 @@ mod tests {
|
|||
assert!(!s.is_enabled("select.refineEdge"));
|
||||
}
|
||||
|
||||
/// Output To New Document (with or without a layer mask) opens a document like the source,
|
||||
/// at every depth, and leaves the source document as it was.
|
||||
#[test]
|
||||
fn refine_edge_outputs_to_a_new_document() {
|
||||
for depth in [8, 16, 32] {
|
||||
let mut s = blurred_edge_selected_at(depth);
|
||||
s.edit("profile", |doc, _| {
|
||||
doc.resolution_dpi = 300.0;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
let before = s.active().unwrap().doc.clone();
|
||||
let r = s.execute("select.refineEdge", json!({"radius": 8, "output": "newDocument"})).unwrap();
|
||||
assert_eq!((r["output"].as_str(), r["document"].as_u64()), (Some("newDocument"), Some(1)), "depth {depth}");
|
||||
assert_eq!(s.active_index(), Some(1));
|
||||
let nd = &s.active().unwrap().doc;
|
||||
assert_eq!((nd.size, nd.mode, nd.depth, nd.resolution_dpi), (before.size, before.mode, before.depth, 300.0));
|
||||
assert_eq!(nd.layers.len(), 1);
|
||||
assert!(nd.selection.is_none() && nd.layers[0].mask.is_none());
|
||||
let surf = nd.layers[0].surface().unwrap();
|
||||
let a = |x: i32| surf.pixel(x, 20)[3];
|
||||
assert!(a(5) > 0.99 && a(75) == 0.0 && a(40) > 0.05 && a(40) < 0.95, "depth {depth}: {} {} {}", a(5), a(40), a(75));
|
||||
// The source is untouched: same layers, still visible, selection kept, no history step.
|
||||
let src = &s.documents()[0];
|
||||
assert_eq!(src.doc.layers, before.layers);
|
||||
assert_eq!(src.doc.selection, before.selection);
|
||||
assert_eq!(src.history.undo_label(), Some("profile"));
|
||||
|
||||
// With a layer mask: full pixels, the refined matte as the mask.
|
||||
s.execute("file.close", json!({"document": 1})).unwrap();
|
||||
let r = s.execute("select.refineEdge", json!({"radius": 8, "output": "newDocumentWithMask"})).unwrap();
|
||||
assert_eq!(r["output"], "newDocumentWithMask");
|
||||
let nd = &s.active().unwrap().doc;
|
||||
let l = &nd.layers[0];
|
||||
assert_eq!(l.surface().unwrap().pixel(75, 20), before.layers[0].surface().unwrap().pixel(75, 20), "depth {depth}");
|
||||
let m = l.mask.as_ref().unwrap();
|
||||
assert!(soft_count(&m.surface, 20) >= 4, "depth {depth}");
|
||||
assert!(m.value(5, 20) > 0.99 && m.value(75, 20) < 0.01, "depth {depth}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refine_edge_shift_and_contrast() {
|
||||
let mut s = blurred_edge_selected();
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,21 @@ mod tests {
|
|||
assert!(missing.is_empty(), "missing Korean dynamic labels: {missing:#?}");
|
||||
}
|
||||
|
||||
/// Select and Mask's Output To lists all six of Photoshop's targets, each translated.
|
||||
#[test]
|
||||
fn select_and_mask_output_choices_are_translated() {
|
||||
let spec = photocraft_engine::commands::find("select.refineEdge").unwrap().params;
|
||||
let p = parse_spec(spec).into_iter().find(|p| p.key == "output").unwrap();
|
||||
let Kind::Choice { options: choices, .. } = p.kind else { panic!("output is not a choice") };
|
||||
let labels: Vec<String> = choices.iter().map(|c| source_label(c)).collect();
|
||||
assert_eq!(labels, ["Selection", "Layer Mask", "New Layer", "New Layer With Mask", "New Document", "New Document With Mask"]);
|
||||
for lang in crate::i18n::Lang::all().filter(|l| l.code() != "en") {
|
||||
for label in &labels {
|
||||
assert!(crate::i18n::has(lang, label), "{}: untranslated {label}", lang.code());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_runs_engine_command_on_proxy() {
|
||||
let mut doc = Document::with_background(
|
||||
|
|
|
|||
|
|
@ -1561,6 +1561,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ pohyb {key} spustit esc zavřít
|
||||
Version {version} Verze {version}
|
||||
New Document Nový dokument
|
||||
New Document With Mask Nový dokument s maskou
|
||||
New Layer With Mask Nová vrstva s maskou
|
||||
New Layer Nová vrstva
|
||||
Saved Uložené
|
||||
Save Preset… Uložit předvolbu…
|
||||
Preset Name Název předvolby
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1540,6 +1540,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ Navigieren {key} Ausführen Esc Schließen
|
||||
Version {version} Version {version}
|
||||
New Document Neues Dokument
|
||||
New Document With Mask Neues Dokument mit Maske
|
||||
New Layer With Mask Neue Ebene mit Maske
|
||||
New Layer Neue Ebene
|
||||
Save Preset… Vorgabe speichern…
|
||||
Preset Name Name der Vorgabe
|
||||
No saved presets yet. Noch keine gespeicherten Vorgaben.
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -436,6 +436,8 @@
|
|||
Text Layer Επίπεδο κειμένου
|
||||
Type Layer Επίπεδο κειμένου
|
||||
New Document Νέο έγγραφο
|
||||
New Document With Mask Νέο έγγραφο με μάσκα
|
||||
New Layer With Mask Νέο επίπεδο με μάσκα
|
||||
Document Έγγραφο
|
||||
Documents Έγγραφα
|
||||
File Name Όνομα αρχείου
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 7.
|
|
|
@ -1546,6 +1546,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ navegar {key} ejecutar esc cerrar
|
||||
Version {version} Versión {version}
|
||||
New Document Documento nuevo
|
||||
New Document With Mask Documento nuevo con máscara
|
||||
New Layer With Mask Nueva capa con máscara
|
||||
New Layer Nueva capa
|
||||
Save Preset… Guardar ajuste preestablecido…
|
||||
Preset Name Nombre del ajuste preestablecido
|
||||
No saved presets yet. Todavía no hay ajustes preestablecidos guardados.
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
|
|
@ -1527,6 +1527,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ naviguer {key} exécuter échap fermer
|
||||
Version {version} Version {version}
|
||||
New Document Nouveau document
|
||||
New Document With Mask Nouveau document avec masque
|
||||
New Layer With Mask Nouveau calque avec masque
|
||||
New Layer Nouveau calque
|
||||
Preset Name Nom du préréglage
|
||||
No saved presets yet. Aucun préréglage enregistré pour le moment.
|
||||
Error Erreur
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1521,6 +1521,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ navigasi {key} jalankan esc tutup
|
||||
Version {version} Versi {version}
|
||||
New Document Dokumen Baru
|
||||
New Document With Mask Dokumen Baru dengan Mask
|
||||
New Layer With Mask Layer Baru dengan Mask
|
||||
New Layer Layer Baru
|
||||
Preset Name Nama Preset
|
||||
No saved presets yet. Belum ada preset tersimpan.
|
||||
Error Error
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 4 and column 47.
|
|
|
@ -1545,6 +1545,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ naviga {key} esegui esc chiudi
|
||||
Version {version} Versione {version}
|
||||
New Document Nuovo documento
|
||||
New Document With Mask Nuovo documento con maschera
|
||||
New Layer With Mask Nuovo livello con maschera
|
||||
New Layer Nuovo livello
|
||||
Save Preset… Salva predefinito…
|
||||
Preset Name Nome del predefinito
|
||||
No saved presets yet. Nessun predefinito salvato.
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 16.
|
|
|
@ -1545,6 +1545,8 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ 移動 {key} 実行 esc 閉じる
|
||||
Version {version} バージョン {version}
|
||||
New Document 新規ドキュメント
|
||||
New Document With Mask 新規ドキュメント(マスク付き)
|
||||
New Layer With Mask 新規レイヤー(マスク付き)
|
||||
Saved 保存済み
|
||||
Save Preset… プリセットを保存…
|
||||
Preset Name プリセット名
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1516,6 +1516,8 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ 이동 {key} 실행 esc 닫기
|
||||
Version {version} 버전 {version}
|
||||
New Document 새 문서
|
||||
New Document With Mask 새 문서(마스크 포함)
|
||||
New Layer With Mask 새 레이어(마스크 포함)
|
||||
Saved 저장됨
|
||||
Save Preset… 사전 설정 저장…
|
||||
Preset Name 사전 설정 이름
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 11.
|
|
|
@ -1527,6 +1527,8 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ navigeren {key} uitvoeren esc sluiten
|
||||
Version {version} Versie {version}
|
||||
New Document Nieuw document
|
||||
New Document With Mask Nieuw document met masker
|
||||
New Layer With Mask Nieuwe laag met masker
|
||||
Save Preset… Voorinstelling opslaan…
|
||||
Preset Name Naam voorinstelling
|
||||
No saved presets yet. Nog geen opgeslagen voorinstellingen.
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 8 and column 34.
|
|
|
@ -1540,6 +1540,8 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ nawigacja {key} uruchom esc zamknij
|
||||
Version {version} Wersja {version}
|
||||
New Document Nowy dokument
|
||||
New Document With Mask Nowy dokument z maską
|
||||
New Layer With Mask Nowa warstwa z maską
|
||||
Preset Name Nazwa ustawienia wstępnego
|
||||
No saved presets yet. Nie ma jeszcze zapisanych ustawień wstępnych.
|
||||
Error Błąd
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1525,6 +1525,8 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ navegar {key} executar esc fechar
|
||||
Version {version} Versão {version}
|
||||
New Document Novo documento
|
||||
New Document With Mask Novo documento com máscara
|
||||
New Layer With Mask Nova camada com máscara
|
||||
Preset Name Nome da predefinição
|
||||
No saved presets yet. Nenhuma predefinição salva ainda.
|
||||
Error Erro
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1753,6 +1753,9 @@
|
|||
Divide Разделить
|
||||
Result Результат
|
||||
New Document Новый документ
|
||||
New Document With Mask Новый документ с маской
|
||||
New Layer With Mask Новый слой с маской
|
||||
New Layer Новый слой
|
||||
Preset Name Имя набора
|
||||
No saved presets yet. Сохранённых наборов пока нет.
|
||||
New Channel Новый канал
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -1733,6 +1733,8 @@
|
|||
Divide Ділення
|
||||
Result Результат
|
||||
New Document Новий документ
|
||||
New Document With Mask Новий документ з маскою
|
||||
New Layer With Mask Новий шар з маскою
|
||||
Preset Name Назва набору
|
||||
No saved presets yet. Збережених наборів ще немає.
|
||||
New Channel Новий канал
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 14.
|
|
|
@ -1558,6 +1558,9 @@
|
|||
↑↓ navigate ↵ run esc close ↑↓ 导航 ↵ 运行 esc 关闭
|
||||
Version {version} 版本 {version}
|
||||
New Document 新建文档
|
||||
New Document With Mask 带蒙版的新建文档
|
||||
New Layer With Mask 带蒙版的新建图层
|
||||
New Layer 新建图层
|
||||
Saved 已保存
|
||||
Save Preset… 保存预设…
|
||||
Preset Name 预设名称
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 15.
|
|
|
@ -1552,6 +1552,9 @@
|
|||
↑↓ navigate {key} run esc close ↑↓ 瀏覽 {key} 執行 esc 關閉
|
||||
Version {version} 版本 {version}
|
||||
New Document 新增文件
|
||||
New Document With Mask 含遮色片的新增文件
|
||||
New Layer With Mask 含遮色片的新增圖層
|
||||
New Layer 新增圖層
|
||||
Saved 已儲存
|
||||
Save Preset… 儲存預設…
|
||||
Preset Name 預設集名稱
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -65,7 +65,7 @@ what an agent can't finish alone.
|
|||
- **Estimate:** 60–100 h (2–9 h each, per the calibration).
|
||||
|
||||
### G7. Workspaces shown as parameter dialogs
|
||||
- **Missing:** Select and Mask workspace layout and New Document outputs
|
||||
- **Missing:** Select and Mask workspace layout
|
||||
(TOOL-214-1, Refine Hair #2671; its view modes preview live since #1754, Refine Edge Brush #2972); Content-Aware Fill workspace with a paintable sampling area
|
||||
(TOOL-214-15); Vanishing Point plane editor (TOOL-214-14); Content-Aware Scale through the
|
||||
transform box (TOOL-214-7); Puppet Warp mesh following the outline (TOOL-214-6); Content-Aware
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ Corpus floors (`crates/io/tests/corpus.rs`): the enforced minimums; the corpus t
|
|||
| TOOL-213-13 | Brush regression tests: no seams, hard edge, zero-scatter defaults, ABR never black | done | [#213](https://github.com/storytold/photocraft/issues/213) | Seam, hard-edge and zero-scatter tests in brush_tests.rs; ABR tip colour test in abr_map/tests.rs |
|
||||
| TOOL-213-14 | Tablet: no OS arrow cursor over the canvas on macOS, Windows and Linux | partial | [#213](https://github.com/storytold/photocraft/issues/213) | Brush tools set CursorIcon::None (canvas.rs:1259-1275); not verified per OS |
|
||||
| TOOL-213-18 | Pattern Stamp tool: sibling of Clone Stamp in the S flyout | done | [#213](https://github.com/storytold/photocraft/issues/213) | Tool::PatternStamp in the S group; paint.patternStamp stamps the Patterns-panel tile with Aligned and Impressionist (an approximation: it jitters each dab's tile phase, not Photoshop's smeared impressionist look); engine tests cover depths, seams, hostile input |
|
||||
| TOOL-214-1 | Select and Mask workspace: view modes, Refine Edge brush, 6 outputs | partial | [#214](https://github.com/storytold/photocraft/issues/214) | select.refineEdge dialog with a live preview in Photoshop's view modes: Onion Skin, Marching Ants, Overlay, On Black, On White, Black & White, On Layers, per-view Opacity, Show Edge (J), Show Original (P), F cycles, X shows the image (select_mask_view.rs, #1754); Refine Edge Brush (R, size, Expand Detection Area / Restore Original Edge, Alt swaps) paints select.refineEdge refineStrokes, estimated by colour-line matting (matting/brush.rs, #2972); outputs Selection, Layer Mask, New Layer, New Layer with Mask; no workspace layout or New Document outputs |
|
||||
| TOOL-214-1 | Select and Mask workspace: view modes, Refine Edge brush, 6 outputs | partial | [#214](https://github.com/storytold/photocraft/issues/214) | select.refineEdge dialog with a live preview in Photoshop's view modes: Onion Skin, Marching Ants, Overlay, On Black, On White, Black & White, On Layers, per-view Opacity, Show Edge (J), Show Original (P), F cycles, X shows the image (select_mask_view.rs, #1754); Refine Edge Brush (R, size, Expand Detection Area / Restore Original Edge, Alt swaps) paints select.refineEdge refineStrokes, estimated by colour-line matting (matting/brush.rs, #2972); outputs Selection, Layer Mask, New Layer, New Layer with Mask, New Document, New Document with Mask (#2998); no workspace layout |
|
||||
| TOOL-214-2 | Marquee: Shift square, Alt from centre, live W x H readout | done | [#214](https://github.com/storytold/photocraft/issues/214) | canvas.rs marquee_corners: Shift square or circle, Alt from the centre, live W x H readout beside the cursor (marquee_readout); marquee_tests.rs |
|
||||
| TOOL-214-3 | Color Range test: Sampled Colors at Fuzziness 0 selects the exact match only | partial | [#214](https://github.com/storytold/photocraft/issues/214) | Fuzziness-0 tests cover midtones/shadows only (selection_cmds.rs:444-447) |
|
||||
| TOOL-214-4 | Move tool: box-select layers, Auto-Select hover outline, double-click fit, drag to a tab | partial | [#214](https://github.com/storytold/photocraft/issues/214) | Auto-Select hover outline (Preferences › Tools › Show Highlight on Rollover, #2834) and box-select layers from an empty spot (move_box.rs, layer.selectInRect, #2844) done; double-click fit and drag to a tab open |
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ id = "TOOL-214-1"
|
|||
target = "Select and Mask workspace: view modes, Refine Edge brush, 6 outputs"
|
||||
status = "partial"
|
||||
issue = 214
|
||||
note = "select.refineEdge dialog with a live preview in Photoshop's view modes: Onion Skin, Marching Ants, Overlay, On Black, On White, Black & White, On Layers, per-view Opacity, Show Edge (J), Show Original (P), F cycles, X shows the image (select_mask_view.rs, #1754); Refine Edge Brush (R, size, Expand Detection Area / Restore Original Edge, Alt swaps) paints select.refineEdge refineStrokes, estimated by colour-line matting (matting/brush.rs, #2972); outputs Selection, Layer Mask, New Layer, New Layer with Mask; no workspace layout or New Document outputs"
|
||||
note = "select.refineEdge dialog with a live preview in Photoshop's view modes: Onion Skin, Marching Ants, Overlay, On Black, On White, Black & White, On Layers, per-view Opacity, Show Edge (J), Show Original (P), F cycles, X shows the image (select_mask_view.rs, #1754); Refine Edge Brush (R, size, Expand Detection Area / Restore Original Edge, Alt swaps) paints select.refineEdge refineStrokes, estimated by colour-line matting (matting/brush.rs, #2972); outputs Selection, Layer Mask, New Layer, New Layer with Mask, New Document, New Document with Mask (#2998); no workspace layout"
|
||||
|
||||
[[item]]
|
||||
id = "TOOL-214-2"
|
||||
|
|
|
|||
Loading…
Reference in New Issue