Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ A new frame gets the roll's film type, and the crop, print stock and Lab setting

### Viewing

* **Before / After** (`\`, or the **Print** menu): splits the canvas between the default conversion and your edit. Hold `\` to see the default alone.
* **Before / After** (`\`, or the **Print** menu): splits the canvas between the default conversion and your edit. Hold `\` to see the default alone. To compare with an earlier version, right-click it in **History**.
* **Peeks**: hold `N` for the scan as loaded (the negative), `M` for the flat master, and `P` for the camera's own JPEG. The **Print** menu has them too.
* **Reference View** (`Shift+R`): pins this frame beside the canvas, to match other frames to it.
* **Probe at Cursor** (`Shift+I`, **Overlays** menu): shows the density and zone under the pointer.
Expand Down Expand Up @@ -441,13 +441,13 @@ A **work print** is a version of this frame that you keep under a name, like the

* **Save Work Print…** (`Ctrl+Shift+S`) keeps the current edit.
* **Click** a work print to use it. `Ctrl+Z` goes back.
* **⋮** or right-click: **Export This Version…**, **Rename…** or **Delete…**.
* **⋮** or right-click: **Compare With This Version**, **Export This Version…**, **Rename…** or **Delete…**. **Compare With This Version** opens **Before / After** with the work print on the left.
* **Load Metadata** (off at first): also restores the metadata saved with the work print.

<!-- panel:edit_history -->
### Edit history

Every change you made, newest on top (the last 100). **Now** marks the current state; after an undo, later steps show as **Undone** until your next edit. Click a step to go back to it. **⋮** or right-click → **Export This Version…** exports a past state.
Every change you made, newest on top (the last 100). **Now** marks the current state; after an undo, later steps show as **Undone** until your next edit. Click a step to go back to it. **⋮** or right-click → **Export This Version…** exports a past state. **Compare With This Step** opens **Before / After** with that step on the left, on your current crop. Press `\` to close it.

---

Expand Down
56 changes: 55 additions & 1 deletion negpy/desktop/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,24 @@ def baseline_compare_config(config: WorkspaceConfig) -> WorkspaceConfig:
)


def reference_compare_config(reference: WorkspaceConfig, config: WorkspaceConfig) -> WorkspaceConfig:
"""
The 'before' config for a split against a history step or work print: its look on the
current framing, composite, flat field and output settings, so both halves line up.
The preview source is not decoded again, so a step's other source settings take no effect.
"""
return replace(
reference,
geometry=config.geometry,
stitch=config.stitch,
hdr=config.hdr,
flatfield=config.flatfield,
rgbscan=config.rgbscan,
metadata=config.metadata,
export=config.export,
)


# Solved knee fields under their slider names (sidebar/tone.py).
_KNEE_LABELS = {
"shadow_grade": tr("Shadows Grade"),
Expand Down Expand Up @@ -7369,6 +7387,9 @@ def request_render(
self.render_requested.emit(task)

def _baseline_compare_config(self) -> WorkspaceConfig:
reference = self.state.compare_reference
if reference is not None:
return reference_compare_config(reference, self.state.config)
return baseline_compare_config(self.state.config)

def _compare_before_key(self) -> str:
Expand Down Expand Up @@ -7404,10 +7425,42 @@ def exit_compare(self) -> None:
self.state.compare_before = None
self.state.compare_before_rect = None
self.state.compare_before_key = ""
self.state.compare_reference = None
self.state.compare_reference_label = ""
if self.state.compare_mode:
self.state.compare_mode = False
self.compare_changed.emit(False)

def compare_with_history_step(self, index: int) -> None:
"""Open the split against a history step instead of the default conversion."""
file_hash = self.state.current_file_hash
if not file_hash:
return
# The live top step may not be persisted yet: it lives in state.config.
config = self.state.config if index == self.state.undo_index else self.session.repo.load_history_step(file_hash, index)
self._compare_with(config, tr("STEP {number}", number=index))

def compare_with_work_print(self, name: str) -> None:
"""Open the split against a work print instead of the default conversion."""
file_hash = self.state.current_file_hash
if not file_hash:
return
self._compare_with(self.session.repo.load_work_print(file_hash, name), name.upper())

def _compare_with(self, config: Optional[WorkspaceConfig], label: str) -> None:
if config is None or self.state.preview_raw is None:
return
self.state.compare_reference = config
self.state.compare_reference_label = label
if not self.state.compare_mode:
self.toggle_compare()
return
# Drop the old reference's pixels, or they show under the new badge until the render lands.
self.state.compare_before = None
self.state.compare_before_key = ""
self.compare_frame_ready.emit()
self._request_compare_baseline()

def begin_compare_hold(self) -> None:
"""Show the baseline over the whole picture until end_compare_hold. Only over the print:
a peek, the test strip or an uncropped tool view has no baseline that lines up."""
Expand All @@ -7434,7 +7487,8 @@ def end_compare_hold(self) -> None:
self.compare_hold_changed.emit(False)

def toggle_compare(self) -> None:
"""Toggle the before/after split between the edit and the auto baseline."""
"""Toggle the before/after split between the edit and the auto baseline, or the
history step or work print set by _compare_with."""
if self.state.preview_raw is None:
return
if self.state.compare_mode:
Expand Down
4 changes: 4 additions & 0 deletions negpy/desktop/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ class AppState:
compare_split: float = 0.5
# True while the compare key is held: the whole picture shows the baseline.
compare_hold: bool = False
# A history step or work print the split compares against instead of the default
# conversion, and its badge text. Held as a config: a later edit can delete the step.
compare_reference: Optional[Any] = None
compare_reference_label: str = ""

# Export presets (globally managed, not per-file)
export_presets: List[ExportPreset] = field(default_factory=list)
Expand Down
5 changes: 3 additions & 2 deletions negpy/desktop/view/canvas/overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -1396,8 +1396,9 @@ def _draw_compare_split(self, painter: QPainter) -> None:
knob = QRectF(centre.x() - _SPLIT_HANDLE_PX, centre.y() - _SPLIT_HANDLE_PX, 2 * _SPLIT_HANDLE_PX, 2 * _SPLIT_HANDLE_PX)
painter.drawText(knob, Qt.AlignmentFlag.AlignCenter, "◂▸")

if x - target.left() > 92:
draw_view_badge(painter, tr("BEFORE"), target.left() + THEME.space_xl, target.top() + THEME.space_xl)
before_text = self.state.compare_reference_label or tr("BEFORE")
if x - target.left() > max(92.0, view_badge_width(painter, before_text) + 2 * THEME.space_xl):
draw_view_badge(painter, before_text, target.left() + THEME.space_xl, target.top() + THEME.space_xl)
if target.right() - x > 92:
after = tr("AFTER")
draw_view_badge(
Expand Down
12 changes: 10 additions & 2 deletions negpy/desktop/view/sidebar/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,21 +218,29 @@ def _on_context_menu(self, pos) -> None:
if item is None:
return
menu = QMenu(self)
# The current step is the After half, so comparing against it shows nothing.
compare_action = None if item.data(_TAG_ROLE) == "now" else menu.addAction(tr("Compare With This Step"))
export_action = menu.addAction(tr("Export This Version…"))
if menu.exec(self.list.mapToGlobal(pos)) is export_action:
chosen = menu.exec(self.list.mapToGlobal(pos))
if chosen is export_action:
self.controller.export_history_step(item.data(_INDEX_ROLE))
elif chosen is not None and chosen is compare_action:
self.controller.compare_with_history_step(item.data(_INDEX_ROLE))

def _on_work_print_menu(self, pos) -> None:
item = self.work_prints.itemAt(pos)
if item is None:
return
name = item.text()
menu = QMenu(self)
compare_action = menu.addAction(tr("Compare With This Version"))
export_action = menu.addAction(tr("Export This Version…"))
rename_action = menu.addAction(tr("Rename…"))
delete_action = menu.addAction(tr("Delete…"))
chosen = menu.exec(self.work_prints.mapToGlobal(pos))
if chosen is export_action:
if chosen is compare_action:
self.controller.compare_with_work_print(name)
elif chosen is export_action:
self.controller.export_work_print(name)
elif chosen is rename_action:
new_name, ok = QInputDialog.getText(self, tr("Rename Work Print"), tr("Name:"), text=name)
Expand Down
6 changes: 3 additions & 3 deletions negpy/translations/USER_GUIDE.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ A new frame gets the roll's film type, and the crop, print stock and Lab setting

### Viewing

* **Before / After** (`\`, or the **Print** menu): splits the canvas between the default conversion and your edit. Hold `\` to see the default alone.
* **Before / After** (`\`, or the **Print** menu): splits the canvas between the default conversion and your edit. Hold `\` to see the default alone. To compare with an earlier version, right-click it in **History**.
* **Peeks**: hold `N` for the scan as loaded (the negative), `M` for the flat master, and `P` for the camera's own JPEG. The **Print** menu has them too.
* **Reference View** (`Shift+R`): pins this frame beside the canvas, to match other frames to it.
* **Probe at Cursor** (`Shift+I`, **Overlays** menu): shows the density and zone under the pointer.
Expand Down Expand Up @@ -441,13 +441,13 @@ A **work print** is a version of this frame that you keep under a name, like the

* **Save Work Print…** (`Ctrl+Shift+S`) keeps the current edit.
* **Click** a work print to use it. `Ctrl+Z` goes back.
* **⋮** or right-click: **Export This Version…**, **Rename…** or **Delete…**.
* **⋮** or right-click: **Compare With This Version**, **Export This Version…**, **Rename…** or **Delete…**. **Compare With This Version** opens **Before / After** with the work print on the left.
* **Load Metadata** (off at first): also restores the metadata saved with the work print.

<!-- panel:edit_history -->
### Edit history

Every change you made, newest on top (the last 100). **Now** marks the current state; after an undo, later steps show as **Undone** until your next edit. Click a step to go back to it. **⋮** or right-click → **Export This Version…** exports a past state.
Every change you made, newest on top (the last 100). **Now** marks the current state; after an undo, later steps show as **Undone** until your next edit. Click a step to go back to it. **⋮** or right-click → **Export This Version…** exports a past state. **Compare With This Step** opens **Before / After** with that step on the left, on your current crop. Press `\` to close it.

---

Expand Down
8 changes: 4 additions & 4 deletions negpy/translations/USER_GUIDE.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,20 @@ NegPy は、フィルムスキャンをポジ画像に仕上げる仮想暗室
- **保存…**:現在の設定のうちどれを新しいプリセットにするかを選びます。プリセットにはトリミングと光学補正の設定をフラットフィールドのプロファイルと共に保存できますが、フレームごとのトリミング、回転、反転は保存しません。
- **ペン**と**ゴミ箱**:選択したプリセットを編集または削除します。

<!-- panel:work_prints src:e8bf683e -->
<!-- panel:work_prints src:6d4f4720 -->
## ワークプリント

**ワークプリント**は、名前を付けて残すこのフレームのバージョンです。仕上げまでに作るテストプリントと同じです。

- **ワークプリントを保存…**(`Ctrl+Shift+S`)で現在の編集を残します。
- ワークプリントを**クリック**すると使います。`Ctrl+Z` で戻ります。
- **⋮** または右クリック:**このバージョンをエクスポート…**、**名前を変更…**、**削除…**。
- **⋮** または右クリック:**このバージョンと比較**、**このバージョンをエクスポート…**、**名前を変更…**、**削除…**。**このバージョンと比較**は、ワークプリントを左側にして**ビフォー / アフター**を開きます。
- **メタデータのロード**(最初はオフ):ワークプリントと一緒に保存したメタデータも戻します。

<!-- panel:edit_history src:57abcbf1 -->
<!-- panel:edit_history src:59f9ee38 -->
## 編集履歴

行ったすべての変更を新しい順に表示します(最新 100 件)。**現在**が今の状態です。元に戻した後は、次に編集するまで後の手順が**取り消し済み**になります。手順をクリックするとその状態に戻ります。**⋮** または右クリック →**このバージョンをエクスポート…**で過去の状態を書き出します。
行ったすべての変更を新しい順に表示します(最新 100 件)。**現在**が今の状態です。元に戻した後は、次に編集するまで後の手順が**取り消し済み**になります。手順をクリックするとその状態に戻ります。**⋮** または右クリック →**このバージョンをエクスポート…**で過去の状態を書き出します。**このステップと比較**は、そのステップを左側にして、現在のトリミングで**ビフォー / アフター**を開きます。`\` で閉じます。

<!-- panel:film src:7e289624 -->
## セットアップ
Expand Down
8 changes: 4 additions & 4 deletions negpy/translations/USER_GUIDE.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,20 @@ NegPy는 필름 스캔을 포지티브 이미지로 만드는 가상 암실입
- **저장…**: 현재 설정 중 무엇을 새 사전 설정으로 저장할지 고릅니다. 사전 설정에는 자르기와 광학 보정 설정을 플랫 필드 프로필과 함께 저장할 수 있지만, 프레임별 자르기, 회전, 뒤집기는 저장하지 않습니다.
- **펜**과 **휴지통**: 선택한 사전 설정을 편집하거나 삭제합니다.

<!-- panel:work_prints src:e8bf683e -->
<!-- panel:work_prints src:6d4f4720 -->
## 작업 인화물

**작업 인화**는 이름을 붙여 보관하는 이 프레임의 버전으로, 최종 인화까지 만드는 테스트 인화와 같습니다.

- **작업 인화 저장…**(`Ctrl+Shift+S`)으로 현재 편집을 보관합니다.
- 작업 인화를 **클릭**하면 사용합니다. `Ctrl+Z`로 되돌립니다.
- **⋮** 또는 오른쪽 클릭: **이 버전 내보내기…**, **이름 바꾸기…**, **삭제…**.
- **⋮** 또는 오른쪽 클릭: **이 버전과 비교**, **이 버전 내보내기…**, **이름 바꾸기…**, **삭제…**. **이 버전과 비교**는 작업 인화를 왼쪽에 두고 **전/후**를 엽니다.
- **메타데이터 로드**(처음에는 꺼짐): 작업 인화와 함께 저장한 메타데이터도 되돌립니다.

<!-- panel:edit_history src:57abcbf1 -->
<!-- panel:edit_history src:59f9ee38 -->
## 편집 기록

내가 한 모든 변경을 최신순으로 보여줍니다(최근 100개). **현재**는 지금 상태입니다. 실행 취소 뒤에는 다음 편집 전까지 이후 단계가 **취소됨**으로 표시됩니다. 단계를 클릭하면 그 상태로 돌아갑니다. **⋮** 또는 오른쪽 클릭 → **이 버전 내보내기…**로 지난 상태를 내보냅니다.
내가 한 모든 변경을 최신순으로 보여줍니다(최근 100개). **현재**는 지금 상태입니다. 실행 취소 뒤에는 다음 편집 전까지 이후 단계가 **취소됨**으로 표시됩니다. 단계를 클릭하면 그 상태로 돌아갑니다. **⋮** 또는 오른쪽 클릭 → **이 버전 내보내기…**로 지난 상태를 내보냅니다. **이 단계와 비교**는 그 단계를 왼쪽에 두고 현재 자르기로 **전/후**를 엽니다. `\`를 누르면 닫힙니다.

<!-- panel:film src:7e289624 -->
## 기본 설정
Expand Down
8 changes: 4 additions & 4 deletions negpy/translations/USER_GUIDE.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,20 +285,20 @@ NegPy 通过虚拟暗房将胶片扫描制作为正像。源图像保持原样
- **保存…**:选择当前设置中哪些保存为新预设。预设可保存裁切和光学设置及平场配置,但从不保存单帧的裁切、旋转或翻转。
- **笔**和**垃圾桶**:编辑或删除所选预设。

<!-- panel:work_prints src:e8bf683e -->
<!-- panel:work_prints src:6d4f4720 -->
## 工作样片

**工作样片**是你以名称保留的此帧版本,就像得到最终照片前的试印。

- **保存工作样片…**(`Ctrl+Shift+S`)保留当前编辑。
- **点击**工作样片即可使用。`Ctrl+Z` 返回。
- **⋮** 或右键:**导出此版本…**、**重命名…**或**删除…**。
- **⋮** 或右键:**与此版本对比**、**导出此版本…**、**重命名…**或**删除…**。**与此版本对比**会打开**前后对比**,工作样片在左侧。
- **加载元数据**(初始关闭):同时恢复随工作样片保存的元数据。

<!-- panel:edit_history src:57abcbf1 -->
<!-- panel:edit_history src:59f9ee38 -->
## 编辑历史

你做过的每一次更改,最新的在上(保留最近 100 次)。**当前**标出现在的状态;撤销后,之后的步骤会显示为**已撤销**,直到你下一次编辑。点击某一步即可回到那时的状态。**⋮** 或右键 → **导出此版本…**可导出过去的状态。
你做过的每一次更改,最新的在上(保留最近 100 次)。**当前**标出现在的状态;撤销后,之后的步骤会显示为**已撤销**,直到你下一次编辑。点击某一步即可回到那时的状态。**⋮** 或右键 → **导出此版本…**可导出过去的状态。**与此步骤对比**会以当前裁剪打开**前后对比**,该步骤在左侧。按 `\` 关闭。

<!-- panel:film src:7e289624 -->
## 基础设置
Expand Down
3 changes: 3 additions & 0 deletions negpy/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@
"Combines the short and long exposure fusion with multi-pass stacking for the most range and least noise, the slowest option.": "短い露出と長い露出の合成をマルチパス スタッキングと組み合わせ、レンジを最大に、ノイズを最小にします。最も遅いオプションです。",
"Commit the solved print (Enter)": "求めたプリントを確定します (Enter)",
"Compare": "比較",
"Compare With This Step": "このステップと比較",
"Compare With This Version": "このバージョンと比較",
"Composition guide shown in the crop tool": "トリミングツールに表示する構図ガイド",
"Compression": "圧縮",
"Compute and Save": "計算して保存する",
Expand Down Expand Up @@ -2090,6 +2092,7 @@
"SELECTED MASK": "選択中のマスク",
"SIZE": "サイズ",
"SPLIT TONING": "スプリットトーン",
"STEP {number}": "ステップ {number}",
"Sabattier": "サバティエ",
"Sabattier Strength": "サバティエの強さ",
"Sabattier Strength ↑/↓": "サバティエの強さ ↑/↓",
Expand Down
Loading
Loading