@@ -192,6 +192,7 @@ new work easier to reason about.
192192currently displayed image and its interaction state. It owns:
193193
194194* the current `LoadedImage `;
195+ * the current `ViewRecipe `;
195196* status and error text;
196197* zoom, scroll, zoom pivot, and fit behavior;
197198* selection and area-probe state;
@@ -202,6 +203,40 @@ currently displayed image and its interaction state. It owns:
202203If state is tied to one image pane and its navigation state, it usually
203204belongs here.
204205
206+ `ViewRecipe `
207+ ------------
208+
209+ `ViewRecipe ` in `src/imiv/imiv_viewer.h ` is the per-view preview/export recipe.
210+ It currently owns the presentation settings that should travel with one image
211+ view:
212+
213+ * exposure, gamma, and offset;
214+ * interpolation mode;
215+ * channel and color-mode selection;
216+ * OCIO enable state;
217+ * OCIO display, view, and image-color-space choices.
218+
219+ This is the current source of truth for per-view preview state. At runtime,
220+ the active view's recipe is copied into the UI editing state before menus and
221+ tool windows are drawn, then copied back into the active `ViewerState ` after
222+ UI edits are applied.
223+
224+ That mirror step is intentional. It keeps most existing Dear ImGui code
225+ procedural while establishing one durable place for future `Save View As... `
226+ or CPU-side export processing.
227+
228+ The first real CPU export path is now `Save Selection As... `. It does not yet
229+ consume the full `ViewRecipe `; instead, it is intentionally narrower:
230+
231+ * it reconstructs an `ImageBuf ` from the loaded source pixels;
232+ * crops the selected ROI;
233+ * bakes source orientation with `ImageBufAlgo::reorient() `;
234+ * writes the result through OIIO.
235+
236+ That makes it a useful end-to-end test seam for GUI-driven CPU processing
237+ without prematurely locking `Save View As... ` to the wrong recipe-baking
238+ semantics.
239+
205240`ImageLibraryState ` and `MultiViewWorkspace `
206241--------------------------------------------
207242
@@ -216,31 +251,71 @@ The first multi-view slice introduces two more shared state buckets in
216251Each `ImageViewWindow ` owns one `ViewerState `. The main `Image ` window is the
217252primary view. Additional `Image N ` windows are created from
218253`File -> New view from current image ` or by double-clicking entries in the
219- Image List window.
254+ Image List window. Folder-open startup and `File -> Open Folder... ` also feed
255+ the same shared loaded-image library rather than creating a separate browsing
256+ mode.
220257
221258This split matters: queue history is now global to the workspace, but image
222259interaction state remains per view.
223260
261+ That same shared library path now covers startup multi-open, `Open Folder... `,
262+ and multi-file drag/drop. There is one queue model, not separate browsing and
263+ drop-import modes.
264+
265+ The `Image List ` window now also exposes per-row workspace state rather than
266+ being a passive history view:
267+
268+ * `> ` marks the image shown in the active image view;
269+ * `[N] ` reports how many open image views currently show that path;
270+ * a small inline close button appears for rows visible in the active view;
271+ * the row popup menu routes the shared-library actions:
272+ `Open in active view `, `Open in new view `, `Close in active view `,
273+ `Close in all views `, and `Remove from session `.
274+
275+ Those actions are implemented against the shared `ImageLibraryState ` plus the
276+ current `MultiViewWorkspace `. `Close ` mutates view bindings only. `Remove `
277+ edits the shared session queue and then retargets or clears any views that
278+ were showing the removed path.
279+
280+ Folder-open path filtering
281+ --------------------------
282+
283+ The current folder-open path is intentionally cheap:
284+
285+ * it scans one directory non-recursively;
286+ * it filters candidate files by the readable extension set reported by OIIO's
287+ plugin registry;
288+ * it does not open every file just to decide whether it belongs in the queue.
289+
290+ That is the right default for folders containing many ordinary still images.
291+ It is only a queue-building filter, not a guarantee that every accepted file
292+ will decode successfully later.
293+
224294`PlaceholderUiState `
225295--------------------
226296
227- `PlaceholderUiState ` is the persistent UI and preview configuration. It owns:
297+ `PlaceholderUiState ` is the persistent global UI configuration plus the
298+ active-view editing mirror. It owns:
228299
229300* visibility toggles for auxiliary windows;
230- * preview controls such as exposure, gamma, offset, and color mode;
231- * image presentation options such as interpolation and fit-to-window;
232- * OCIO choices;
301+ * global presentation and app settings such as fit-to-window;
302+ * global OCIO config-source selection and user-config path;
233303* saved backend preference for the next launch;
234304* docking policy state such as `image_window_force_dock `.
235305
236306If state describes how the UI should look or how preview rendering should be
237- configured across launches, it usually belongs here.
307+ configured across launches for the application as a whole, it usually belongs
308+ here.
238309
239- At the moment, this also means preview controls are still shared across all
240- open image views. Exposure, gamma, offset, interpolation, and OCIO selection
241- remain global UI state rather than per-view state. Splitting those controls
242- into per-view state is a follow-up task, not part of the first multi-view
243- milestone.
310+ Preview controls such as exposure, gamma, offset, interpolation, channel
311+ selection, and OCIO display/view no longer live here as the source of truth.
312+ They are mirrored into `PlaceholderUiState ` only while editing the active
313+ view, then written back to that view's `ViewRecipe `.
314+
315+ The focused `imiv_view_recipe_regression.py ` regression exists specifically to
316+ lock that behavior down: it opens multiple image views, edits one view's
317+ recipe, switches active views, and verifies that the inactive view's recipe
318+ state stays unchanged.
244319
245320`DeveloperUiState `
246321------------------
@@ -267,14 +342,15 @@ layout state together in a single `imiv.inf` file.
267342* the Dear ImGui `.ini ` text first, straight from
268343 `ImGui::SaveIniSettingsToMemory() `;
269344* then an `ImivApp ` settings section with `PlaceholderUiState `,
270- `ImageLibraryState `, and a small amount of `ViewerState `.
345+ the primary view's `ViewRecipe `, `ImageLibraryState `, and a small amount of
346+ `ViewerState `.
271347
272348This is worth preserving. It gives :program: `imiv ` one file for:
273349
274350* dock/layout state;
275351* window placement;
276352* renderer preference;
277- * preview defaults;
353+ * preview defaults from the primary view recipe ;
278354* recent-image history.
279355
280356The `IMIV_CONFIG_HOME ` environment variable exists mainly for isolated local
@@ -297,20 +373,21 @@ The current order is:
2973735. queue any developer screenshot work;
2983746. execute queued state mutations with `execute_viewer_frame_actions() `;
2993757. process drag and drop and auto-subimage work;
300- 8. clamp UI state;
301- 9. update the renderer preview texture for the active view from the current
302- preview controls;
303- 10. build the dockspace host window ;
304- 11. draw the main image window and any secondary ` Image N ` windows ;
305- 12. draw the Image List window, auxiliary windows, and popups ;
376+ 8. sync the active view recipe into the UI mirror and clamp state;
377+ 9. build the dockspace host window;
378+ 10. draw the main image window and any secondary ` Image N ` windows, each with
379+ its own copied ` ViewRecipe ` ;
380+ 11. draw the Image List window, auxiliary windows, and popups ;
381+ 12. write any UI edits from the active view back into its ` ViewRecipe ` ;
30638213. draw developer-mode Dear ImGui diagnostic windows and the drag overlay.
307383
308384Two design choices here are important:
309385
310386* visible state changes are not scattered through menu items and shortcut
311387 handlers;
312- * preview texture updates happen before the image window asks for texture
313- references, so the window code can stay renderer-neutral.
388+ * per-view preview texture updates happen inside the image-window loop using
389+ each view's own recipe, so the window code can stay renderer-neutral while
390+ still supporting independent view settings.
314391
315392
316393Docking, windows, and viewports
@@ -341,22 +418,23 @@ Secondary image windows:
341418* are created as ordinary Dear ImGui windows with the same image-window class;
342419* are forced into the main dockspace on first creation;
343420* currently use `ImGuiDockNodeFlags_NoUndocking `;
344- * inherit the same shared preview/renderer policy as the primary view.
421+ * share the same renderer backend as the primary view, but keep their own
422+ `ViewerState ` and `ViewRecipe `.
345423
346- Why there is no ` DockBuilder `
347- -----------------------------
424+ Initial dock layout policy
425+ --------------------------
348426
349- :program: `imiv ` does not currently use `DockBuilder `.
427+ :program: `imiv ` still avoids programmatic dock trees for most windows, but the
428+ current multi-view slice makes one deliberate exception:
350429
351- That is deliberate. The viewer relies on:
430+ * if `Image List ` becomes visible for a multi-file load and no saved layout for
431+ that window exists yet, `imiv ` uses a small `DockBuilder ` split to create a
432+ right-side pane of roughly 200 pixels;
433+ * the primary `Image ` window is docked into the remaining left-side node;
434+ * later layout changes are still owned by Dear ImGui settings persistence.
352435
353- * a simple dockspace host;
354- * persistent Dear ImGui layout state loaded from disk;
355- * a small policy flag, `image_window_force_dock `, to keep the main image
356- window docked until the user explicitly undocks it.
357-
358- This keeps layout behavior on public docking API calls and avoids baking a
359- programmatic layout tree into the code.
436+ This keeps the default presentation usable for multi-image loads without
437+ turning the whole application into a hard-coded dock tree.
360438
361439Window model
362440------------
@@ -738,6 +816,41 @@ The OpenGL and Metal paths are intentionally not copies of the Vulkan compute
738816pipeline. That keeps each backend aligned with its native toolchain and makes
739817backend failures easier to diagnose.
740818
819+
820+ Huge images and proxy recommendation
821+ ====================================
822+
823+ The current load path is still a full-image viewer path, not a true proxy or
824+ tile-on-demand design.
825+
826+ Today, `read_image_file() ` in `src/imiv/imiv_viewer.cpp ` does create an
827+ `ImageCache `, but it then reads the whole image into `LoadedImage `, builds
828+ metadata/long-info rows from that loaded image, and the renderer uploads a
829+ full source texture for the active backend.
830+
831+ Implications:
832+
833+ * `max_memory_ic_mb ` is useful as an ImageCache tuning knob, but it does not
834+ make :program: `imiv ` a true huge-image viewer;
835+ * the current path is appropriate for ordinary still images and regression
836+ work, but it is not the right long-term design for very large plates,
837+ stitched panoramas, or other images that should stay sparse on the CPU and
838+ GPU.
839+
840+ Recommendation for future work:
841+
842+ * do not extend the current `LoadedImage -> full backend texture ` path to
843+ chase huge-image support;
844+ * introduce a dedicated proxy/tiled path instead, backed by OIIO
845+ `ImageCache `/ImageBuf proxy access or a similar sparse-image abstraction;
846+ * keep `ViewRecipe ` independent from that storage choice so the same per-view
847+ recipe can drive either full-image preview or a future tile/proxy backend;
848+ * keep CPU export and `Save View As... ` built on `ViewRecipe `, not on backend
849+ texture state.
850+
851+ That separation is the important design guardrail: image storage strategy for
852+ huge files should change independently from per-view preview/export semantics.
853+
741854OCIO integration
742855----------------
743856
@@ -855,8 +968,10 @@ Common examples in the current code are:
855968* draw-list rendering:
856969 `ImDrawList::AddImage() `.
857970
858- Current production `src/imiv ` sources do not include `imgui_internal.h `.
859- That is worth preserving.
971+ Current production `src/imiv ` sources still use Dear ImGui public API first.
972+ There is now one explicit exception in `imiv_frame.cpp ` for the initial
973+ `Image List ` dock split, which uses `imgui_internal.h ` `DockBuilder ` helpers.
974+ That exception should stay narrow.
860975
861976Maintenance-sensitive integrations
862977----------------------------------
0 commit comments