diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..b818c75f
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,43 @@
+name: Build
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Add MSBuild to PATH
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Build x64
+ run: msbuild vs2026/voidImageViewer.sln /p:Configuration=Release /p:Platform=x64 /p:PlatformToolset=v143
+
+ - name: Build x86
+ run: msbuild vs2026/voidImageViewer.sln /p:Configuration=Release /p:Platform=Win32 /p:PlatformToolset=v143
+
+ - name: Setup NSIS
+ run: |
+ choco install nsis -y
+ "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+
+ - name: Build Installer x64
+ run: powershell -File nsis/build_installer.ps1 -Arch x64 -VsVersion vs2026 -BuildConfig Release -Lang English
+
+ - name: Build Installer x86
+ run: powershell -File nsis/build_installer.ps1 -Arch x86 -VsVersion vs2026 -BuildConfig Release -Lang English
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: voidImageViewer-build
+ path: |
+ voidImageViewer/x64/Release/voidImageViewer.exe
+ voidImageViewer/Release/voidImageViewer.exe
+ nsis/voidImageViewer-*-setup.exe
diff --git a/Changes.txt b/Changes.txt
index 62d6cdba..654adff5 100644
--- a/Changes.txt
+++ b/Changes.txt
@@ -1,3 +1,13 @@
+Saturday, 29 August 2026: Version 1.2.1.0
+ added Smooth (Halftone) toggle to main viewer View menu and context menu.
+ added Compare Images side-by-side comparison mode with synchronized/individual zoom and pan.
+ added drag and drop support directly onto left/right compare panes.
+ added "Use Current Image" toggle per pane with mutual exclusion.
+ added mini toolbars per pane for file browsing, zooming, 1:1, fit, and rotation.
+ added top control bar with tips, Auto-Fit, Smooth, Filename overlay, and Close.
+ added WebP fallback decoder and alpha blending over window background for transparent PNG/ICO/GIF images.
+ added full multilingual localization for English, Indonesian, and Simplified Chinese.
+
Monday, 12 January 2026: Version 1.0.0.15
fixed an issue with scrolling large images.
improved position offset when using numpad keys.
diff --git a/README.md b/README.md
index 1d27215e..c1f2f2b5 100644
--- a/README.md
+++ b/README.md
@@ -26,25 +26,83 @@ void Image Viewer main window:
void Image Viewer General Options:
-
+
void Image Viewer View Options:
-
+
void Image Viewer Controls Options:
-
+
+## Changelog
+
+### Version 1.2.1.0 (Latest)
+- **New Feature: Smooth Toggle in Main Viewer**
+ - Added **Smooth** (Halftone / Bilinear interpolation) toggle directly in the `View` menu and right-click context menu.
+ - Linked with configuration persistence (`shrink_blit_mode` / `mag_filter`), and seamlessly integrated with Compare Images initial smoothing state.
+- **New Feature: Compare Images (Side-by-Side Image Comparison)**
+ - Added multi-pane **Compare Images** window accessible via `View` -> `Compare Images...` or shortcut (`Ctrl+Alt+C`).
+ - **Drag & Drop Support:**
+ - Directly drag & drop image files from File Explorer to either the left or right pane to compare.
+ - Dropping 2 files simultaneously automatically loads them into the left and right panes.
+ - **"Use Current Image" Toggle:**
+ - Convenient checkbox on each pane to instantly load the image currently open in the main viewer.
+ - Automatically disables on the other pane when active to avoid duplicate/conflicting states.
+ - **Synchronized Navigation & Precise Zoom:**
+ - Mouse wheel zooms all compared images simultaneously using the exact same zoom engine (`_viv_calculate_next_zoom`) with synchronized viewport centers to eliminate drift.
+ - Left-click drag pans all compared images synchronously.
+ - **Individual Pane Mode:** Hold `Ctrl` key or use **Right Mouse Button** to zoom or pan only the hovered image pane.
+ - **Per-Pane Mini Toolbar:**
+ - Previous (`<`) and Next (`>`) buttons to browse folder/playlist images per pane.
+ - Zoom In (`+`), Zoom Out (`-`), Zoom % display, `1:1` (Actual size), and `Fit` (Best Fit) buttons.
+ - Rotate clockwise button (`90°`).
+ - Change/Select image file button (`...`).
+ - **Top Control Bar:**
+ - Helpful shortcut and mouse tips.
+ - **Auto-Fit** toggle to automatically scale images to fit pane viewports.
+ - **Smooth** toggle (Halftone / High-quality interpolation).
+ - **Filename** toggle with bright green overlay text (`filename`, `dimensions`, `file size`).
+ - **Close** button (or `ESC` key) to quickly return to main viewer.
+ - **Full Format Support & Alpha Transparency:**
+ - Native support for PNG (with alpha transparency), GIF, animated GIF, BMP, ICO, TIFF, JPEG, and WebP (via `libwebp` fallback decoder).
+ - Clean alpha blending over the window background color, preventing black background issues on transparent images.
+ - **Full Multilingual Localization:** Added translations for all compare UI elements in English (`en-US`), Indonesian (`id-ID`), and Simplified Chinese (`zh-CN`).
+
+### Version 1.1.0.0
+- **New Feature: Zoom Options Tab & Calculation Engine**
+ - Added dedicated **Zoom options** tab in Options dialog (`IDD_ZOOM`).
+ - Added customizable zoom calculation methods:
+ - **Relative step:** Multiplicative scaling using configured zoom step % (default: 10%).
+ - **Absolute step:** Additive scaling by fixed percentage point steps.
+ - **Fixed table:** Step-by-step navigation through a user-defined list of zoom percentage levels (default: `1;2;3;10;14;16;19;21;25;28;33;38;43;50;57;66;76;87;100;115;132;150;174;200;230;260;300;350;400;450;500;600;700;800;900;1000;1200;1400;1600;2000;3000;`).
+ - Added **Load default table** button to quickly restore default values.
+- **Improved Zoom Handling:** Pixel under cursor remains stationary during mouse wheel and shortcut zooms across all zoom methods.
+- **Expanded Localization:** Added full UI translations for the Zoom options page in English (`en-US`), Indonesian (`id-ID`), and Simplified Chinese (`zh-CN`).
+- **Configuration Persistence:** Zoom settings (`zoom_calc_method`, `zoom_step`, `zoom_fixed_table`) automatically saved to and loaded from `voidImageViewer.ini`.
+
+| Feature / Behavior | Before (v1.0.1.0) | After (v1.1.0.0) |
+| :--- | :--- | :--- |
+| **Zoom Calculation Method** | Hardcoded fixed exponential power steps (`_viv_zoom_pos` from 0 to 63) | Choice between **Relative step (%)**, **Absolute step (%)**, or customizable **Fixed table** |
+| **Zoom Step Config** | Fixed / non-configurable | Configurable zoom step percentage (default `10%`) |
+| **Fixed Table Values** | Not available | Semicolon-delimited custom table support (up to 50 ordered values) with default reset |
+| **Settings UI** | 3 tabs (*General*, *View*, *Controls*) | 4 tabs (*General*, *View*, *Controls*, ***Zoom options***) |
+| **Localization** | Dialog strings for 3 tabs | Full multilingual translations for all 4 tabs (English, Indonesian, Chinese) |
+| **INI Storage** | No zoom calculation preferences saved | Saves `zoom_calc_method`, `zoom_step`, `zoom_fixed_table` to INI |
+
+
+
See also
--------
https://www.voidtools.com/forum/viewtopic.php?t=5623
+
diff --git a/comments.json b/comments.json
new file mode 100644
index 00000000..a65ec5fd
Binary files /dev/null and b/comments.json differ
diff --git a/issues.json b/issues.json
new file mode 100644
index 00000000..811c766b
Binary files /dev/null and b/issues.json differ
diff --git a/nsis/version.nsh b/nsis/version.nsh
index 6e65d04c..75f01347 100644
--- a/nsis/version.nsh
+++ b/nsis/version.nsh
@@ -1,3 +1,4 @@
-!define VERSION "1.0.0.15"
+!define VERSION "1.2.1.0"
!define BETAVERSION ""
!define VERSIONYEAR "2026"
+
diff --git a/res/resource.h b/res/resource.h
index e3639f70..32e21440 100644
--- a/res/resource.h
+++ b/res/resource.h
@@ -25,6 +25,7 @@
#define IDD_RENAME 127
#define IDD_JUMPTO 128
#define IDD_EVERYTHING 129
+#define IDD_ZOOM 130
#define IDC_TAB1 1003
#define IDC_BMP 1006
#define IDC_GIF 1007
@@ -90,15 +91,43 @@
#define IDC_TITLE_BAR_FORMAT 1052
#define IDC_SHRINK_BLIT_MODE_STATIC 1053
#define IDC_MAGNIFY_BLIT_MODE_STATIC 1054
+#define IDC_RESET_WINDOW 1055
+#define IDC_LANGUAGE_STATIC 1056
+#define IDC_LANGUAGE_COMBOBOX 1057
+#define IDC_LOOP_ANIMATIONS_ONCE_STATIC 1058
+#define IDC_PRELOAD_NEXT_IMAGE_STATIC 1059
+#define IDC_CACHE_LAST_IMAGE_STATIC 1060
+#define IDC_WINDOWEDBACKGROUNDCOLOR_STATIC 1061
+#define IDC_FULLSCREENBACKGROUNDCOLOR_STATIC 1062
+#define IDC_LEFT_CLICK_ACTION_STATIC 1063
+#define IDC_RIGHT_CLICK_ACTION_STATIC 1064
+#define IDC_MOUSE_WHEEL_ACTION_STATIC 1065
+#define IDC_COMMANDS_STATIC 1066
+#define IDC_SETTINGS_FOR_SELECTED_COMMAND_STATIC 1067
+#define IDC_TITLE_BAR_FORMAT_STATIC 1068
+#define IDC_AUTO_SIZE_WINDOW_STATIC 1069
+#define IDC_EDIT_KEYBOARD_SHORTCUT_KEY_STATIC 1070
+#define IDC_EDIT_KEYBOARD_SHORTCUT_KEY_CURRENTLY_USED_BY_STATIC 1071
+#define IDC_CUSTOM_RATE_STATIC 1072
+#define IDC_TAB4 1073
+#define IDC_ZOOM_STEP_STATIC 1074
+#define IDC_ZOOM_STEP_EDIT 1075
+#define IDC_ZOOM_STEP_PERCENT_STATIC 1076
+#define IDC_ZOOM_STEP_DEFAULT_STATIC 1077
+#define IDC_ZOOM_CALC_METHOD_STATIC 1078
+#define IDC_ZOOM_CALC_RELATIVE_RADIO 1079
+#define IDC_ZOOM_CALC_ABSOLUTE_RADIO 1080
+#define IDC_ZOOM_CALC_FIXED_TABLE_RADIO 1081
+#define IDC_ZOOM_FIXED_TABLE_EDIT 1082
+#define IDC_ZOOM_LOAD_DEFAULT_TABLE_BUTTON 1083
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
-#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_RESOURCE_VALUE 131
#define _APS_NEXT_COMMAND_VALUE 40048
-#define _APS_NEXT_CONTROL_VALUE 1055
-#define _APS_NEXT_SYMED_VALUE 109
+#define _APS_NEXT_CONTROL_VALUE 1084
#endif
#endif
diff --git a/res/voidImageViewer.rc b/res/voidImageViewer.rc
index b138421b..1c6b57ee 100644
--- a/res/voidImageViewer.rc
+++ b/res/voidImageViewer.rc
@@ -26,86 +26,109 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// Dialog
//
-IDD_GENERAL DIALOGEX 0, 0, 194, 180
+IDD_GENERAL DIALOGEX 0, 0, 244, 200
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
+ LTEXT "&Language:",IDC_LANGUAGE_STATIC,0,2,50,12,SS_CENTERIMAGE
+ COMBOBOX IDC_LANGUAGE_COMBOBOX,54,0,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "&Store settings in %APPDATA%\\voidImageViewer",IDC_APPDATA,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,0,186,10
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,20,186,10
CONTROL "Allow multiple &instances",IDC_MULTIPLE_INSTANCES,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,18,186,10
- CONTROL "Start &menu shortcuts",IDC_STARTMENU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,36,186,10
- GROUPBOX "Associations",IDC_STATIC,0,54,54,124
- CONTROL "&BMP",IDC_BMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,66,42,10
- CONTROL "&GIF",IDC_GIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,78,42,10
- CONTROL "IC&O",IDC_ICO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,90,42,10
- CONTROL "JP&EG",IDC_JPEG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,102,42,10
- CONTROL "&JPG",IDC_JPG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,114,42,10
- CONTROL "&PNG",IDC_PNG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,126,42,10
- CONTROL "&TIF",IDC_TIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,138,42,10
- CONTROL "TIF&F",IDC_TIFF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,150,42,10
- CONTROL "&WEBP",IDC_WEBP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,162,42,10
- PUSHBUTTON "Check &All",IDC_CHECKALL,60,60,54,14
- PUSHBUTTON "Check &None",IDC_CHECKNONE,60,78,54,14
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,38,186,10
+ CONTROL "Start &menu shortcuts",IDC_STARTMENU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,56,186,10
+ GROUPBOX "Associations",IDC_STATIC,0,74,54,124
+ CONTROL "&BMP",IDC_BMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,86,42,10
+ CONTROL "&GIF",IDC_GIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,98,42,10
+ CONTROL "IC&O",IDC_ICO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,110,42,10
+ CONTROL "JP&EG",IDC_JPEG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,122,42,10
+ CONTROL "&JPG",IDC_JPG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,134,42,10
+ CONTROL "&PNG",IDC_PNG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,146,42,10
+ CONTROL "&TIF",IDC_TIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,158,42,10
+ CONTROL "TIF&F",IDC_TIFF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,170,42,10
+ CONTROL "&WEBP",IDC_WEBP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,182,42,10
+ PUSHBUTTON "Check &All",IDC_CHECKALL,60,80,54,14
+ PUSHBUTTON "Check &None",IDC_CHECKNONE,60,98,54,14
END
-IDD_OPTIONS DIALOGEX 0, 0, 310, 271
+IDD_OPTIONS DIALOGEX 0, 0, 360, 271
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
CAPTION "Options - Void Image Viewer"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
- DEFPUSHBUTTON "OK",IDOK,198,252,50,14,WS_GROUP
- PUSHBUTTON "Cancel",IDCANCEL,252,252,50,14
+ DEFPUSHBUTTON "OK",IDOK,248,252,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,302,252,50,14
CONTROL "",IDC_TREE1,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_TRACKSELECT | WS_BORDER | WS_TABSTOP,6,6,84,240
- CONTROL "",IDC_TAB1,"SysTabControl32",WS_TABSTOP,96,6,210,240
- CONTROL "",IDC_TAB2,"SysTabControl32",WS_TABSTOP,96,6,210,240
- CONTROL "",IDC_TAB3,"SysTabControl32",WS_TABSTOP,96,6,210,240
- LTEXT "Static",IDC_PAGEPLACEHOLDER,106,26,186,214,NOT WS_VISIBLE
+ CONTROL "",IDC_TAB1,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB2,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB3,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB4,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ LTEXT "Static",IDC_PAGEPLACEHOLDER,106,26,236,214,NOT WS_VISIBLE
END
-IDD_VIEW DIALOGEX 0, 0, 194, 183
+IDD_VIEW DIALOGEX 0, 0, 244, 183
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
- LTEXT "&Shrink blit mode:",IDC_SHRINK_BLIT_MODE_STATIC,0,0,74,12,SS_CENTERIMAGE
- COMBOBOX IDC_COMBO1,74,0,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "&Magnify blit mode:",IDC_MAGNIFY_BLIT_MODE_STATIC,0,19,74,8
- COMBOBOX IDC_COMBO2,74,17,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "&Title bar format:",IDC_STATIC,0,36,74,8
- COMBOBOX IDC_TITLE_BAR_FORMAT,74,34,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- CONTROL "Auto si&ze window:",IDC_AUTO_ZOOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,53,74,10
- COMBOBOX IDC_COMBO4,74,51,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- CONTROL "&Play animations at least once in slideshow",IDC_LOOP_ANIMATIONS_ONCE,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,70,192,10
- CONTROL "Preload &next image",IDC_PRELOAD_NEXT_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,87,192,10
- CONTROL "Cache &last image",IDC_CACHE_LAST_IMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,103,192,10
- LTEXT "&Windowed background color:",IDC_STATIC,0,122,96,8
- PUSHBUTTON "",IDC_WINDOWEDBACKGROUNDCOLOR,96,120,50,14,BS_BITMAP
- LTEXT "&Fullscreen background color:",IDC_STATIC,0,140,96,8
- PUSHBUTTON "",IDC_FULLSCREENBACKGROUNDCOLOR,96,138,50,14,BS_BITMAP
+ LTEXT "&Shrink blit mode:",IDC_SHRINK_BLIT_MODE_STATIC,0,0,100,12,SS_CENTERIMAGE
+ COMBOBOX IDC_COMBO1,104,0,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Magnify blit mode:",IDC_MAGNIFY_BLIT_MODE_STATIC,0,19,100,8
+ COMBOBOX IDC_COMBO2,104,17,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Title bar format:",IDC_TITLE_BAR_FORMAT_STATIC,0,36,100,8
+ COMBOBOX IDC_TITLE_BAR_FORMAT,104,34,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Auto si&ze window:",IDC_AUTO_ZOOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,53,100,10
+ COMBOBOX IDC_COMBO4,104,51,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "&Play animations at least once in slideshow",IDC_LOOP_ANIMATIONS_ONCE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,70,236,10
+ CONTROL "Preload &next image",IDC_PRELOAD_NEXT_IMAGE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,87,236,10
+ CONTROL "Cache &last image",IDC_CACHE_LAST_IMAGE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,103,236,10
+ LTEXT "&Windowed background color:",IDC_WINDOWEDBACKGROUNDCOLOR_STATIC,0,122,120,8
+ PUSHBUTTON "",IDC_WINDOWEDBACKGROUNDCOLOR,124,120,50,14,BS_BITMAP
+ LTEXT "&Fullscreen background color:",IDC_FULLSCREENBACKGROUNDCOLOR_STATIC,0,140,120,8
+ PUSHBUTTON "",IDC_FULLSCREENBACKGROUNDCOLOR,124,138,50,14,BS_BITMAP
+ PUSHBUTTON "Reset Window Size && Position",IDC_RESET_WINDOW,0,158,150,14
END
-IDD_CONTROLS DIALOGEX 0, 0, 194, 216
+IDD_CONTROLS DIALOGEX 0, 0, 244, 216
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
- LTEXT "&Left click action:",IDC_STATIC,0,0,54,12,SS_CENTERIMAGE
- COMBOBOX IDC_LEFTCLICKACTION,74,0,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "&Right click action:",IDC_STATIC,0,19,63,8
- COMBOBOX IDC_RIGHTCLICKACTION,74,17,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "&Mouse wheel action:",IDC_STATIC,0,36,72,8
- COMBOBOX IDC_MOUSEWHEELACTION,74,34,119,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "&Commands:",IDC_STATIC,0,53,57,8
- LISTBOX IDC_COMMANDS_LIST,0,64,193,78,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
- GROUPBOX "Settings for selected command",IDC_STATIC,0,148,193,67
+ LTEXT "&Left click action:",IDC_LEFT_CLICK_ACTION_STATIC,0,0,70,12,SS_CENTERIMAGE
+ COMBOBOX IDC_LEFTCLICKACTION,74,0,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Right click action:",IDC_RIGHT_CLICK_ACTION_STATIC,0,19,70,8
+ COMBOBOX IDC_RIGHTCLICKACTION,74,17,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Mouse wheel action:",IDC_MOUSE_WHEEL_ACTION_STATIC,0,36,70,8
+ COMBOBOX IDC_MOUSEWHEELACTION,74,34,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Commands:",IDC_COMMANDS_STATIC,0,53,70,8
+ LISTBOX IDC_COMMANDS_LIST,0,64,244,78,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Settings for selected command",IDC_SETTINGS_FOR_SELECTED_COMMAND_STATIC,0,148,244,67
LISTBOX IDC_KEYS_LIST,7,158,125,50,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
- PUSHBUTTON "&Add...",IDC_ADD_KEY,138,158,50,14
- PUSHBUTTON "&Edit...",IDC_EDIT_KEY,137,177,50,14
- PUSHBUTTON "Remo&ve",IDC_REMOVE_KEY,137,195,50,14
+ PUSHBUTTON "&Add...",IDC_ADD_KEY,188,158,50,14
+ PUSHBUTTON "&Edit...",IDC_EDIT_KEY,187,177,50,14
+ PUSHBUTTON "Remo&ve",IDC_REMOVE_KEY,187,195,50,14
+END
+
+IDD_ZOOM DIALOGEX 0, 0, 244, 200
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Zoom &step:",IDC_ZOOM_STEP_STATIC,0,2,42,12,SS_CENTERIMAGE
+ EDITTEXT IDC_ZOOM_STEP_EDIT,46,2,24,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "%",IDC_ZOOM_STEP_PERCENT_STATIC,73,2,8,12,SS_CENTERIMAGE
+ LTEXT "(default: 10 %)",IDC_ZOOM_STEP_DEFAULT_STATIC,86,2,100,12,SS_CENTERIMAGE
+ LTEXT "Zoom calculation method:",IDC_ZOOM_CALC_METHOD_STATIC,0,22,244,10
+ CONTROL "&Relative step",IDC_ZOOM_CALC_RELATIVE_RADIO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,10,36,220,10
+ CONTROL "&Absolute step",IDC_ZOOM_CALC_ABSOLUTE_RADIO,"Button",BS_AUTORADIOBUTTON,10,50,220,10
+ CONTROL "Use &fixed table: (for experts: max. 50 ordered values from 3 to 4000)",IDC_ZOOM_CALC_FIXED_TABLE_RADIO,"Button",BS_AUTORADIOBUTTON,10,64,234,10
+ EDITTEXT IDC_ZOOM_FIXED_TABLE_EDIT,18,78,140,14,ES_AUTOHSCROLL | WS_TABSTOP
+ PUSHBUTTON "&Load default table",IDC_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,162,78,76,14,WS_TABSTOP
END
IDD_CUSTOM_RATE DIALOGEX 0, 0, 202, 63
@@ -205,8 +228,8 @@ IDI_BESTFIT ICON "bestfit.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,0,0,15
- PRODUCTVERSION 1,0,0,15
+ FILEVERSION 1,2,1,0
+ PRODUCTVERSION 1,2,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -222,12 +245,12 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "voidImageViewer"
- VALUE "FileVersion", "1.0.0.15"
+ VALUE "FileVersion", "1.2.1.0"
VALUE "InternalName", "voidImageViewer"
- VALUE "LegalCopyright", "Copyright 2026 voidtools"
+ VALUE "LegalCopyright", "Copyright © 2026 voidtools"
VALUE "OriginalFilename", "voidImageViewer.exe"
VALUE "ProductName", "voidImageViewer"
- VALUE "ProductVersion", "1.0.0.15"
+ VALUE "ProductVersion", "1.2.1.0"
END
END
BLOCK "VarFileInfo"
@@ -287,3 +310,5 @@ END
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
+
+
diff --git a/res/voidImageViewer_utf8.rc b/res/voidImageViewer_utf8.rc
new file mode 100644
index 00000000..b7657fca
--- /dev/null
+++ b/res/voidImageViewer_utf8.rc
@@ -0,0 +1,312 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_GENERAL DIALOGEX 0, 0, 244, 200
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "&Language:",IDC_LANGUAGE_STATIC,0,2,50,12,SS_CENTERIMAGE
+ COMBOBOX IDC_LANGUAGE_COMBOBOX,54,0,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "&Store settings in %APPDATA%\\voidImageViewer",IDC_APPDATA,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,20,186,10
+ CONTROL "Allow multiple &instances",IDC_MULTIPLE_INSTANCES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,38,186,10
+ CONTROL "Start &menu shortcuts",IDC_STARTMENU,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,56,186,10
+ GROUPBOX "Associations",IDC_STATIC,0,74,54,124
+ CONTROL "&BMP",IDC_BMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,86,42,10
+ CONTROL "&GIF",IDC_GIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,98,42,10
+ CONTROL "IC&O",IDC_ICO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,110,42,10
+ CONTROL "JP&EG",IDC_JPEG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,122,42,10
+ CONTROL "&JPG",IDC_JPG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,134,42,10
+ CONTROL "&PNG",IDC_PNG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,146,42,10
+ CONTROL "&TIF",IDC_TIF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,158,42,10
+ CONTROL "TIF&F",IDC_TIFF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,170,42,10
+ CONTROL "&WEBP",IDC_WEBP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,182,42,10
+ PUSHBUTTON "Check &All",IDC_CHECKALL,60,80,54,14
+ PUSHBUTTON "Check &None",IDC_CHECKNONE,60,98,54,14
+END
+
+IDD_OPTIONS DIALOGEX 0, 0, 360, 271
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Options - Void Image Viewer"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,248,252,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,302,252,50,14
+ CONTROL "",IDC_TREE1,"SysTreeView32",TVS_SHOWSELALWAYS | TVS_TRACKSELECT | WS_BORDER | WS_TABSTOP,6,6,84,240
+ CONTROL "",IDC_TAB1,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB2,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB3,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ CONTROL "",IDC_TAB4,"SysTabControl32",WS_TABSTOP,96,6,260,240
+ LTEXT "Static",IDC_PAGEPLACEHOLDER,106,26,236,214,NOT WS_VISIBLE
+END
+
+IDD_VIEW DIALOGEX 0, 0, 244, 183
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "&Shrink blit mode:",IDC_SHRINK_BLIT_MODE_STATIC,0,0,100,12,SS_CENTERIMAGE
+ COMBOBOX IDC_COMBO1,104,0,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Magnify blit mode:",IDC_MAGNIFY_BLIT_MODE_STATIC,0,19,100,8
+ COMBOBOX IDC_COMBO2,104,17,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Title bar format:",IDC_TITLE_BAR_FORMAT_STATIC,0,36,100,8
+ COMBOBOX IDC_TITLE_BAR_FORMAT,104,34,135,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Auto si&ze window:",IDC_AUTO_ZOOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,53,100,10
+ COMBOBOX IDC_COMBO4,104,51,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "&Play animations at least once in slideshow",IDC_LOOP_ANIMATIONS_ONCE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,70,236,10
+ CONTROL "Preload &next image",IDC_PRELOAD_NEXT_IMAGE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,87,236,10
+ CONTROL "Cache &last image",IDC_CACHE_LAST_IMAGE_STATIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,0,103,236,10
+ LTEXT "&Windowed background color:",IDC_WINDOWEDBACKGROUNDCOLOR_STATIC,0,122,120,8
+ PUSHBUTTON "",IDC_WINDOWEDBACKGROUNDCOLOR,124,120,50,14,BS_BITMAP
+ LTEXT "&Fullscreen background color:",IDC_FULLSCREENBACKGROUNDCOLOR_STATIC,0,140,120,8
+ PUSHBUTTON "",IDC_FULLSCREENBACKGROUNDCOLOR,124,138,50,14,BS_BITMAP
+ PUSHBUTTON "Reset Window Size && Position",IDC_RESET_WINDOW,0,158,150,14
+END
+
+IDD_CONTROLS DIALOGEX 0, 0, 244, 216
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "&Left click action:",IDC_LEFT_CLICK_ACTION_STATIC,0,0,70,12,SS_CENTERIMAGE
+ COMBOBOX IDC_LEFTCLICKACTION,74,0,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Right click action:",IDC_RIGHT_CLICK_ACTION_STATIC,0,19,70,8
+ COMBOBOX IDC_RIGHTCLICKACTION,74,17,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Mouse wheel action:",IDC_MOUSE_WHEEL_ACTION_STATIC,0,36,70,8
+ COMBOBOX IDC_MOUSEWHEELACTION,74,34,165,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Commands:",IDC_COMMANDS_STATIC,0,53,70,8
+ LISTBOX IDC_COMMANDS_LIST,0,64,244,78,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Settings for selected command",IDC_SETTINGS_FOR_SELECTED_COMMAND_STATIC,0,148,244,67
+ LISTBOX IDC_KEYS_LIST,7,158,125,50,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "&Add...",IDC_ADD_KEY,188,158,50,14
+ PUSHBUTTON "&Edit...",IDC_EDIT_KEY,187,177,50,14
+ PUSHBUTTON "Remo&ve",IDC_REMOVE_KEY,187,195,50,14
+END
+
+IDD_ZOOM DIALOGEX 0, 0, 244, 200
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Zoom &step:",IDC_ZOOM_STEP_STATIC,0,2,42,12,SS_CENTERIMAGE
+ EDITTEXT IDC_ZOOM_STEP_EDIT,46,2,24,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "%",IDC_ZOOM_STEP_PERCENT_STATIC,73,2,8,12,SS_CENTERIMAGE
+ LTEXT "(default: 10 %)",IDC_ZOOM_STEP_DEFAULT_STATIC,86,2,100,12,SS_CENTERIMAGE
+ LTEXT "Zoom calculation method:",IDC_ZOOM_CALC_METHOD_STATIC,0,22,244,10
+ CONTROL "&Relative step",IDC_ZOOM_CALC_RELATIVE_RADIO,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,10,36,220,10
+ CONTROL "&Absolute step",IDC_ZOOM_CALC_ABSOLUTE_RADIO,"Button",BS_AUTORADIOBUTTON,10,50,220,10
+ CONTROL "Use &fixed table: (for experts: max. 50 ordered values from 3 to 4000)",IDC_ZOOM_CALC_FIXED_TABLE_RADIO,"Button",BS_AUTORADIOBUTTON,10,64,234,10
+ EDITTEXT IDC_ZOOM_FIXED_TABLE_EDIT,18,78,140,14,ES_AUTOHSCROLL | WS_TABSTOP
+ PUSHBUTTON "&Load default table",IDC_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,162,78,76,14,WS_TABSTOP
+END
+
+IDD_CUSTOM_RATE DIALOGEX 0, 0, 202, 63
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+CAPTION "Set Custom Rate"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ EDITTEXT IDC_CUSTOM_RATE_EDIT,54,12,66,12,ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT
+ DEFPUSHBUTTON "OK",IDOK,90,36,48,14
+ PUSHBUTTON "Cancel",IDCANCEL,144,36,48,14
+ LTEXT "&Custom rate:",IDC_STATIC,6,14,45,8
+ COMBOBOX IDC_CUSTOM_RATE_TYPE_COMBO,126,12,66,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_ABOUT DIALOGEX 0, 0, 228, 189
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+CAPTION "About void Image Viewer"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,120,168,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,174,168,50,14
+ LTEXT "",IDC_ABOUTBACK,0,0,228,54
+ EDITTEXT IDC_ABOUTTITLE,30,18,192,30,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ EDITTEXT IDC_ABOUTVOIDIMAGEVIEWER,30,66,192,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ EDITTEXT IDC_ABOUTVERSION,30,84,192,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ EDITTEXT IDC_ABOUTCOPYRIGHT,30,102,192,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ EDITTEXT IDC_ABOUTEMAIL,30,120,192,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ EDITTEXT IDC_ABOUTWEBSITE,30,138,192,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+END
+
+IDD_EDIT_KEY DIALOGEX 0, 0, 186, 119
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Edit Keyboard Shortcut"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Shortcut &key:",IDC_STATIC,7,7,44,8
+ EDITTEXT IDC_EDIT_KEY_EDIT,7,17,172,14,ES_AUTOHSCROLL | ES_WANTRETURN
+ LTEXT "Shortcut key currently used by:",IDC_STATIC,7,36,102,8
+ LISTBOX IDC_EDIT_KEY_CURRENTLY_USED_BY_LIST,7,47,172,43,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK",IDOK,70,98,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,98,50,14
+END
+
+IDD_RENAME DIALOGEX 0, 0, 186, 51
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Rename"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ EDITTEXT IDC_RENAME_EDIT,7,7,172,14,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,74,30,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,30,50,14
+ EDITTEXT IDC_RENAME_OLD_EDIT,7,30,40,14,ES_AUTOHSCROLL | NOT WS_VISIBLE
+END
+
+IDD_JUMPTO DIALOGEX 0, 0, 187, 229
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Jump To"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ EDITTEXT IDC_JUMPTO_EDIT,7,7,173,14,ES_AUTOHSCROLL | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+ LISTBOX IDC_JUMPTO_LIST,7,29,173,170,WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK",IDOK,74,208,50,14,WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+ PUSHBUTTON "Cancel",IDCANCEL,130,208,50,14,WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+END
+
+IDD_EVERYTHING DIALOGEX 0, 0, 186, 63
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Search Everything"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ EDITTEXT IDC_EVERYTHING_EDIT,6,6,173,14,ES_AUTOHSCROLL
+ CONTROL "Randomize",IDC_SEARCH_EVERYTHING_RANDOM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,24,51,10
+ DEFPUSHBUTTON "OK",IDOK,72,42,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,127,42,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "voidImageViewer.ico"
+IDI_PREV ICON "prev.ico"
+IDI_PLAY ICON "play.ico"
+IDI_NEXT ICON "next.ico"
+IDI_PAUSE ICON "pause.ico"
+IDI_1TO1 ICON "1to1-8bit.ico"
+IDI_BESTFIT ICON "bestfit.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,2,1,0
+ PRODUCTVERSION 1,2,1,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x0L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "voidImageViewer"
+ VALUE "FileVersion", "1.2.1.0"
+ VALUE "InternalName", "voidImageViewer"
+ VALUE "LegalCopyright", "Copyright © 2026 voidtools"
+ VALUE "OriginalFilename", "voidImageViewer.exe"
+ VALUE "ProductName", "voidImageViewer"
+ VALUE "ProductVersion", "1.2.1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (Australia) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (Australia) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/scratch_version.ps1 b/scratch_version.ps1
new file mode 100644
index 00000000..91d9bb86
--- /dev/null
+++ b/scratch_version.ps1
@@ -0,0 +1,4 @@
+$c = Get-Content res\voidImageViewer.rc -Raw
+$c = $c -replace '1,0,0,15', '1,0,1,0'
+$c = $c -replace '1\.0\.0\.15', '1.0.1.0'
+Set-Content res\voidImageViewer.rc $c -Encoding Default
diff --git a/src/config.c b/src/config.c
index 719eb0c1..c7285193 100644
--- a/src/config.c
+++ b/src/config.c
@@ -29,7 +29,8 @@ static void _config_write_string(HANDLE h,const char *ascii_key,const wchar_t *s
static void _config_write_utf8(HANDLE h,const utf8_t *s);
static void _config_save_settings_by_location(const wchar_t *path,int is_root);
-BYTE config_appdata = 0; // store settings in %APPDATA%\voidimageviewer or in the same location as voidimageviewer.exe
+BYTE config_appdata = 1; // store settings in %APPDATA%\voidimageviewer or in the same location as voidimageviewer.exe
+BYTE config_language = 255; // 255 = auto
BYTE config_keep_centered = 1; // when zooming out, don't recenter the image. (keep cursor under the same pixel)
int config_x = 0;
int config_y = 0;
@@ -40,8 +41,8 @@ int config_slideshow_rate = 5000;
BYTE config_allow_shrinking = 1; // prevent resizing an image below 100%
BYTE config_shrink_blit_mode = CONFIG_SHRINK_BLIT_MODE_HALFTONE; // shrink filter
BYTE config_mag_filter = CONFIG_MAG_FILTER_COLORONCOLOR; // magnify filter
-BYTE config_nav_sort = CONFIG_NAV_SORT_DATE_MODIFIED; // current navigation sort.
-BYTE config_nav_sort_ascending = 0; // sort navigation ascending or descending.
+BYTE config_nav_sort = CONFIG_NAV_SORT_NAME; // current navigation sort.
+BYTE config_nav_sort_ascending = 1; // sort navigation ascending or descending.
BYTE config_keep_aspect_ratio = 1; // stretch images with the original aspect ratio.
BYTE config_fill_window = 0; // stretch the image to fill the window
BYTE config_fullscreen_fill_window = 1; // same as fill_window, except this setting is used when we are fullscreen
@@ -90,6 +91,9 @@ BYTE config_pixel_info = 0;
BYTE config_orientation = 1;
BYTE config_title_bar_format = 1; // 0=full path, 1=filename, 2=none
int config_add_command_line_timeout = 500; // in milliseconds
+BYTE config_zoom_calc_method = CONFIG_ZOOM_CALC_RELATIVE;
+int config_zoom_step = CONFIG_ZOOM_DEFAULT_STEP;
+wchar_t config_zoom_fixed_table[STRING_SIZE] = CONFIG_ZOOM_DEFAULT_FIXED_TABLE;
static void _config_load_settings_by_location(const wchar_t *path,int is_root)
{
@@ -102,6 +106,7 @@ static void _config_load_settings_by_location(const wchar_t *path,int is_root)
if (ini)
{
config_x = ini_get_int(ini,(const utf8_t *)"x",config_x);
+ config_language = ini_get_int(ini,(const utf8_t *)"language",config_language);
config_y = ini_get_int(ini,(const utf8_t *)"y",config_y);
config_wide = ini_get_int(ini,(const utf8_t *)"wide",config_wide);
config_high = ini_get_int(ini,(const utf8_t *)"high",config_high);
@@ -161,6 +166,15 @@ static void _config_load_settings_by_location(const wchar_t *path,int is_root)
config_toolbar_move_window = ini_get_int(ini,(const utf8_t *)"toolbar_move_window",config_toolbar_move_window);
config_title_bar_format = ini_get_int(ini,(const utf8_t *)"title_bar_format",config_title_bar_format);
config_add_command_line_timeout = ini_get_int(ini,(const utf8_t *)"add_command_line_timeout",config_add_command_line_timeout);
+ config_zoom_calc_method = ini_get_int(ini,(const utf8_t *)"zoom_calc_method",config_zoom_calc_method);
+ config_zoom_step = ini_get_int(ini,(const utf8_t *)"zoom_step",config_zoom_step);
+ {
+ const utf8_t *fixed_table = ini_get_string(ini,(const utf8_t *)"zoom_fixed_table");
+ if (fixed_table && *fixed_table)
+ {
+ string_copy_utf8_string(config_zoom_fixed_table,fixed_table);
+ }
+ }
if (is_root)
{
@@ -286,6 +300,7 @@ static void _config_save_settings_by_location(const wchar_t *path,int is_root)
else
{
_config_write_int(h,"x",config_x);
+ _config_write_int(h,"language",config_language);
_config_write_int(h,"y",config_y);
_config_write_int(h,"wide",config_wide);
_config_write_int(h,"high",config_high);
@@ -345,6 +360,9 @@ static void _config_save_settings_by_location(const wchar_t *path,int is_root)
_config_write_int(h,"toolbar_move_window",config_toolbar_move_window);
_config_write_int(h,"title_bar_format",config_title_bar_format);
_config_write_int(h,"add_command_line_timeout",config_add_command_line_timeout);
+ _config_write_int(h,"zoom_calc_method",config_zoom_calc_method);
+ _config_write_int(h,"zoom_step",config_zoom_step);
+ _config_write_string(h,"zoom_fixed_table",config_zoom_fixed_table);
// save keys
{
diff --git a/src/config.h b/src/config.h
index 6a6afa13..953bb92e 100644
--- a/src/config.h
+++ b/src/config.h
@@ -55,10 +55,19 @@ typedef struct config_key_s
#define CONFIG_MAG_FILTER_COLORONCOLOR 0
#define CONFIG_MAG_FILTER_HALFTONE 1
+#define CONFIG_ZOOM_CALC_RELATIVE 0
+#define CONFIG_ZOOM_CALC_ABSOLUTE 1
+#define CONFIG_ZOOM_CALC_FIXED_TABLE 2
+
+#define CONFIG_ZOOM_DEFAULT_STEP 10
+#define CONFIG_ZOOM_DEFAULT_FIXED_TABLE L"1;2;3;10;14;16;19;21;25;28;33;38;43;50;57;66;76;87;100;115;132;150;174;200;230;260;300;350;400;450;500;600;700;800;900;1000;1200;1400;1600;2000;3000;"
+#define CONFIG_ZOOM_DEFAULT_FIXED_TABLE_A "1;2;3;10;14;16;19;21;25;28;33;38;43;50;57;66;76;87;100;115;132;150;174;200;230;260;300;350;400;450;500;600;700;800;900;1000;1200;1400;1600;2000;3000;"
+
void config_load_settings(void);
void config_save_settings(int appdata);
extern BYTE config_appdata;
+extern BYTE config_language;
extern BYTE config_keep_centered;
extern int config_x;
extern int config_y;
@@ -115,11 +124,13 @@ extern BYTE config_show_caption;
extern BYTE config_show_thickframe;
extern BYTE config_toolbar_move_window;
extern BYTE config_windowed_hide_cursor;
-extern BYTE config_auto_zoom;
extern BYTE config_pixel_info;
extern BYTE config_orientation;
extern BYTE config_title_bar_format;
extern int config_add_command_line_timeout;
+extern BYTE config_zoom_calc_method;
+extern int config_zoom_step;
+extern wchar_t config_zoom_fixed_table[STRING_SIZE];
#ifdef __cplusplus
}
diff --git a/src/localization.c b/src/localization.c
index 691e4716..9e58b292 100644
--- a/src/localization.c
+++ b/src/localization.c
@@ -24,11 +24,13 @@
#include "viv.h"
#include "localization_en_us.h"
#include "localization_zh_cn.h"
+#include "localization_id_id.h"
static const utf8_t **_localization_language_array[LOCALIZATION_LANGUAGE_COUNT] =
{
_localization_string_array_en_us, // LOCALIZATION_LANGUAGE_ENGLISH
_localization_string_array_zh_cn, // LOCALIZATION_LANGUAGE_CHINESE_SIMPLIFIED
+ _localization_string_array_id_id, // LOCALIZATION_LANGUAGE_INDONESIAN
};
BYTE localization_language = LOCALIZATION_LANGUAGE_ENGLISH;
@@ -54,6 +56,16 @@ const utf8_t *localization_get_en_us_string(localization_id_t localization_id)
void localization_init(void)
{
+ // If config_language is not Auto, use it
+ if (config_language != 255)
+ {
+ if (config_language < LOCALIZATION_LANGUAGE_COUNT)
+ {
+ localization_language = config_language;
+ return;
+ }
+ }
+
// Detect system language
if (os_GetUserDefaultUILanguage)
{
@@ -69,5 +81,23 @@ void localization_init(void)
{
localization_language = LOCALIZATION_LANGUAGE_CHINESE_SIMPLIFIED;
}
+ else if (langid == 0x0421) // Indonesian
+ {
+ localization_language = LOCALIZATION_LANGUAGE_INDONESIAN;
+ }
+ else
+ {
+ localization_language = LOCALIZATION_LANGUAGE_ENGLISH;
+ }
}
}
+
+const utf8_t *localization_get_language_name(BYTE language)
+{
+ if (language < LOCALIZATION_LANGUAGE_COUNT)
+ {
+ return _localization_language_array[language][LOCALIZATION_ID_LANGUAGE];
+ }
+
+ return (const utf8_t *)"";
+}
diff --git a/src/localization.h b/src/localization.h
index 99248c5d..72c2c512 100644
--- a/src/localization.h
+++ b/src/localization.h
@@ -29,17 +29,19 @@ enum
{
LOCALIZATION_LANGUAGE_ENGLISH = 0,
LOCALIZATION_LANGUAGE_CHINESE_SIMPLIFIED,
+ LOCALIZATION_LANGUAGE_INDONESIAN,
LOCALIZATION_LANGUAGE_COUNT,
};
-typedef BYTE localization_id_t;
+typedef unsigned short localization_id_t;
-#define LOCALIZATION_ID_INVALID 255
+#define LOCALIZATION_ID_INVALID 65535
// Get localized string for a command name
// Returns UTF-8 encoded string
const utf8_t *localization_get_string(localization_id_t localization_id);
const utf8_t *localization_get_en_us_string(localization_id_t localization_id);
+const utf8_t *localization_get_language_name(BYTE language);
// Initialize language system (detects system language)
void localization_init(void);
@@ -49,6 +51,8 @@ enum
{
// Localization
LOCALIZATION_ID_LANGUAGE = 0,
+ LOCALIZATION_ID_LANGUAGE_STATIC,
+ LOCALIZATION_ID_LANGUAGE_AUTO,
// Application name
LOCALIZATION_ID_APP_NAME,
@@ -233,6 +237,7 @@ enum
LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,
LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,
LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG,
+ LOCALIZATION_ID_OPTIONS_ZOOM_DIALOG,
LOCALIZATION_ID_OK_BUTTON,
LOCALIZATION_ID_CANCEL_BUTTON,
LOCALIZATION_ID_STORE_SETTINGS_APPDATA,
@@ -255,6 +260,7 @@ enum
LOCALIZATION_ID_CACHE_LAST_IMAGE_STATIC,
LOCALIZATION_ID_WINDOWED_BACKGROUND_COLOR_STATIC,
LOCALIZATION_ID_FULLSCREEN_BACKGROUND_COLOR_STATIC,
+ LOCALIZATION_ID_OPTIONS_VIEW_RESET_WINDOW_BUTTON,
LOCALIZATION_ID_LEFT_CLICK_ACTION_STATIC,
LOCALIZATION_ID_RIGHT_CLICK_ACTION_STATIC,
LOCALIZATION_ID_MOUSE_WHEEL_ACTION_STATIC,
@@ -300,6 +306,13 @@ enum
LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FULL_PATH_COMBOBOXITEM,
LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FILENAME_ONLY_COMBOBOXITEM,
LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_NONE_COMBOBOXITEM,
+ LOCALIZATION_ID_OPTIONS_ZOOM_STEP_STATIC,
+ LOCALIZATION_ID_OPTIONS_ZOOM_STEP_DEFAULT_STATIC,
+ LOCALIZATION_ID_OPTIONS_ZOOM_CALC_METHOD_STATIC,
+ LOCALIZATION_ID_OPTIONS_ZOOM_CALC_RELATIVE_RADIO,
+ LOCALIZATION_ID_OPTIONS_ZOOM_CALC_ABSOLUTE_RADIO,
+ LOCALIZATION_ID_OPTIONS_ZOOM_CALC_FIXED_TABLE_RADIO,
+ LOCALIZATION_ID_OPTIONS_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,
LOCALIZATION_ID_EVERYTHING_NOT_AVAILABLE_MESSAGE,
LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION,
LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION,
@@ -318,6 +331,26 @@ enum
LOCALIZATION_ID_ASSOCIATION_DESCRIPTION_TIFF,
LOCALIZATION_ID_ASSOCIATION_DESCRIPTION_WEBP,
+ // Compare Images
+ LOCALIZATION_ID_COMPARE_IMAGES,
+ LOCALIZATION_ID_COMPARE_TIPS,
+ LOCALIZATION_ID_COMPARE_AUTO_FIT,
+ LOCALIZATION_ID_COMPARE_SMOOTH,
+ LOCALIZATION_ID_COMPARE_FILENAME,
+ LOCALIZATION_ID_COMPARE_CLOSE,
+ LOCALIZATION_ID_COMPARE_PREV,
+ LOCALIZATION_ID_COMPARE_NEXT,
+ LOCALIZATION_ID_COMPARE_ZOOM_IN,
+ LOCALIZATION_ID_COMPARE_ZOOM_OUT,
+ LOCALIZATION_ID_COMPARE_ACTUAL_SIZE,
+ LOCALIZATION_ID_COMPARE_BEST_FIT,
+ LOCALIZATION_ID_COMPARE_ROTATE,
+ LOCALIZATION_ID_COMPARE_SELECT_IMAGE,
+ LOCALIZATION_ID_COMPARE_USE_CURRENT,
+ LOCALIZATION_ID_COMPARE_NO_IMAGE_DROP,
+
+ LOCALIZATION_ID_SMOOTH,
+
LOCALIZATION_ID_COUNT,
};
diff --git a/src/localization_en_us.h b/src/localization_en_us.h
index 5747262c..fa7d0cbf 100644
--- a/src/localization_en_us.h
+++ b/src/localization_en_us.h
@@ -26,6 +26,8 @@ static const utf8_t *_localization_string_array_en_us[LOCALIZATION_ID_COUNT] =
{
// Application name
"English (US)", // LOCALIZATION_ID_LANGUAGE
+ "&Language:", // LOCALIZATION_ID_LANGUAGE_STATIC
+ "System Default", // LOCALIZATION_ID_LANGUAGE_AUTO
// Application name
"void Image Viewer", // LOCALIZATION_ID_APP_NAME
@@ -210,6 +212,7 @@ static const utf8_t *_localization_string_array_en_us[LOCALIZATION_ID_COUNT] =
"General", // LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,
"View", // LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,
"Controls", // LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG,
+ "Zoom options", // LOCALIZATION_ID_OPTIONS_ZOOM_DIALOG,
"OK", // LOCALIZATION_ID_OK_BUTTON,
"Cancel", // LOCALIZATION_ID_CANCEL_BUTTON,
"&Store settings in %APPDATA%\\voidImageViewer", // LOCALIZATION_ID_STORE_SETTINGS_APPDATA,
@@ -232,6 +235,7 @@ static const utf8_t *_localization_string_array_en_us[LOCALIZATION_ID_COUNT] =
"Cache &last image", // LOCALIZATION_ID_CACHE_LAST_IMAGE_STATIC,
"&Windowed background color:", // LOCALIZATION_ID_WINDOWED_BACKGROUND_COLOR_STATIC,
"&Fullscreen background color:", // LOCALIZATION_ID_FULLSCREEN_BACKGROUND_COLOR_STATIC,
+ "Reset Window Size & Position", // LOCALIZATION_ID_OPTIONS_VIEW_RESET_WINDOW_BUTTON,
"&Left click action:", // LOCALIZATION_ID_LEFT_CLICK_ACTION_STATIC,
"&Right click action:", // LOCALIZATION_ID_RIGHT_CLICK_ACTION_STATIC,
"&Mouse wheel action:", // LOCALIZATION_ID_MOUSE_WHEEL_ACTION_STATIC,
@@ -277,6 +281,13 @@ static const utf8_t *_localization_string_array_en_us[LOCALIZATION_ID_COUNT] =
"Full Path", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FULL_PATH_COMBOBOXITEM,
"Filename Only", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FILENAME_ONLY_COMBOBOXITEM,
"None", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_NONE_COMBOBOXITEM,
+ "Zoom &step:", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_STATIC,
+ "(default: 10 %)", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_DEFAULT_STATIC,
+ "Zoom calculation method:", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_METHOD_STATIC,
+ "&Relative step", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_RELATIVE_RADIO,
+ "&Absolute step", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_ABSOLUTE_RADIO,
+ "Use &fixed table: (for experts: max. 50 ordered values from 3 to 4000)", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_FIXED_TABLE_RADIO,
+ "&Load default table", // LOCALIZATION_ID_OPTIONS_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,
"Everything not available", // LOCALIZATION_ID_EVERYTHING_NOT_AVAILABLE_MESSAGE,
"Add Everything Search", // LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION,
"Load Everything Search", // LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION,
@@ -294,4 +305,23 @@ static const utf8_t *_localization_string_array_en_us[LOCALIZATION_ID_COUNT] =
"TIFF Image",
"TIFF Image",
"WebP Image",
+
+ // Compare Images
+ "Compare Images...", // LOCALIZATION_ID_COMPARE_IMAGES,
+ "Tips: Drag images with LEFT mouse button; Scroll mouse WHEEL to zoom in/out; Press Ctrl key or Right mouse button to work with individual images.", // LOCALIZATION_ID_COMPARE_TIPS,
+ "Auto-Fit", // LOCALIZATION_ID_COMPARE_AUTO_FIT,
+ "Smooth", // LOCALIZATION_ID_COMPARE_SMOOTH,
+ "Filename", // LOCALIZATION_ID_COMPARE_FILENAME,
+ "Close", // LOCALIZATION_ID_COMPARE_CLOSE,
+ "Previous", // LOCALIZATION_ID_COMPARE_PREV,
+ "Next", // LOCALIZATION_ID_COMPARE_NEXT,
+ "Zoom In", // LOCALIZATION_ID_COMPARE_ZOOM_IN,
+ "Zoom Out", // LOCALIZATION_ID_COMPARE_ZOOM_OUT,
+ "1:1", // LOCALIZATION_ID_COMPARE_ACTUAL_SIZE,
+ "Fit", // LOCALIZATION_ID_COMPARE_BEST_FIT,
+ "Rotate", // LOCALIZATION_ID_COMPARE_ROTATE,
+ "Select Image", // LOCALIZATION_ID_COMPARE_SELECT_IMAGE,
+ "Use Current Image", // LOCALIZATION_ID_COMPARE_USE_CURRENT,
+ "[No Image - Drop image here or click ... / Use Current]", // LOCALIZATION_ID_COMPARE_NO_IMAGE_DROP,
+ "&Smooth", // LOCALIZATION_ID_SMOOTH,
};
diff --git a/src/localization_id_id.h b/src/localization_id_id.h
new file mode 100644
index 00000000..e3dded6f
--- /dev/null
+++ b/src/localization_id_id.h
@@ -0,0 +1,327 @@
+//
+// Copyright 2026 hesphoros
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// Indonesian localization
+// Save as UTF-8 without BOM.
+
+static const utf8_t *_localization_string_array_id_id[LOCALIZATION_ID_COUNT] =
+{
+ // Nama bahasa
+ "Bahasa Indonesia", // LOCALIZATION_ID_LANGUAGE
+ "&Bahasa:", // LOCALIZATION_ID_LANGUAGE_STATIC
+ "Default Sistem", // LOCALIZATION_ID_LANGUAGE_AUTO
+
+ // Nama aplikasi
+ "void Image Viewer", // LOCALIZATION_ID_APP_NAME
+
+ // Menu Berkas
+ "&Berkas", // LOCALIZATION_ID_FILE,
+ "&Buka Berkas...", // LOCALIZATION_ID_OPEN_FILE,
+ "Buka &Folder...", // LOCALIZATION_ID_OPEN_FOLDER,
+ "Buka Pencarian &Everything...", // LOCALIZATION_ID_OPEN_EVERYTHING_SEARCH,
+ "&Tambah Berkas...", // LOCALIZATION_ID_ADD_FILE,
+ "Tambah Folder...", // LOCALIZATION_ID_ADD_FOLDER,
+ "Tambah Pencarian Everything...", // LOCALIZATION_ID_ADD_EVERYTHING_SEARCH,
+ "Buka &Lokasi Berkas...", // LOCALIZATION_ID_OPEN_FILE_LOCATION,
+ "&Ubah...", // LOCALIZATION_ID_EDIT,
+ "P&ratinjau...", // LOCALIZATION_ID_PREVIEW,
+ "&Cetak...", // LOCALIZATION_ID_PRINT,
+ "Jadikan Wallpaper Des&ktop", // LOCALIZATION_ID_SET_DESKTOP_WALLPAPER,
+ "&Tutup", // LOCALIZATION_ID_CLOSE,
+ "&Hapus", // LOCALIZATION_ID_DELETE,
+ "Hapus (Tong Sampah)", // LOCALIZATION_ID_DELETE_RECYCLE,
+ "Hapus (Permanen)", // LOCALIZATION_ID_DELETE_PERMANENTLY,
+ "Ubah &Nama", // LOCALIZATION_ID_RENAME,
+ "P&roperti", // LOCALIZATION_ID_PROPERTIES,
+ "&Keluar", // LOCALIZATION_ID_EXIT,
+
+ // Menu Ubah
+ "&Ubah", // LOCALIZATION_ID_EDIT_MENU,
+ "Po&tong", // LOCALIZATION_ID_CUT,
+ "&Salin", // LOCALIZATION_ID_COPY,
+ "Salin Nama Berkas", // LOCALIZATION_ID_COPY_FILENAME,
+ "Salin &Gambar", // LOCALIZATION_ID_COPY_IMAGE,
+ "&Tempel", // LOCALIZATION_ID_PASTE,
+ "Putar Se&arah Jarum Jam", // LOCALIZATION_ID_ROTATE_CLOCKWISE,
+ "Putar Ber&lawanan Jarum Jam", // LOCALIZATION_ID_ROTATE_COUNTERCLOCKWISE,
+ "Salin ke &Folder...", // LOCALIZATION_ID_COPY_TO,
+ "Pi&ndah ke Folder...", // LOCALIZATION_ID_MOVE_TO,
+
+ // Menu Tampilan
+ "&Tampilan", // LOCALIZATION_ID_VIEW,
+ "Keterangan", // LOCALIZATION_ID_CAPTION,
+ "Bingkai", // LOCALIZATION_ID_FRAME,
+ "&Menu", // LOCALIZATION_ID_MENU,
+ "Bilah &Status", // LOCALIZATION_ID_STATUS_BAR,
+ "K&ontrol", // LOCALIZATION_ID_CONTROLS,
+ "&Preset", // LOCALIZATION_ID_PRESET,
+ "&Minimalis", // LOCALIZATION_ID_MINIMAL,
+ "&Ringkas", // LOCALIZATION_ID_COMPACT,
+ "&Normal", // LOCALIZATION_ID_NORMAL,
+ "&Layar Penuh", // LOCALIZATION_ID_FULLSCREEN,
+ "&Slideshow", // LOCALIZATION_ID_SLIDESHOW,
+ "&Ukuran Jendela", // LOCALIZATION_ID_VIEW_WINDOW_SIZE,
+ "50%", // LOCALIZATION_ID_VIEW_WINDOW_SIZE_50_PERCENT,
+ "100%", // LOCALIZATION_ID_VIEW_WINDOW_SIZE_100_PERCENT,
+ "200%", // LOCALIZATION_ID_VIEW_WINDOW_SIZE_200_PERCENT,
+ "&Pas Otomatis", // LOCALIZATION_ID_VIEW_WINDOW_SIZE_AUTO_FIT,
+ "&Segarkan", // LOCALIZATION_ID_REFRESH,
+ "&Izinkan Pengecilan", // LOCALIZATION_ID_ALLOW_SHRINKING,
+ "&Pertahankan Rasio Aspek", // LOCALIZATION_ID_KEEP_ASPECT_RATIO,
+ "&Isi Jendela", // LOCALIZATION_ID_FILL_WINDOW,
+ "1:1", // LOCALIZATION_ID_ONE_TO_ONE,
+ "Paling &Pas", // LOCALIZATION_ID_BEST_FIT,
+ "Geser && &Pindai", // LOCALIZATION_ID_PAN_SCAN,
+ "&Perbesar Ukuran", // LOCALIZATION_ID_INCREASE_SIZE,
+ "&Perkecil Ukuran", // LOCALIZATION_ID_DECREASE_SIZE,
+ "Pe&rlebar", // LOCALIZATION_ID_INCREASE_WIDTH,
+ "Pe&nyempit", // LOCALIZATION_ID_DECREASE_WIDTH,
+ "Per&tinggi", // LOCALIZATION_ID_INCREASE_HEIGHT,
+ "Per&pendek", // LOCALIZATION_ID_DECREASE_HEIGHT,
+ "&Zoom", // LOCALIZATION_ID_ZOOM,
+ "Zoom &Masuk", // LOCALIZATION_ID_ZOOM_IN,
+ "Zoom &Keluar", // LOCALIZATION_ID_ZOOM_OUT,
+ "&Atur Ulang", // LOCALIZATION_ID_RESET,
+ "Geser Ke &Atas", // LOCALIZATION_ID_MOVE_UP,
+ "Geser Ke &Bawah", // LOCALIZATION_ID_MOVE_DOWN,
+ "Geser Ke &Kiri", // LOCALIZATION_ID_MOVE_LEFT,
+ "Geser Ke &Kanan", // LOCALIZATION_ID_MOVE_RIGHT,
+ "Geser Atas Kiri", // LOCALIZATION_ID_MOVE_UP_LEFT,
+ "Geser Atas Kanan", // LOCALIZATION_ID_MOVE_UP_RIGHT,
+ "Geser Bawah Kiri", // LOCALIZATION_ID_MOVE_DOWN_LEFT,
+ "Geser Bawah Kanan", // LOCALIZATION_ID_MOVE_DOWN_RIGHT,
+ "Geser ke &Tengah", // LOCALIZATION_ID_MOVE_CENTER,
+ "Atur &Ulang", // LOCALIZATION_ID_PANSCAN_RESET,
+ "Selalu di &Atas", // LOCALIZATION_ID_ON_TOP,
+ "&Selalu", // LOCALIZATION_ID_ALWAYS,
+ "S&aat Memutar Slideshow atau Animasi", // LOCALIZATION_ID_WHILE_PLAYING_OR_ANIMATING,
+ "&Jangan Pernah", // LOCALIZATION_ID_NEVER,
+ "&Opsi...", // LOCALIZATION_ID_OPTIONS,
+
+ // Menu Slideshow
+ "&Slideshow", // LOCALIZATION_ID_SLIDESHOW_MENU,
+ "&Putar/Jeda", // LOCALIZATION_ID_PLAY_PAUSE,
+ "&Kecepatan", // LOCALIZATION_ID_RATE,
+ "&Kurangi Kecepatan", // LOCALIZATION_ID_DECREASE_RATE,
+ "&Tambah Kecepatan", // LOCALIZATION_ID_INCREASE_RATE,
+ "250 Milidetik", // LOCALIZATION_ID_RATE_250_MILLISECONDS,
+ "500 Milidetik", // LOCALIZATION_ID_RATE_500_MILLISECONDS,
+ "&1 Detik", // LOCALIZATION_ID_RATE_1_SECOND,
+ "&2 Detik", // LOCALIZATION_ID_RATE_2_SECONDS,
+ "&3 Detik", // LOCALIZATION_ID_RATE_3_SECONDS,
+ "&4 Detik", // LOCALIZATION_ID_RATE_4_SECONDS,
+ "&5 Detik", // LOCALIZATION_ID_RATE_5_SECONDS,
+ "&6 Detik", // LOCALIZATION_ID_RATE_6_SECONDS,
+ "&7 Detik", // LOCALIZATION_ID_RATE_7_SECONDS,
+ "&8 Detik", // LOCALIZATION_ID_RATE_8_SECONDS,
+ "&9 Detik", // LOCALIZATION_ID_RATE_9_SECONDS,
+ "1&0 Detik", // LOCALIZATION_ID_RATE_10_SECONDS,
+ "20 Detik", // LOCALIZATION_ID_RATE_20_SECONDS,
+ "30 Detik", // LOCALIZATION_ID_RATE_30_SECONDS,
+ "40 Detik", // LOCALIZATION_ID_RATE_40_SECONDS,
+ "50 Detik", // LOCALIZATION_ID_RATE_50_SECONDS,
+ "1 Menit", // LOCALIZATION_ID_RATE_1_MINUTE,
+ "Kustom...", // LOCALIZATION_ID_CUSTOM,
+
+ // Menu Animasi
+ "&Animasi", // LOCALIZATION_ID_ANIMATION,
+ "&Putar/Jeda", // LOCALIZATION_ID_ANIMATION_PLAY_PAUSE,
+ "Lompat ke &Depan", // LOCALIZATION_ID_ANIMATION_JUMP_FORWARD,
+ "Lompat ke &Belakang", // LOCALIZATION_ID_ANIMATION_JUMP_BACKWARD,
+ "Lompat Pendek ke &Depan", // LOCALIZATION_ID_ANIMATION_SHORT_JUMP_FORWARD,
+ "Lompat Pendek ke &Belakang", // LOCALIZATION_ID_ANIMATION_SHORT_JUMP_BACKWARD,
+ "Lompat Jauh ke &Depan", // LOCALIZATION_ID_ANIMATION_LONG_JUMP_FORWARD,
+ "Lompat Jauh ke &Belakang", // LOCALIZATION_ID_ANIMATION_LONG_JUMP_BACKWARD,
+ "Langkah &Frame", // LOCALIZATION_ID_ANIMATION_FRAME_STEP,
+ "Frame &Sebelumnya", // LOCALIZATION_ID_ANIMATION_PREVIOUS_FRAME,
+ "Frame Per&tama", // LOCALIZATION_ID_ANIMATION_FIRST_FRAME,
+ "Frame Ter&akhir", // LOCALIZATION_ID_ANIMATION_LAST_FRAME,
+ "&Kurangi Kecepatan", // LOCALIZATION_ID_ANIMATION_DECREASE_RATE,
+ "&Tambah Kecepatan", // LOCALIZATION_ID_ANIMATION_INCREASE_RATE,
+ "Atur &Ulang Kecepatan", // LOCALIZATION_ID_ANIMATION_RESET_RATE,
+
+ // Menu Navigasi
+ "&Navigasi", // LOCALIZATION_ID_NAVIGATE,
+ "&Selanjutnya", // LOCALIZATION_ID_NEXT,
+ "&Sebelumnya", // LOCALIZATION_ID_PREVIOUS,
+ "&Home", // LOCALIZATION_ID_HOME,
+ "&End", // LOCALIZATION_ID_END,
+ "&Urutkan", // LOCALIZATION_ID_SORT,
+ "&Nama", // LOCALIZATION_ID_SORT_NAME,
+ "&Path Lengkap", // LOCALIZATION_ID_SORT_FULL_PATH,
+ "&Ukuran", // LOCALIZATION_ID_SORT_SIZE,
+ "Tanggal &Modifikasi", // LOCALIZATION_ID_SORT_DATE_MODIFIED,
+ "Tanggal &Dibuat", // LOCALIZATION_ID_SORT_DATE_CREATED,
+ "&Menaik (Ascending)", // LOCALIZATION_ID_SORT_ASCENDING,
+ "&Menurun (Descending)", // LOCALIZATION_ID_SORT_DESCENDING,
+ "Acak", // LOCALIZATION_ID_SHUFFLE,
+ "&Lompat Ke...", // LOCALIZATION_ID_JUMP_TO,
+
+ // Menu Bantuan
+ "&Bantuan", // LOCALIZATION_ID_HELP,
+ "&Bantuan", // LOCALIZATION_ID_HELP_MENU,
+ "&Opsi Command Line", // LOCALIZATION_ID_COMMAND_LINE_OPTIONS,
+ "&Halaman Utama", // LOCALIZATION_ID_HOME_PAGE,
+ "&Donasi", // LOCALIZATION_ID_DONATE,
+ "&Tentang", // LOCALIZATION_ID_ABOUT,
+ "https://www.voidtools.com/support/voidimageviewer/", // LOCALIZATION_ID_HELP_SUPPORT_URL,
+ "https://www.voidtools.com/donate/", // LOCALIZATION_ID_HELP_DONATE_URL,
+ "https://www.voidtools.com/", // LOCALIZATION_ID_HELP_WEBSITE_URL,
+
+ // Bilah Alat (Toolbar)
+ "Gambar Sebelumnya", // LOCALIZATION_ID_TOOLBAR_PREVIOUS_IMAGE_BUTTON,
+ "Gambar Selanjutnya", // LOCALIZATION_ID_TOOLBAR_NEXT_IMAGE_BUTTON,
+ "Putar Slideshow", // LOCALIZATION_ID_TOOLBAR_PLAY_SLIDESHOW_BUTTON,
+ "Jeda Slideshow", // LOCALIZATION_ID_TOOLBAR_PAUSE_SLIDESHOW_BUTTON,
+ "Paling Pas", // LOCALIZATION_ID_TOOLBAR_BEST_FIT_BUTTON,
+ "Ukuran Asli", // LOCALIZATION_ID_TOOLBAR_ACTUAL_SIZE_BUTTON,
+
+ // Bilah Status
+ "PRAPEMUATAN", // LOCALIZATION_ID_STATUS_BAR_PRELOAD,
+ "Memuat...", // LOCALIZATION_ID_STATUS_BAR_LOADING,
+ "Berkas tidak ditemukan.", // LOCALIZATION_ID_STATUS_BAR_FILE_NOT_FOUND,
+ "Gagal memuat gambar.", // LOCALIZATION_ID_STATUS_BAR_FAILED_TO_LOAD_IMAGE,
+ "Slideshow diputar", // LOCALIZATION_ID_STATUS_BAR_SLIDESHOW_PLAYING,
+ "Pos %0.3f %0.3f, Zoom %0.3f %0.3f, Rasio Aspek %0.3f", // LOCALIZATION_ID_STATUS_BAR_POS_ZOOM_FORMAT,
+ "Kecepatan animasi %0.3f", // LOCALIZATION_ID_STATUS_BAR_ANIMATION_RATE_FORMAT,
+ "Kecepatan slideshow %d %s", // LOCALIZATION_ID_STATUS_BAR_SLIDESHOW_RATE_FORMAT,
+ "menit", // LOCALIZATION_ID_STATUS_BAR_MINUTES,
+ "detik", // LOCALIZATION_ID_STATUS_BAR_SECONDS,
+ "milidetik", // LOCALIZATION_ID_STATUS_BAR_MILLISECONDS,
+
+ // Dialog Opsi
+ "Opsi - void Image Viewer", // LOCALIZATION_ID_OPTIONS_CAPTION,
+ "Umum", // LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,
+ "Tampilan", // LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,
+ "Kontrol", // LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG,
+ "Opsi zoom", // LOCALIZATION_ID_OPTIONS_ZOOM_DIALOG,
+ "OK", // LOCALIZATION_ID_OK_BUTTON,
+ "Batal", // LOCALIZATION_ID_CANCEL_BUTTON,
+ "&Simpan pengaturan di %APPDATA%\\voidImageViewer", // LOCALIZATION_ID_STORE_SETTINGS_APPDATA,
+ "Izinkan beberapa &instansi", // LOCALIZATION_ID_ALLOW_MULTIPLE_INSTANCES,
+ "Pintasan &menu Start", // LOCALIZATION_ID_STARTMENU_SHORTCUTS,
+ "Asosiasi", // LOCALIZATION_ID_ASSOCIATIONS,
+ "Pilih &Semua", // LOCALIZATION_ID_CHECK_ALL,
+ "Pilih &Tidak Ada", // LOCALIZATION_ID_CHECK_NONE,
+ "Mode blit &pengecilan:", // LOCALIZATION_ID_SHRINK_BLIT_MODE_STATIC,
+ "Mode blit &pembesaran:", // LOCALIZATION_ID_MAGNIFY_BLIT_MODE,
+ "Nearest", // LOCALIZATION_ID_BLIT_MODE_NEAREST_COMBOBOXITEM,
+ "Linear", // LOCALIZATION_ID_BLIT_MODE_LINEAR_COMBOBOXITEM,
+ "Ukuran jendela &otomatis:", // LOCALIZATION_ID_OPTIONS_VIEW_AUTO_SIZE_WINDOW_STATIC,
+ "50%", // LOCALIZATION_ID_OPTIONS_VIEW_AUTO_SIZE_WINDOW_50_PERCENT_COMBOBOXITEM,
+ "100%", // LOCALIZATION_ID_OPTIONS_VIEW_AUTO_SIZE_WINDOW_100_PERCENT_COMBOBOXITEM,
+ "200%", // LOCALIZATION_ID_OPTIONS_VIEW_AUTO_SIZE_WINDOW_200_PERCENT_COMBOBOXITEM,
+ "Pas Otomatis", // LOCALIZATION_ID_OPTIONS_VIEW_AUTO_SIZE_WINDOW_AUTO_FIT_COMBOBOXITEM,
+ "&Putar animasi setidaknya sekali dalam slideshow", // LOCALIZATION_ID_PLAY_ANIMATIONS_ONCE_STATIC,
+ "Prapemuatan gambar &selanjutnya", // LOCALIZATION_ID_PRELOAD_NEXT_IMAGE_STATIC,
+ "Simpan gambar &terakhir di cache", // LOCALIZATION_ID_CACHE_LAST_IMAGE_STATIC,
+ "Warna latar &jendela:", // LOCALIZATION_ID_WINDOWED_BACKGROUND_COLOR_STATIC,
+ "Warna latar &layar penuh:", // LOCALIZATION_ID_FULLSCREEN_BACKGROUND_COLOR_STATIC,
+ "Atur Ulang Ukuran && Posisi Jendela", // LOCALIZATION_ID_OPTIONS_VIEW_RESET_WINDOW_BUTTON,
+ "Aksi klik &kiri:", // LOCALIZATION_ID_LEFT_CLICK_ACTION_STATIC,
+ "Aksi klik &kanan:", // LOCALIZATION_ID_RIGHT_CLICK_ACTION_STATIC,
+ "Aksi roda &mouse:", // LOCALIZATION_ID_MOUSE_WHEEL_ACTION_STATIC,
+ "&Perintah:", // LOCALIZATION_ID_COMMANDS_STATIC,
+ "Pengaturan untuk perintah terpilih", // LOCALIZATION_ID_SETTINGS_FOR_SELECTED_COMMAND,
+ "&Tambah...", // LOCALIZATION_ID_ADD_KEY_BUTTON,
+ "&Ubah...", // LOCALIZATION_ID_EDIT_KEY_BUTTON,
+ "&Hapus", // LOCALIZATION_ID_REMOVE_KEY_BUTTON,
+ "Tambah Pintasan Keyboard", // LOCALIZATION_ID_ADD_KEYBOARD_SHORTCUT_CAPTION,
+ "Ubah Pintasan Keyboard", // LOCALIZATION_ID_EDIT_KEYBOARD_SHORTCUT_CAPTION,
+ "&Tombol pintasan:", // LOCALIZATION_ID_SHORTCUT_KEY,
+ "Tombol pintasan sedang digunakan oleh:", // LOCALIZATION_ID_SHORTCUT_KEY_CURRENTLY_USED_BY,
+ "&Hapus", // LOCALIZATION_ID_REMOVE,
+ "Atur Kecepatan Kustom", // LOCALIZATION_ID_SET_CUSTOM_RATE_CAPTION,
+ "Kecepatan &kustom:", // LOCALIZATION_ID_CUSTOM_RATE_STATIC,
+ "menit", // LOCALIZATION_ID_CUSTOM_RATE_MINUTES,
+ "detik", // LOCALIZATION_ID_CUSTOM_RATE_SECONDS,
+ "milidetik", // LOCALIZATION_ID_CUSTOM_RATE_MILLISECONDS,
+ "Tentang void Image Viewer", // LOCALIZATION_ID_ABOUT_CAPTION,
+ "Ubah Nama", // LOCALIZATION_ID_RENAME_CAPTION,
+ "Lompat Ke", // LOCALIZATION_ID_JUMP_TO_TITLE,
+ "Pencarian Everything", // LOCALIZATION_ID_SEARCH_EVERYTHING,
+ "Hak Cipta \xC2\xA9 %d voidtools", // LOCALIZATION_ID_ABOUT_COPYRIGHT_FORMAT,
+ "support@voidtools.com", // LOCALIZATION_ID_ABOUT_EMAIL,
+ "www.voidtools.com", // LOCALIZATION_ID_ABOUT_WEBSITE,
+ "Buka Gambar", // LOCALIZATION_ID_OPEN_IMAGE_CAPTION,
+ "Semua Berkas Gambar", // LOCALIZATION_ID_OPEN_ALL_IMAGE_FILES,
+ "Semua Berkas", // LOCALIZATION_ID_OPEN_ALL_FILES,
+ "Gulir", // LOCALIZATION_ID_OPTIONS_ACTION_SCROLL_COMBOBOXITEM,
+ "Putar/Jeda Slideshow", // LOCALIZATION_ID_OPTIONS_ACTION_PLAY_PAUSE_SLIDESHOW_COMBOBOXITEM,
+ "Putar/Jeda Animasi", // LOCALIZATION_ID_OPTIONS_ACTION_PLAY_PAUSE_ANIMATION_COMBOBOXITEM,
+ "Zoom Masuk", // LOCALIZATION_ID_OPTIONS_ACTION_ZOOM_IN_COMBOBOXITEM,
+ "Gambar Selanjutnya", // LOCALIZATION_ID_OPTIONS_ACTION_NEXT_IMAGE_COMBOBOXITEM,
+ "Gulir 1:1", // LOCALIZATION_ID_OPTIONS_ACTION_ONE_TO_ONE_SCROLL_COMBOBOXITEM,
+ "Gulir/Pindah Jendela", // LOCALIZATION_ID_OPTIONS_ACTION_SCROLL_MOVE_WINDOW_COMBOBOXITEM,
+ "Menu Konteks", // LOCALIZATION_ID_OPTIONS_ACTION_CONTEXT_MENU_COMBOBOXITEM,
+ "Zoom Keluar", // LOCALIZATION_ID_OPTIONS_ACTION_ZOOM_OUT_COMBOBOXITEM,
+ "Gambar Sebelumnya", // LOCALIZATION_ID_OPTIONS_ACTION_PREVIOUS_IMAGE_COMBOBOXITEM,
+ "Zoom", // LOCALIZATION_ID_OPTIONS_ACTION_ZOOM_COMBOBOXITEM,
+ "Selanjutnya/Sebelumnya", // LOCALIZATION_ID_OPTIONS_ACTION_NEXT_PREV_COMBOBOXITEM,
+ "Sebelumnya/Selanjutnya", // LOCALIZATION_ID_OPTIONS_ACTION_PREV_NEXT_COMBOBOXITEM,
+ "Format bilah &judul:", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_STATIC,
+ "Path Lengkap", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FULL_PATH_COMBOBOXITEM,
+ "Hanya Nama Berkas", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FILENAME_ONLY_COMBOBOXITEM,
+ "Tidak Ada", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_NONE_COMBOBOXITEM,
+ "Langkah &zoom:", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_STATIC,
+ "(standar: 10 %)", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_DEFAULT_STATIC,
+ "Metode kalkulasi zoom:", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_METHOD_STATIC,
+ "Langkah &relatif", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_RELATIVE_RADIO,
+ "Langkah &absolut", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_ABSOLUTE_RADIO,
+ "Gunakan tabel &tetap: (khusus ahli: maks. 50 nilai terurut dari 3 ke 4000)", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_FIXED_TABLE_RADIO,
+ "Muat tabel &standar", // LOCALIZATION_ID_OPTIONS_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,
+ "Everything tidak tersedia", // LOCALIZATION_ID_EVERYTHING_NOT_AVAILABLE_MESSAGE,
+ "Tambah Pencarian Everything", // LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION,
+ "Muat Pencarian Everything", // LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION,
+ "Acak", // LOCALIZATION_ID_RANDOMIZE,
+ "Salin Ke", // LOCALIZATION_ID_COPY_TO_CAPTION,
+ "Pindah Ke", // LOCALIZATION_ID_MOVE_TO_CAPTION,
+
+ // Deskripsi Asosiasi
+ "Gambar Bitmap",
+ "Gambar GIF Animasi",
+ "Berkas Ikon",
+ "Gambar JPEG",
+ "Gambar JPEG",
+ "Gambar PNG",
+ "Gambar TIFF",
+ "Gambar TIFF",
+ "Gambar WebP",
+
+ // Compare Images
+ "Bandingkan Gambar...", // LOCALIZATION_ID_COMPARE_IMAGES,
+ "Tips: Seret gambar dengan tombol mouse KIRI; Gulir RODA mouse untuk zoom; Tekan tombol Ctrl atau tombol mouse Kanan untuk memproses gambar individual.", // LOCALIZATION_ID_COMPARE_TIPS,
+ "Pas Otomatis", // LOCALIZATION_ID_COMPARE_AUTO_FIT,
+ "Halus", // LOCALIZATION_ID_COMPARE_SMOOTH,
+ "Nama Berkas", // LOCALIZATION_ID_COMPARE_FILENAME,
+ "Tutup", // LOCALIZATION_ID_COMPARE_CLOSE,
+ "Sebelumnya", // LOCALIZATION_ID_COMPARE_PREV,
+ "Berikutnya", // LOCALIZATION_ID_COMPARE_NEXT,
+ "Perbesar", // LOCALIZATION_ID_COMPARE_ZOOM_IN,
+ "Perkecil", // LOCALIZATION_ID_COMPARE_ZOOM_OUT,
+ "1:1", // LOCALIZATION_ID_COMPARE_ACTUAL_SIZE,
+ "Pas", // LOCALIZATION_ID_COMPARE_BEST_FIT,
+ "Putar", // LOCALIZATION_ID_COMPARE_ROTATE,
+ "Pilih Gambar", // LOCALIZATION_ID_COMPARE_SELECT_IMAGE,
+ "Gunakan Gambar Saat Ini", // LOCALIZATION_ID_COMPARE_USE_CURRENT,
+ "[Tidak Ada Gambar - Tarik & lepas gambar ke sini atau klik ... / Gunakan Saat Ini]", // LOCALIZATION_ID_COMPARE_NO_IMAGE_DROP,
+ "&Halus", // LOCALIZATION_ID_SMOOTH,
+};
diff --git a/src/localization_zh_cn.h b/src/localization_zh_cn.h
index 3d135697..7f1e5531 100644
--- a/src/localization_zh_cn.h
+++ b/src/localization_zh_cn.h
@@ -26,6 +26,8 @@ static const utf8_t *_localization_string_array_zh_cn[LOCALIZATION_ID_COUNT] =
{
// Language
"简体中文", // LOCALIZATION_ID_LANGUAGE
+ "语言(&L):", // LOCALIZATION_ID_LANGUAGE_STATIC
+ "系统默认", // LOCALIZATION_ID_LANGUAGE_AUTO
// Application name
"void Image Viewer", // LOCALIZATION_ID_APP_NAME
@@ -211,6 +213,7 @@ static const utf8_t *_localization_string_array_zh_cn[LOCALIZATION_ID_COUNT] =
"常规", // LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,
"视图", // LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,
"控件", // LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG,
+ "缩放选项", // LOCALIZATION_ID_OPTIONS_ZOOM_DIALOG,
"确定", // LOCALIZATION_ID_OK_BUTTON,
"取消", // LOCALIZATION_ID_CANCEL_BUTTON,
"在 %APPDATA%\\voidImageViewer 中存储设置(&S)", // LOCALIZATION_ID_STORE_SETTINGS_APPDATA,
@@ -233,6 +236,7 @@ static const utf8_t *_localization_string_array_zh_cn[LOCALIZATION_ID_COUNT] =
"缓存最后一张图像(&L)", // LOCALIZATION_ID_CACHE_LAST_IMAGE_STATIC,
"窗口背景颜色(&W):", // LOCALIZATION_ID_WINDOWED_BACKGROUND_COLOR_STATIC,
"全屏背景颜色(&F):", // LOCALIZATION_ID_FULLSCREEN_BACKGROUND_COLOR_STATIC,
+ "重置窗口大小和位置", // LOCALIZATION_ID_OPTIONS_VIEW_RESET_WINDOW_BUTTON,
"左键操作(&L):", // LOCALIZATION_ID_LEFT_CLICK_ACTION_STATIC,
"右键操作(&R):", // LOCALIZATION_ID_RIGHT_CLICK_ACTION_STATIC,
"鼠标滚轮操作(&M):", // LOCALIZATION_ID_MOUSE_WHEEL_ACTION_STATIC,
@@ -278,6 +282,13 @@ static const utf8_t *_localization_string_array_zh_cn[LOCALIZATION_ID_COUNT] =
"完整路径", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FULL_PATH_COMBOBOXITEM,
"仅文件名", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_FILENAME_ONLY_COMBOBOXITEM,
"无", // LOCALIZATION_ID_OPTIONS_TITLE_BAR_FORMAT_NONE_COMBOBOXITEM,
+ "缩放步长(&S):", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_STATIC,
+ "(默认: 10 %)", // LOCALIZATION_ID_OPTIONS_ZOOM_STEP_DEFAULT_STATIC,
+ "缩放计算方式:", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_METHOD_STATIC,
+ "相对步长(&R)", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_RELATIVE_RADIO,
+ "绝对步长(&A)", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_ABSOLUTE_RADIO,
+ "使用固定表(&F): (专家级: 3 到 4000 之间的最多 50 个有序值)", // LOCALIZATION_ID_OPTIONS_ZOOM_CALC_FIXED_TABLE_RADIO,
+ "加载默认表(&L)", // LOCALIZATION_ID_OPTIONS_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,
"Everything 不可用", // LOCALIZATION_ID_EVERYTHING_NOT_AVAILABLE_MESSAGE,
"添加 Everything 搜索", // LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION,
"加载 Everything 搜索", // LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION,
@@ -295,4 +306,23 @@ static const utf8_t *_localization_string_array_zh_cn[LOCALIZATION_ID_COUNT] =
"TIFF Image",
"TIFF Image",
"WebP Image",
+
+ // Compare Images
+ "比较图像...", // LOCALIZATION_ID_COMPARE_IMAGES,
+ "提示: 按住鼠标左键拖动图像; 滚动鼠标滚轮缩放; 按住 Ctrl 键或鼠标右键可单独操作当前图像。", // LOCALIZATION_ID_COMPARE_TIPS,
+ "自动适合", // LOCALIZATION_ID_COMPARE_AUTO_FIT,
+ "平滑", // LOCALIZATION_ID_COMPARE_SMOOTH,
+ "文件名", // LOCALIZATION_ID_COMPARE_FILENAME,
+ "关闭", // LOCALIZATION_ID_COMPARE_CLOSE,
+ "上一个", // LOCALIZATION_ID_COMPARE_PREV,
+ "下一个", // LOCALIZATION_ID_COMPARE_NEXT,
+ "放大", // LOCALIZATION_ID_COMPARE_ZOOM_IN,
+ "缩小", // LOCALIZATION_ID_COMPARE_ZOOM_OUT,
+ "1:1", // LOCALIZATION_ID_COMPARE_ACTUAL_SIZE,
+ "适合", // LOCALIZATION_ID_COMPARE_BEST_FIT,
+ "旋转", // LOCALIZATION_ID_COMPARE_ROTATE,
+ "选择图像", // LOCALIZATION_ID_COMPARE_SELECT_IMAGE,
+ "使用当前图像", // LOCALIZATION_ID_COMPARE_USE_CURRENT,
+ "[无图像 - 拖放图像至此处或点击 ... / 使用当前图像]", // LOCALIZATION_ID_COMPARE_NO_IMAGE_DROP,
+ "平滑(&S)", // LOCALIZATION_ID_SMOOTH,
};
diff --git a/src/version.h b/src/version.h
index ea4550ee..c96d09cf 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,6 +1,7 @@
#define VERSION_YEAR 2026
#define VERSION_MAJOR 1
-#define VERSION_MINOR 0
-#define VERSION_REVISION 0
-#define VERSION_BUILD 15
+#define VERSION_MINOR 2
+#define VERSION_REVISION 1
+#define VERSION_BUILD 0
#define VERSION_TYPE ""
+
diff --git a/src/viv.c b/src/viv.c
index deff4526..e70970ad 100644
--- a/src/viv.c
+++ b/src/viv.c
@@ -257,7 +257,7 @@
#define _VIV_ASSOCIATION_TIFF 0x00000080
#define _VIV_ASSOCIATION_WEBP 0x00000100
-#define _VIV_ZOOM_MAX 16
+#define _VIV_ZOOM_MAX 64
#define BCM_SETSHIELD 0x0000160C
@@ -444,7 +444,7 @@ typedef struct _viv_webp_s
typedef struct _viv_name_mapping_s
{
UINT count;
- SHNAMEMAPPING *mappings;
+ SHNAMEMAPPING mappings[1];
}_viv_name_mapping_t;
static void _viv_update_title(void);
@@ -496,6 +496,9 @@ static INT_PTR CALLBACK _viv_options_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,L
static INT_PTR CALLBACK _viv_options_general_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
static INT_PTR CALLBACK _viv_options_view_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
static INT_PTR CALLBACK _viv_options_controls_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
+static INT_PTR CALLBACK _viv_options_zoom_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
+static double _viv_calculate_next_zoom(double current_percent,int zoom_in);
+static int _viv_parse_fixed_table(const wchar_t *table_str,double *values,int max_values);
static void _viv_install_association_by_extension(const char *association,const char *description,const char *icon_location);
static void _viv_uninstall_association_by_extension(const char *association);
static int _viv_is_association(const char *association);
@@ -591,6 +594,8 @@ static INT_PTR CALLBACK _viv_rename_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM
static void _viv_shuffle_playlist(void);
static void _viv_show_jumpto(void);
static LRESULT CALLBACK _viv_jumpto_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
+static void _viv_compare_show(void);
+static LRESULT CALLBACK _viv_compare_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
static void _viv_jumpto_on_size(HWND hwnd);
static void _viv_jumpto_on_search(HWND hwnd);
static void _viv_jumpto_open_sel(HWND hwnd);
@@ -608,6 +613,7 @@ static int _viv_compare_id(const WIN32_FIND_DATA *a,const WIN32_FIND_DATA *b);
static int _viv_fd_compare_name(const WIN32_FIND_DATA *a,const WIN32_FIND_DATA *b);
static int _viv_fd_compare_path_and_name(const WIN32_FIND_DATA *a,const WIN32_FIND_DATA *b);
static void _viv_update_1to1_scroll(int x,int y);
+static HBITMAP _viv_create_bitmap(HDC hdc, int width, int height);
static HBITMAP _viv_orientate_hbitmap(HBITMAP hbitmap,int counterclockwise);
static void _viv_send_random_everything_search(void);
static void _viv_do_mousewheel_action(int action,int delta,int x,int y);
@@ -674,6 +680,10 @@ static BYTE _viv_animation_play = 1; // play or pause animations
static BYTE _viv_1to1 = 0; // temporarily show the image with 100% scaling
static BYTE _viv_have_old_zoom = 0; // restore this zoom level after leaving 1:1 mode.
static int _viv_old_zoom_pos = 0; // restore this zoom level after leaving 1:1 mode.
+static BYTE _viv_is_custom_zoom = 0; // custom zoom level active
+static double _viv_custom_zoom_percent = 100.0; // custom zoom in percent
+static BYTE _viv_old_is_custom_zoom = 0;
+static double _viv_old_custom_zoom_percent = 100.0;
WIN32_FIND_DATA *_viv_current_fd = 0; // the current image find data including the full path and filename.
WIN32_FIND_DATA *_viv_preload_fd = 0;
static int _viv_view_x = 0; // the current image offset in pixels
@@ -681,8 +691,6 @@ static int _viv_view_y = 0; // the current image offset in pixels
static double _viv_view_ix = 0.0; // the current image offset in percent, used when resizing the window
static double _viv_view_iy = 0.0; // the current image offset in percent, used when resizing the window
static int _viv_zoom_pos = 0; // the current zoom level
-//static float _viv_zoom_presets[_VIV_ZOOM_MAX] = {0.004815f,0.019215f,0.043060f,0.076120f,0.118079f,0.168530f,0.226989f,0.292893f,0.365607f,0.444430f,0.528603f,0.617316f,0.709715f,0.804909f,0.901983f,1.000000f}; // (1 - cos(((float)(x+1) * 1.570796f) / _VIV_ZOOM_MAX)) // this is missing cos((1 * 1.570796f) / _VIV_ZOOM_MAX), which is too small
-static float _viv_zoom_presets[_VIV_ZOOM_MAX] = {0.0000,0.0100,0.0225,0.0379,0.0569,0.0806,0.1098,0.1461,0.1909,0.2465,0.3154,0.4007,0.5063,0.6372,0.7993,1.0000}; // 0.01 - 0.2 curve
static ULONG_PTR os_GdiplusToken; // gdiplus handle
static int _viv_image_wide = 0; // current image width
@@ -733,12 +741,12 @@ static volatile int _viv_load_image_terminate = 0;
static _viv_reply_t *_viv_reply_start = 0;
static _viv_reply_t *_viv_reply_last = 0;
static wchar_t *_viv_status_temp_text = 0;
-static localization_id_t _viv_options_page_localization_id_array[] = {LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG};
-static int _viv_options_tab_ids[] = {IDC_TAB1,IDC_TAB2,IDC_TAB3};
-static int _viv_options_dialog_ids[] = {IDD_GENERAL,IDD_VIEW,IDD_CONTROLS};
+static localization_id_t _viv_options_page_localization_id_array[] = {LOCALIZATION_ID_OPTIONS_GENERAL_DIALOG,LOCALIZATION_ID_OPTIONS_VIEW_DIALOG,LOCALIZATION_ID_OPTIONS_CONTROLS_DIALOG,LOCALIZATION_ID_OPTIONS_ZOOM_DIALOG};
+static int _viv_options_tab_ids[] = {IDC_TAB1,IDC_TAB2,IDC_TAB3,IDC_TAB4};
+static int _viv_options_dialog_ids[] = {IDD_GENERAL,IDD_VIEW,IDD_CONTROLS,IDD_ZOOM};
#define _VIV_OPTIONS_PAGE_COUNT (sizeof(_viv_options_dialog_ids) / sizeof(int))
-static int _viv_options_page_ids[] = {VIV_ID_OPTIONS_GENERAL,VIV_ID_OPTIONS_VIEW,VIV_ID_OPTIONS_CONTROLS};
-static DLGPROC _viv_options_page_procs[] = {_viv_options_general_proc,_viv_options_view_proc,_viv_options_controls_proc};
+static int _viv_options_page_ids[] = {VIV_ID_OPTIONS_GENERAL,VIV_ID_OPTIONS_VIEW,VIV_ID_OPTIONS_CONTROLS,VIV_ID_OPTIONS_ZOOM};
+static DLGPROC _viv_options_page_procs[] = {_viv_options_general_proc,_viv_options_view_proc,_viv_options_controls_proc,_viv_options_zoom_proc};
static HFONT _viv_about_hfont = 0;
static wchar_t *_viv_last_open_file = 0;
static wchar_t *_viv_last_open_folder = 0;
@@ -849,6 +857,7 @@ static _viv_command_t _viv_commands[] =
{LOCALIZATION_ID_INVALID,MF_SEPARATOR,_VIV_MENU_VIEW,0},
{LOCALIZATION_ID_FULLSCREEN,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_FULLSCREEN},
{LOCALIZATION_ID_SLIDESHOW,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_SLIDESHOW},
+ {LOCALIZATION_ID_COMPARE_IMAGES,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_COMPARE_IMAGES},
{LOCALIZATION_ID_VIEW_WINDOW_SIZE,MF_POPUP,_VIV_MENU_VIEW,_VIV_MENU_VIEW_WINDOW_SIZE},
{LOCALIZATION_ID_VIEW_WINDOW_SIZE_50_PERCENT,MF_STRING,_VIV_MENU_VIEW_WINDOW_SIZE,VIV_ID_VIEW_WINDOW_SIZE_50},
{LOCALIZATION_ID_VIEW_WINDOW_SIZE_100_PERCENT,MF_STRING,_VIV_MENU_VIEW_WINDOW_SIZE,VIV_ID_VIEW_WINDOW_SIZE_100},
@@ -859,6 +868,7 @@ static _viv_command_t _viv_commands[] =
{LOCALIZATION_ID_ALLOW_SHRINKING,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_ALLOW_SHRINKING},
{LOCALIZATION_ID_KEEP_ASPECT_RATIO,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_KEEP_ASPECT_RATIO},
{LOCALIZATION_ID_FILL_WINDOW,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_FILL_WINDOW},
+ {LOCALIZATION_ID_SMOOTH,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_SMOOTH},
{LOCALIZATION_ID_ONE_TO_ONE,MF_STRING,_VIV_MENU_VIEW,VIV_ID_VIEW_1TO1},
{LOCALIZATION_ID_BEST_FIT,MF_STRING|MF_OWNERDRAW,_VIV_MENU_VIEW,VIV_ID_VIEW_BESTFIT},
{LOCALIZATION_ID_INVALID,MF_SEPARATOR,_VIV_MENU_VIEW,0},
@@ -988,12 +998,11 @@ _viv_default_key_t _viv_default_keys[] =
{VIV_ID_EDIT_COPY,CONFIG_KEYFLAG_CTRL | 'C'},
{VIV_ID_EDIT_COPY_FILENAME,CONFIG_KEYFLAG_CTRL | CONFIG_KEYFLAG_SHIFT | 'C'},
{VIV_ID_EDIT_PASTE,CONFIG_KEYFLAG_CTRL | 'V'},
- {VIV_ID_VIEW_PRESET_1,'1'},
- {VIV_ID_VIEW_PRESET_2,'2'},
- {VIV_ID_VIEW_PRESET_3,'3'},
+
{VIV_ID_VIEW_1TO1,CONFIG_KEYFLAG_CTRL | CONFIG_KEYFLAG_ALT | '0'},
{VIV_ID_VIEW_FULLSCREEN,CONFIG_KEYFLAG_ALT | VK_RETURN},
{VIV_ID_VIEW_SLIDESHOW,VK_F11},
+ {VIV_ID_VIEW_COMPARE_IMAGES,CONFIG_KEYFLAG_CTRL | CONFIG_KEYFLAG_ALT | 'C'},
{VIV_ID_VIEW_WINDOW_SIZE_50,CONFIG_KEYFLAG_ALT | '1'},
{VIV_ID_VIEW_WINDOW_SIZE_100,CONFIG_KEYFLAG_ALT | '2'},
{VIV_ID_VIEW_WINDOW_SIZE_200,CONFIG_KEYFLAG_ALT | '3'},
@@ -1086,6 +1095,7 @@ WORD _viv_context_menu_items[] =
VIV_ID_VIEW_ALLOW_SHRINKING,
VIV_ID_VIEW_KEEP_ASPECT_RATIO,
VIV_ID_VIEW_FILL_WINDOW,
+ VIV_ID_VIEW_SMOOTH,
VIV_ID_VIEW_1TO1,
// VIV_ID_VIEW_SLIDESHOW,
0,
@@ -1280,12 +1290,16 @@ static void _viv_clear(void)
_viv_frame_count = 0;
_viv_frame_loaded_count = 0;
_viv_zoom_pos = 0;
+ _viv_is_custom_zoom = 0;
+ _viv_custom_zoom_percent = 100.0;
_viv_view_x = 0;
_viv_view_y = 0;
_viv_view_ix = 0.0;
_viv_view_iy = 0.0;
_viv_1to1 = 0;
_viv_have_old_zoom = 0;
+ _viv_old_is_custom_zoom = 0;
+ _viv_old_custom_zoom_percent = 100.0;
_viv_image_wide = 0;
_viv_image_high = 0;
_viv_animation_play = 1;
@@ -1675,9 +1689,12 @@ static void _viv_command_with_is_key_repeat(int command_id,int is_key_repeat)
case VIV_ID_VIEW_ZOOM_RESET:
_viv_1to1 = 0;
+ _viv_is_custom_zoom = 0;
+ _viv_custom_zoom_percent = 100.0;
_viv_zoom_pos = 0;
_viv_view_set(_viv_view_x,_viv_view_y,1);
InvalidateRect(_viv_hwnd,0,FALSE);
+ _viv_toolbar_update_buttons();
break;
case VIV_ID_HELP_HELP:
@@ -2051,6 +2068,12 @@ static void _viv_command_with_is_key_repeat(int command_id,int is_key_repeat)
InvalidateRect(_viv_hwnd,0,FALSE);
break;
+ case VIV_ID_VIEW_SMOOTH:
+ config_shrink_blit_mode = (config_shrink_blit_mode == CONFIG_SHRINK_BLIT_MODE_HALFTONE) ? CONFIG_SHRINK_BLIT_MODE_COLORONCOLOR : CONFIG_SHRINK_BLIT_MODE_HALFTONE;
+ config_mag_filter = (config_shrink_blit_mode == CONFIG_SHRINK_BLIT_MODE_HALFTONE) ? CONFIG_MAG_FILTER_HALFTONE : CONFIG_MAG_FILTER_COLORONCOLOR;
+ InvalidateRect(_viv_hwnd,0,FALSE);
+ break;
+
case VIV_ID_VIEW_FULLSCREEN:
_viv_toggle_fullscreen();
@@ -2073,6 +2096,12 @@ static void _viv_command_with_is_key_repeat(int command_id,int is_key_repeat)
break;
+ case VIV_ID_VIEW_COMPARE_IMAGES:
+
+ _viv_compare_show();
+
+ break;
+
case VIV_ID_VIEW_WINDOW_SIZE_50:
case VIV_ID_VIEW_WINDOW_SIZE_100:
case VIV_ID_VIEW_WINDOW_SIZE_200:
@@ -5260,6 +5289,7 @@ static int _viv_init(int nCmdShow)
// load settings
config_load_settings();
+ localization_init();
// config_maximized will be overwritten when we show are normal window
// so save it now and apply it later.
@@ -6881,6 +6911,19 @@ static void _viv_get_render_size(int *prw,int *prh)
return;
}
+ if (_viv_is_custom_zoom)
+ {
+ rw = (int)((double)_viv_image_wide * _viv_custom_zoom_percent / 100.0 + 0.5);
+ rh = (int)((double)_viv_image_high * _viv_custom_zoom_percent / 100.0 + 0.5);
+ if (rw <= 0) rw = 1;
+ if (rh <= 0) rh = 1;
+
+ *prw = rw;
+ *prh = rh;
+
+ return;
+ }
+
if (_viv_is_fullscreen)
{
fill_window = config_fullscreen_fill_window;
@@ -6975,23 +7018,11 @@ static void _viv_get_render_size(int *prw,int *prh)
if (config_keep_aspect_ratio)
{
- if (rw > _viv_image_wide)
- {
- max_zoom_wide = rw * 16;
- }
- else
- {
- max_zoom_wide = _viv_image_wide * 16;
- }
+ max_zoom_wide = rw * 16;
+ if (max_zoom_wide < _viv_image_wide * 16) max_zoom_wide = _viv_image_wide * 16;
- if (rh > _viv_image_high)
- {
- max_zoom_high = rh * 16;
- }
- else
- {
- max_zoom_high = _viv_image_high * 16;
- }
+ max_zoom_high = rh * 16;
+ if (max_zoom_high < _viv_image_high * 16) max_zoom_high = _viv_image_high * 16;
}
else
{
@@ -7011,8 +7042,9 @@ static void _viv_get_render_size(int *prw,int *prh)
*/
if (_viv_zoom_pos)
{
- rw = rw + (int)((max_zoom_wide - rw) * _viv_zoom_presets[_viv_zoom_pos]);
- rh = rh + (int)((max_zoom_high - rh) * _viv_zoom_presets[_viv_zoom_pos]);
+ double zoom_factor = pow((double)max_zoom_wide / (double)rw, (double)_viv_zoom_pos / (double)(_VIV_ZOOM_MAX - 1));
+ rw = (int)((double)rw * zoom_factor);
+ rh = (int)((double)rh * zoom_factor);
}
*prw = rw;
@@ -7128,6 +7160,7 @@ static void _viv_check_menus(HMENU hmenu)
CheckMenuItem(hmenu,VIV_ID_VIEW_ALLOW_SHRINKING,config_allow_shrinking ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu,VIV_ID_VIEW_KEEP_ASPECT_RATIO,config_keep_aspect_ratio ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu,VIV_ID_VIEW_FILL_WINDOW,fill_window ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hmenu,VIV_ID_VIEW_SMOOTH,(config_shrink_blit_mode == CONFIG_SHRINK_BLIT_MODE_HALFTONE) ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu,VIV_ID_VIEW_1TO1,((rw == _viv_image_wide) && (rh == _viv_image_high)) ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu,VIV_ID_VIEW_FULLSCREEN,_viv_is_fullscreen ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu,VIV_ID_VIEW_SLIDESHOW,is_slideshow ? MF_CHECKED : MF_UNCHECKED);
@@ -7212,7 +7245,7 @@ static void _viv_delete(int permanently)
fo.hwnd = _viv_hwnd;
fo.wFunc = FO_DELETE;
fo.pFrom = filename_list;
- fo.fFlags = permanently ? 0 : FOF_ALLOWUNDO;
+ fo.fFlags = permanently ? FOF_WANTNUKEWARNING : FOF_ALLOWUNDO;
if (SHFileOperation(&fo) == 0)
{
@@ -7295,8 +7328,8 @@ static INT_PTR CALLBACK _viv_rename_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM
fo.pTo = new_filename_list;
fo.fFlags = FOF_ALLOWUNDO | FOF_WANTMAPPINGHANDLE;
- // returns ERROR_CANCELLED if user cancelled.
- if (SHFileOperation(&fo) == 0)
+ int sh_ret = SHFileOperation(&fo);
+ if (sh_ret == 0)
{
if (fo.fAnyOperationsAborted)
{
@@ -7316,7 +7349,7 @@ static INT_PTR CALLBACK _viv_rename_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM
mappings = (_viv_name_mapping_t *)fo.hNameMappings;
- if (mappings->count == 1)
+ if (mappings->count >= 1)
{
// use the resolved name incase there was a rename collision.
file_op_new_name = mappings->mappings[0].pszNewPath;
@@ -7340,6 +7373,11 @@ static INT_PTR CALLBACK _viv_rename_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM
SHFreeNameMappings(fo.hNameMappings);
}
}
+ else
+ {
+ // returned non-zero (user cancelled or clicked No)
+ dont_end_dialog = 1;
+ }
}
if (!dont_end_dialog)
@@ -7505,7 +7543,7 @@ static void _viv_set_clipboard_image(void)
{
HBITMAP mem1_hbitmap;
- mem1_hbitmap = CreateCompatibleBitmap(screen_hdc,_viv_image_wide,_viv_image_high);
+ mem1_hbitmap = _viv_create_bitmap(screen_hdc,_viv_image_wide,_viv_image_high);
if (mem1_hbitmap)
{
HGDIOBJ last_mem1_hbitmap;
@@ -7936,8 +7974,26 @@ static INT_PTR CALLBACK _viv_options_general_proc(HWND hwnd,UINT msg,WPARAM wPar
case WM_INITDIALOG:
{
int exti;
-
os_SetDlgItemText_localization_id(hwnd,IDC_APPDATA,LOCALIZATION_ID_STORE_SETTINGS_APPDATA);
+ os_SetDlgItemText_localization_id(hwnd,IDC_LANGUAGE_STATIC,LOCALIZATION_ID_LANGUAGE_STATIC);
+
+ {
+ HWND hLanguage = GetDlgItem(hwnd,IDC_LANGUAGE_COMBOBOX);
+ int index;
+
+ index = os_ComboBox_AddString_localization_id(hwnd,IDC_LANGUAGE_COMBOBOX,LOCALIZATION_ID_LANGUAGE_AUTO);
+ ComboBox_SetItemData(hLanguage,index,255);
+ if (config_language == 255) ComboBox_SetCurSel(hLanguage,index);
+
+ for(int i=0;i= L'0') && (*p <= L'9')) || (*p == L'.'))
+ {
+ double val = 0.0;
+ double frac = 0.0;
+ double div = 10.0;
+
+ while ((*p >= L'0') && (*p <= L'9'))
+ {
+ val = (val * 10.0) + (*p - L'0');
+ p++;
+ }
+ if (*p == L'.')
+ {
+ p++;
+ while ((*p >= L'0') && (*p <= L'9'))
+ {
+ frac += (*p - L'0') / div;
+ div *= 10.0;
+ p++;
+ }
+ }
+ val += frac;
+ if (val > 0.0)
+ {
+ values[count++] = val;
+ }
+ }
+ else
+ {
+ p++;
+ }
+ }
+
+ {
+ int i, j;
+ for (i = 0; i < count - 1; i++)
+ {
+ for (j = i + 1; j < count; j++)
+ {
+ if (values[j] < values[i])
+ {
+ double tmp = values[i];
+ values[i] = values[j];
+ values[j] = tmp;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+static double _viv_calculate_next_zoom(double current_percent,int zoom_in)
+{
+ double new_percent = current_percent;
+ int step = config_zoom_step > 0 ? config_zoom_step : CONFIG_ZOOM_DEFAULT_STEP;
+
+ if (config_zoom_calc_method == CONFIG_ZOOM_CALC_RELATIVE)
+ {
+ if (zoom_in)
+ {
+ new_percent = current_percent * (1.0 + ((double)step / 100.0));
+ }
+ else
+ {
+ new_percent = current_percent / (1.0 + ((double)step / 100.0));
+ }
+ }
+ else if (config_zoom_calc_method == CONFIG_ZOOM_CALC_ABSOLUTE)
+ {
+ if (zoom_in)
+ {
+ new_percent = current_percent + (double)step;
+ }
+ else
+ {
+ new_percent = current_percent - (double)step;
+ }
+ }
+ else if (config_zoom_calc_method == CONFIG_ZOOM_CALC_FIXED_TABLE)
+ {
+ double table_values[64];
+ int table_count = _viv_parse_fixed_table(config_zoom_fixed_table, table_values, 64);
+ if (table_count <= 0)
+ {
+ table_count = _viv_parse_fixed_table(CONFIG_ZOOM_DEFAULT_FIXED_TABLE, table_values, 64);
+ }
+
+ if (table_count > 0)
+ {
+ int i;
+ if (zoom_in)
+ {
+ new_percent = table_values[table_count - 1];
+ for (i = 0; i < table_count; i++)
+ {
+ if (table_values[i] > current_percent + 0.001)
+ {
+ new_percent = table_values[i];
+ break;
+ }
+ }
+ }
+ else
+ {
+ new_percent = table_values[0];
+ for (i = table_count - 1; i >= 0; i--)
+ {
+ if (table_values[i] < current_percent - 0.001)
+ {
+ new_percent = table_values[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (new_percent < 0.1) new_percent = 0.1;
+ if (new_percent > 10000.0) new_percent = 10000.0;
+
+ return new_percent;
+}
+
+static INT_PTR CALLBACK _viv_options_zoom_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t wbuf[STRING_SIZE];
+
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_STEP_STATIC,LOCALIZATION_ID_OPTIONS_ZOOM_STEP_STATIC);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_STEP_DEFAULT_STATIC,LOCALIZATION_ID_OPTIONS_ZOOM_STEP_DEFAULT_STATIC);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_CALC_METHOD_STATIC,LOCALIZATION_ID_OPTIONS_ZOOM_CALC_METHOD_STATIC);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_CALC_RELATIVE_RADIO,LOCALIZATION_ID_OPTIONS_ZOOM_CALC_RELATIVE_RADIO);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_CALC_ABSOLUTE_RADIO,LOCALIZATION_ID_OPTIONS_ZOOM_CALC_ABSOLUTE_RADIO);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_CALC_FIXED_TABLE_RADIO,LOCALIZATION_ID_OPTIONS_ZOOM_CALC_FIXED_TABLE_RADIO);
+ os_SetDlgItemText_localization_id(hwnd,IDC_ZOOM_LOAD_DEFAULT_TABLE_BUTTON,LOCALIZATION_ID_OPTIONS_ZOOM_LOAD_DEFAULT_TABLE_BUTTON);
+
+ string_format_number(wbuf,config_zoom_step);
+ SetDlgItemText(hwnd,IDC_ZOOM_STEP_EDIT,wbuf);
+
+ if (config_zoom_calc_method == CONFIG_ZOOM_CALC_RELATIVE)
+ {
+ CheckRadioButton(hwnd,IDC_ZOOM_CALC_RELATIVE_RADIO,IDC_ZOOM_CALC_FIXED_TABLE_RADIO,IDC_ZOOM_CALC_RELATIVE_RADIO);
+ }
+ else if (config_zoom_calc_method == CONFIG_ZOOM_CALC_ABSOLUTE)
+ {
+ CheckRadioButton(hwnd,IDC_ZOOM_CALC_RELATIVE_RADIO,IDC_ZOOM_CALC_FIXED_TABLE_RADIO,IDC_ZOOM_CALC_ABSOLUTE_RADIO);
+ }
+ else
+ {
+ CheckRadioButton(hwnd,IDC_ZOOM_CALC_RELATIVE_RADIO,IDC_ZOOM_CALC_FIXED_TABLE_RADIO,IDC_ZOOM_CALC_FIXED_TABLE_RADIO);
+ }
+
+ SetDlgItemText(hwnd,IDC_ZOOM_FIXED_TABLE_EDIT,config_zoom_fixed_table);
+
+ return FALSE;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_ZOOM_LOAD_DEFAULT_TABLE_BUTTON:
+ SetDlgItemText(hwnd,IDC_ZOOM_FIXED_TABLE_EDIT,CONFIG_ZOOM_DEFAULT_FIXED_TABLE);
+ break;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
static void _viv_options_treeview_changed(HWND hwnd)
{
TV_ITEM tvi;
@@ -8649,6 +8931,17 @@ static INT_PTR CALLBACK _viv_options_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARA
general_page = GetDlgItem(hwnd,VIV_ID_OPTIONS_GENERAL);
view_page = GetDlgItem(hwnd,VIV_ID_OPTIONS_VIEW);
controls_page = GetDlgItem(hwnd,VIV_ID_OPTIONS_CONTROLS);
+
+ {
+ BYTE new_language = (BYTE)ComboBox_GetItemData(GetDlgItem(general_page,IDC_LANGUAGE_COMBOBOX),ComboBox_GetCurSel(GetDlgItem(general_page,IDC_LANGUAGE_COMBOBOX)));
+ if (new_language != config_language)
+ {
+ config_language = new_language;
+ localization_init();
+ // Note: full UI refresh might be needed for dynamic change, but for now we just save it.
+ }
+ }
+
config_multiple_instances = 0;
if (IsDlgButtonChecked(general_page,IDC_APPDATA) == BST_CHECKED)
@@ -8774,6 +9067,38 @@ static INT_PTR CALLBACK _viv_options_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARA
config_loop_animations_once = IsDlgButtonChecked(view_page,IDC_LOOP_ANIMATIONS_ONCE_STATIC) == BST_CHECKED ? 1 : 0;
config_preload_next = IsDlgButtonChecked(view_page,IDC_PRELOAD_NEXT_IMAGE_STATIC) == BST_CHECKED ? 1 : 0;
config_cache_last = IsDlgButtonChecked(view_page,IDC_CACHE_LAST_IMAGE_STATIC) == BST_CHECKED ? 1 : 0;
+
+ {
+ HWND zoom_page;
+ zoom_page = GetDlgItem(hwnd,VIV_ID_OPTIONS_ZOOM);
+ if (zoom_page)
+ {
+ wchar_t wbuf[STRING_SIZE];
+ int step;
+
+ GetDlgItemText(zoom_page,IDC_ZOOM_STEP_EDIT,wbuf,STRING_SIZE);
+ step = string_to_int(wbuf);
+ if (step > 0)
+ {
+ config_zoom_step = step;
+ }
+
+ if (IsDlgButtonChecked(zoom_page,IDC_ZOOM_CALC_RELATIVE_RADIO) == BST_CHECKED)
+ {
+ config_zoom_calc_method = CONFIG_ZOOM_CALC_RELATIVE;
+ }
+ else if (IsDlgButtonChecked(zoom_page,IDC_ZOOM_CALC_ABSOLUTE_RADIO) == BST_CHECKED)
+ {
+ config_zoom_calc_method = CONFIG_ZOOM_CALC_ABSOLUTE;
+ }
+ else if (IsDlgButtonChecked(zoom_page,IDC_ZOOM_CALC_FIXED_TABLE_RADIO) == BST_CHECKED)
+ {
+ config_zoom_calc_method = CONFIG_ZOOM_CALC_FIXED_TABLE;
+ }
+
+ GetDlgItemText(zoom_page,IDC_ZOOM_FIXED_TABLE_EDIT,config_zoom_fixed_table,STRING_SIZE);
+ }
+ }
// copy keys.
_viv_key_list_copy(_viv_key_list,(_viv_key_list_t *)GetWindowLongPtr(GetDlgItem(controls_page,IDC_COMMANDS_LIST),GWLP_USERDATA));
@@ -9321,9 +9646,12 @@ static void _viv_view_1to1(void)
if (_viv_have_old_zoom)
{
_viv_1to1 = 0;
+ _viv_is_custom_zoom = _viv_old_is_custom_zoom;
+ _viv_custom_zoom_percent = _viv_old_custom_zoom_percent;
_viv_zoom_pos = _viv_old_zoom_pos;
_viv_view_set(_viv_view_x,_viv_view_y,1);
InvalidateRect(_viv_hwnd,0,FALSE);
+ _viv_toolbar_update_buttons();
return;
}
@@ -9331,11 +9659,15 @@ static void _viv_view_1to1(void)
_viv_have_old_zoom = 1;
_viv_old_zoom_pos = _viv_zoom_pos;
+ _viv_old_is_custom_zoom = _viv_is_custom_zoom;
+ _viv_old_custom_zoom_percent = _viv_custom_zoom_percent;
_viv_1to1 = 1;
+ _viv_is_custom_zoom = 0;
_viv_zoom_pos = 0;
_viv_view_set(_viv_view_x,_viv_view_y,1);
InvalidateRect(_viv_hwnd,0,FALSE);
+ _viv_toolbar_update_buttons();
}
static void _viv_playlist_add_current_if_empty(void)
@@ -10260,7 +10592,7 @@ static int _viv_webp_frame_proc(_viv_webp_t *viv_webp,BYTE *pixels,int delay)
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
- hbitmap = CreateCompatibleBitmap(viv_webp->screen_hdc,viv_webp->wide,viv_webp->high);
+ hbitmap = _viv_create_bitmap(viv_webp->screen_hdc,viv_webp->wide,viv_webp->high);
// Set RGB data to the bitmap
if (hbitmap)
@@ -10612,7 +10944,7 @@ static DWORD WINAPI _viv_load_image_thread_proc(void *param)
break;
}
- hbitmap = CreateCompatibleBitmap(screen_hdc,load_wide,load_high);
+ hbitmap = _viv_create_bitmap(screen_hdc,load_wide,load_high);
if (hbitmap)
{
UINT image_flags;
@@ -11692,7 +12024,7 @@ static void _viv_toolbar_update_buttons(void)
tbbinfo.cbSize = sizeof(TBBUTTONINFO);
tbbinfo.dwMask = TBIF_STATE;
- tbbinfo.fsState = ((_viv_zoom_pos == 0) && (!_viv_1to1)) ? (0) : (TBSTATE_ENABLED);
+ tbbinfo.fsState = ((!_viv_is_custom_zoom) && (_viv_zoom_pos == 0) && (!_viv_1to1)) ? (0) : (TBSTATE_ENABLED);
SendMessage(_viv_toolbar_hwnd,TB_SETBUTTONINFO,VIV_ID_VIEW_BESTFIT,(LPARAM)&tbbinfo);
}
@@ -11974,7 +12306,7 @@ static void _viv_update_color_button_bitmap(HWND hwnd)
screen_hdc = GetDC(0);
mem_hdc = CreateCompatibleDC(screen_hdc);
- hbitmap = CreateCompatibleBitmap(screen_hdc,wide,high);
+ hbitmap = _viv_create_bitmap(screen_hdc,wide,high);
last_hbitmap = SelectObject(mem_hdc,hbitmap);
@@ -13279,67 +13611,1715 @@ static void _viv_show_jumpto(void)
}
}
-static void _viv_nav_item_free_all(void)
-{
- if (_viv_nav_items)
- {
- int i;
-
- for(i=0;i<_viv_nav_item_count;i++)
- {
- mem_free(_viv_nav_items[i]);
- }
-
- mem_free(_viv_nav_items);
+//
+// Compare Images Implementation
+//
- __viv_nav_item_start = 0;
- _viv_nav_item_last = 0;
- _viv_nav_items = 0;
- _viv_nav_item_count = 0;
- }
-}
+#define _VIV_COMPARE_PANE_COUNT 2
+
+enum {
+ _VIV_COMPARE_BTN_NONE = 0,
+ _VIV_COMPARE_BTN_PREV,
+ _VIV_COMPARE_BTN_NEXT,
+ _VIV_COMPARE_BTN_ZOOM_IN,
+ _VIV_COMPARE_BTN_ZOOM_OUT,
+ _VIV_COMPARE_BTN_ZOOM_TEXT,
+ _VIV_COMPARE_BTN_1TO1,
+ _VIV_COMPARE_BTN_FIT,
+ _VIV_COMPARE_BTN_ROTATE,
+ _VIV_COMPARE_BTN_SELECT,
+ _VIV_COMPARE_BTN_USE_CURRENT,
+
+ _VIV_COMPARE_TOP_AUTOFIT = 101,
+ _VIV_COMPARE_TOP_SMOOTH = 102,
+ _VIV_COMPARE_TOP_FILENAME = 103,
+ _VIV_COMPARE_TOP_CLOSE = 104
+};
-static void _viv_nav_item_add(WIN32_FIND_DATA *fd)
+typedef struct _viv_compare_pane_s
{
- _viv_nav_item_t *navitem;
-
- navitem = mem_alloc(sizeof(_viv_nav_item_t));
+ wchar_t filename[STRING_SIZE];
+ VIV_UINT64 filesize;
+ int image_wide;
+ int image_high;
+ HBITMAP hbitmap;
+ _viv_mipmap_t *mipmap;
+ int orientation;
- os_copy_memory(&navitem->fd,fd,sizeof(WIN32_FIND_DATA));
-
- if (__viv_nav_item_start)
+ double zoom_percent;
+ int is_custom_zoom;
+ int is_1to1;
+ int is_autofit;
+ int view_x;
+ int view_y;
+
+ RECT pane_rect;
+ RECT toolbar_rect;
+ RECT viewport_rect;
+
+ RECT btn_prev_rect;
+ RECT btn_next_rect;
+ RECT btn_zoom_in_rect;
+ RECT btn_zoom_out_rect;
+ RECT zoom_text_rect;
+ RECT btn_1to1_rect;
+ RECT btn_fit_rect;
+ RECT btn_rotate_rect;
+ RECT btn_select_rect;
+ RECT use_current_rect;
+
+ int use_current;
+
+ _viv_playlist_t *playlist_item;
+} _viv_compare_pane_t;
+
+typedef struct _viv_compare_state_s
+{
+ HWND hwnd;
+ int pane_count;
+ _viv_compare_pane_t panes[_VIV_COMPARE_PANE_COUNT];
+
+ int opt_autofit;
+ int opt_smooth;
+ int opt_filename;
+
+ RECT topbar_rect;
+ RECT tips_rect;
+ RECT autofit_rect;
+ RECT smooth_rect;
+ RECT filename_rect;
+ RECT close_btn_rect;
+
+ int is_dragging;
+ int drag_last_x;
+ int drag_last_y;
+ int drag_pane_index;
+ int drag_is_individual;
+
+ int hovered_pane;
+ int hovered_btn;
+ int hovered_top;
+
+ HFONT font_ui;
+ HFONT font_bold;
+ HFONT font_overlay;
+} _viv_compare_state_t;
+
+static _viv_compare_state_t *_viv_compare_state = NULL;
+
+static void _viv_compare_free_pane_image(_viv_compare_pane_t *pane)
+{
+ if (pane->mipmap)
{
- _viv_nav_item_last->next = navitem;
+ _viv_mipmap_free(pane->mipmap);
+ pane->mipmap = NULL;
}
- else
+ if (pane->hbitmap)
{
- __viv_nav_item_start = navitem;
+ DeleteObject(pane->hbitmap);
+ pane->hbitmap = NULL;
}
-
- navitem->next = 0;
- _viv_nav_item_last = navitem;
- _viv_nav_item_count++;
+ pane->image_wide = 0;
+ pane->image_high = 0;
+ pane->filesize = 0;
}
-static int _viv_nav_compare(const _viv_nav_item_t *a,const _viv_nav_item_t *b)
+typedef struct {
+ _viv_compare_pane_t *pane;
+ DWORD wide;
+ DWORD high;
+ int has_alpha;
+ int orientation;
+} _viv_compare_webp_ctx_t;
+
+static int _viv_compare_webp_info_proc(void *user_data, DWORD frame_count, DWORD wide, DWORD high, int has_alpha)
{
- return _viv_fd_compare_name(&a->fd,&b->fd);
+ _viv_compare_webp_ctx_t *ctx = (_viv_compare_webp_ctx_t *)user_data;
+ ctx->wide = wide;
+ ctx->high = high;
+ ctx->has_alpha = has_alpha;
+ return 1;
}
-static INT_PTR CALLBACK _viv_search_everything_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+static int _viv_compare_webp_frame_proc(void *user_data, BYTE *pixels, int delay)
{
- switch(msg)
+ _viv_compare_webp_ctx_t *ctx = (_viv_compare_webp_ctx_t *)user_data;
+ _viv_compare_pane_t *pane = ctx->pane;
+ HDC screen_hdc;
+
+ if (pane->hbitmap)
{
- case WM_INITDIALOG:
-
- os_center_dialog(hwnd);
-
- SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
-
- os_SetWindowText_localization_id(hwnd,lParam ? LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION : LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION);
- os_SetDlgItemText_localization_id(hwnd,IDC_SEARCH_EVERYTHING_RANDOM_CHECKBOX,LOCALIZATION_ID_RANDOMIZE);
-
- return TRUE;
+ return 0;
+ }
+
+ if (ctx->has_alpha)
+ {
+ BYTE *p = pixels;
+ DWORD run = ctx->wide * ctx->high;
+ while (run--)
+ {
+ int a = p[3];
+ if (a != 255)
+ {
+ int r = p[0], g = p[1], b = p[2];
+ p[0] = config_windowed_background_color_r + ((r - config_windowed_background_color_r) * a) / 255;
+ p[1] = config_windowed_background_color_g + ((g - config_windowed_background_color_g) * a) / 255;
+ p[2] = config_windowed_background_color_b + ((b - config_windowed_background_color_b) * a) / 255;
+ p[3] = 255;
+ }
+ p += 4;
+ }
+ }
+
+ // Convert RGBA to BGR
+ {
+ const BYTE *p = pixels;
+ BYTE *d = pixels;
+ DWORD high_run = ctx->high;
+
+ while (high_run)
+ {
+ BYTE *wd = d;
+ DWORD wide_run = ctx->wide;
+ while (wide_run)
+ {
+ int r = p[0], g = p[1], b = p[2];
+ p += 4;
+ wd[0] = b;
+ wd[1] = g;
+ wd[2] = r;
+ wd += 3;
+ wide_run--;
+ }
+ d += (((ctx->wide * 3) + 3) / 4) * 4;
+ high_run--;
+ }
+ }
+
+ screen_hdc = GetDC(NULL);
+ if (screen_hdc)
+ {
+ HDC mem_hdc = CreateCompatibleDC(screen_hdc);
+ if (mem_hdc)
+ {
+ HBITMAP hbitmap = _viv_create_bitmap(screen_hdc, ctx->wide, ctx->high);
+ if (hbitmap)
+ {
+ BITMAPINFO bmi;
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = ctx->wide;
+ bmi.bmiHeader.biHeight = -(int)ctx->high;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 24;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ SetDIBits(mem_hdc, hbitmap, 0, ctx->high, pixels, &bmi, DIB_RGB_COLORS);
+
+ if (ctx->orientation > 1)
+ {
+ HBITMAP oriented = _viv_orientate_hbitmap(hbitmap, ctx->orientation);
+ if (oriented)
+ {
+ DeleteObject(hbitmap);
+ hbitmap = oriented;
+
+ switch (ctx->orientation)
+ {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ {
+ DWORD tmp = ctx->wide;
+ ctx->wide = ctx->high;
+ ctx->high = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ pane->hbitmap = hbitmap;
+ pane->image_wide = ctx->wide;
+ pane->image_high = ctx->high;
+ pane->zoom_percent = 100.0;
+ pane->is_autofit = 1;
+ pane->view_x = 0;
+ pane->view_y = 0;
+ }
+ DeleteDC(mem_hdc);
+ }
+ ReleaseDC(NULL, screen_hdc);
+ }
+
+ return 1;
+}
+
+static BOOL _viv_compare_load_image(const wchar_t *filename, _viv_compare_pane_t *pane)
+{
+ HANDLE h;
+ DWORD size;
+ HANDLE global_handle;
+ char *buf;
+ IStream *stream = NULL;
+ void *image = NULL;
+ int load_ret = 1;
+ WIN32_FIND_DATA fd;
+
+ _viv_compare_free_pane_image(pane);
+
+ if (!filename || !*filename)
+ {
+ pane->filename[0] = 0;
+ return FALSE;
+ }
+
+ string_copy(pane->filename, filename);
+
+ if (os_GetFileAttributesExW && os_GetFileAttributesExW(filename, GetFileExInfoStandard, &fd))
+ {
+ pane->filesize = ((VIV_UINT64)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
+ }
+ else
+ {
+ pane->filesize = 0;
+ }
+
+ h = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | ((os_major_version >= 5) ? FILE_SHARE_DELETE : 0), 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ return FALSE;
+ }
+
+ size = GetFileSize(h, 0);
+ if (pane->filesize == 0)
+ {
+ pane->filesize = size;
+ }
+
+ global_handle = GlobalAlloc(GMEM_MOVEABLE, size);
+ if (!global_handle)
+ {
+ CloseHandle(h);
+ return FALSE;
+ }
+
+ buf = (char *)GlobalLock(global_handle);
+ if (buf)
+ {
+ DWORD numread;
+ DWORD totreadsize = size;
+ char *d = buf;
+
+ while (totreadsize)
+ {
+ DWORD readsize = 1 * 1024 * 1024;
+ if (readsize > totreadsize) readsize = totreadsize;
+ if (ReadFile(h, d, readsize, &numread, 0))
+ {
+ if (!numread) break;
+ d += numread;
+ totreadsize -= numread;
+ }
+ else
+ {
+ break;
+ }
+ }
+ GlobalUnlock(global_handle);
+
+ if (totreadsize == 0)
+ {
+ if (SUCCEEDED(CreateStreamOnHGlobal(global_handle, TRUE, &stream)))
+ {
+ global_handle = 0;
+ }
+ }
+ }
+
+ if (global_handle)
+ {
+ GlobalFree(global_handle);
+ }
+ CloseHandle(h);
+
+ if (!stream)
+ {
+ return FALSE;
+ }
+
+ if (config_orientation)
+ {
+ pane->orientation = os_get_orientation(filename);
+ }
+ else
+ {
+ pane->orientation = 0;
+ }
+
+ if (os_GdipLoadImageFromStream)
+ {
+ if (config_icm && os_GdipLoadImageFromStreamICM)
+ {
+ load_ret = os_GdipLoadImageFromStreamICM(stream, &image);
+ }
+ else
+ {
+ load_ret = os_GdipLoadImageFromStream(stream, &image);
+ }
+ }
+
+ if (load_ret == 0 && image)
+ {
+ UINT raw_wide = 0, raw_high = 0;
+ if (os_GdipGetImageWidth(image, &raw_wide) == 0 && os_GdipGetImageHeight(image, &raw_high) == 0)
+ {
+ HDC screen_hdc = GetDC(0);
+ if (screen_hdc)
+ {
+ HDC mem_hdc = CreateCompatibleDC(screen_hdc);
+ if (mem_hdc)
+ {
+ HBITMAP hbitmap = _viv_create_bitmap(screen_hdc, raw_wide, raw_high);
+ if (hbitmap)
+ {
+ HGDIOBJ last_hbitmap = SelectObject(mem_hdc, hbitmap);
+ void *g = NULL;
+ if (os_GdipCreateFromHDC(mem_hdc, &g) == 0 && g)
+ {
+ UINT image_flags = 0;
+
+ if (os_GdipImageSelectActiveFrame)
+ {
+ os_GdipImageSelectActiveFrame(image, &_viv_FrameDimensionTime, 0);
+ }
+
+ if (os_GdipGetImageFlags)
+ {
+ os_GdipGetImageFlags(image, &image_flags);
+ }
+
+ // If has alpha, fill background with window background color and use SourceOver
+ if (image_flags & 2)
+ {
+ RECT rc = {0, 0, (int)raw_wide, (int)raw_high};
+ HBRUSH bg_br = CreateSolidBrush(RGB(config_windowed_background_color_r, config_windowed_background_color_g, config_windowed_background_color_b));
+ FillRect(mem_hdc, &rc, bg_br);
+ DeleteObject(bg_br);
+ os_GdipSetCompositingMode(g, 0); // CompositingModeSourceOver
+ }
+ else
+ {
+ os_GdipSetCompositingMode(g, 1); // CompositingModeSourceCopy
+ }
+
+ os_GdipSetCompositingQuality(g, 1);
+ os_GdipSetInterpolationMode(g, 5);
+ os_GdipSetPixelOffsetMode(g, 3);
+ os_GdipSetSmoothingMode(g, 3);
+ os_GdipDrawImageRectI(g, image, 0, 0, raw_wide, raw_high);
+ os_GdipDeleteGraphics(g);
+
+ SelectObject(mem_hdc, last_hbitmap);
+
+ if (pane->orientation > 1)
+ {
+ HBITMAP oriented = _viv_orientate_hbitmap(hbitmap, pane->orientation);
+ if (oriented)
+ {
+ DeleteObject(hbitmap);
+ hbitmap = oriented;
+
+ switch (pane->orientation)
+ {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ {
+ UINT tmp = raw_wide;
+ raw_wide = raw_high;
+ raw_high = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ pane->hbitmap = hbitmap;
+ pane->image_wide = raw_wide;
+ pane->image_high = raw_high;
+ pane->zoom_percent = 100.0;
+ pane->is_autofit = 1;
+ pane->view_x = 0;
+ pane->view_y = 0;
+ }
+ else
+ {
+ SelectObject(mem_hdc, last_hbitmap);
+ DeleteObject(hbitmap);
+ }
+ }
+ DeleteDC(mem_hdc);
+ }
+ ReleaseDC(0, screen_hdc);
+ }
+ }
+ os_GdipDisposeImage(image);
+ }
+ else
+ {
+ // Fallback to WebP loader
+ _viv_compare_webp_ctx_t ctx;
+ ctx.pane = pane;
+ ctx.wide = 0;
+ ctx.high = 0;
+ ctx.has_alpha = 0;
+ ctx.orientation = pane->orientation;
+
+ webp_load(stream, &ctx, _viv_compare_webp_info_proc, _viv_compare_webp_frame_proc);
+ }
+
+ IStream_Release(stream);
+ return (pane->hbitmap != NULL);
+}
+
+static void _viv_compare_format_filesize(wchar_t *buf, VIV_UINT64 size)
+{
+ if (size >= 1024 * 1024 * 1024)
+ {
+ double gb = (double)size / (1024.0 * 1024.0 * 1024.0);
+ string_printf(buf, "%f GB", gb);
+ }
+ else if (size >= 1024 * 1024)
+ {
+ double mb = (double)size / (1024.0 * 1024.0);
+ string_printf(buf, "%f MB", mb);
+ }
+ else if (size >= 1024)
+ {
+ double kb = (double)size / 1024.0;
+ string_printf(buf, "%f KB", kb);
+ }
+ else
+ {
+ string_printf(buf, "%u Bytes", (DWORD)size);
+ }
+}
+
+static void _viv_compare_update_layout(_viv_compare_state_t *cs, int wide, int high)
+{
+ int top_h = 32;
+ int toolbar_h = 28;
+ int pane_w, i;
+
+ if (wide < 100) wide = 100;
+ if (high < 100) high = 100;
+
+ cs->topbar_rect = (RECT){0, 0, wide, top_h};
+ cs->tips_rect = (RECT){10, 6, wide - 420, top_h - 2};
+ cs->autofit_rect = (RECT){wide - 410, 6, wide - 330, top_h - 6};
+ cs->smooth_rect = (RECT){wide - 320, 6, wide - 240, top_h - 6};
+ cs->filename_rect = (RECT){wide - 230, 6, wide - 130, top_h - 6};
+ cs->close_btn_rect = (RECT){wide - 110, 4, wide - 10, top_h - 4};
+
+ pane_w = wide / cs->pane_count;
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ int p_left = i * pane_w;
+ int p_right = (i == cs->pane_count - 1) ? wide : (p_left + pane_w);
+ _viv_compare_pane_t *pane = &cs->panes[i];
+
+ pane->pane_rect = (RECT){p_left, top_h, p_right, high};
+ pane->toolbar_rect = (RECT){p_left, top_h, p_right, top_h + toolbar_h};
+ pane->viewport_rect = (RECT){p_left, top_h + toolbar_h, p_right, high};
+
+ int by = top_h + 3;
+ int bh = toolbar_h - 6;
+ int bx = p_left + 6;
+
+ pane->btn_prev_rect = (RECT){bx, by, bx + 22, by + bh}; bx += 24;
+ pane->btn_next_rect = (RECT){bx, by, bx + 22, by + bh}; bx += 28;
+ pane->btn_zoom_in_rect = (RECT){bx, by, bx + 22, by + bh}; bx += 24;
+ pane->btn_zoom_out_rect = (RECT){bx, by, bx + 22, by + bh}; bx += 24;
+ pane->zoom_text_rect = (RECT){bx, by, bx + 55, by + bh}; bx += 59;
+ pane->btn_1to1_rect = (RECT){bx, by, bx + 32, by + bh}; bx += 36;
+ pane->btn_fit_rect = (RECT){bx, by, bx + 32, by + bh}; bx += 36;
+ pane->btn_rotate_rect = (RECT){bx, by, bx + 32, by + bh}; bx += 36;
+ pane->btn_select_rect = (RECT){p_right - 30, by, p_right - 6, by + bh};
+ pane->use_current_rect = (RECT){p_right - 215, by, p_right - 36, by + bh};
+ }
+}
+
+static int _viv_compare_hit_test(_viv_compare_state_t *cs, int x, int y, int *out_pane, int *out_btn)
+{
+ int i;
+ *out_pane = -1;
+ *out_btn = _VIV_COMPARE_BTN_NONE;
+
+ if (PtInRect(&cs->close_btn_rect, (POINT){x, y})) return _VIV_COMPARE_TOP_CLOSE;
+ if (PtInRect(&cs->autofit_rect, (POINT){x, y})) return _VIV_COMPARE_TOP_AUTOFIT;
+ if (PtInRect(&cs->smooth_rect, (POINT){x, y})) return _VIV_COMPARE_TOP_SMOOTH;
+ if (PtInRect(&cs->filename_rect, (POINT){x, y})) return _VIV_COMPARE_TOP_FILENAME;
+
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ _viv_compare_pane_t *pane = &cs->panes[i];
+ if (PtInRect(&pane->pane_rect, (POINT){x, y}))
+ {
+ *out_pane = i;
+ if (PtInRect(&pane->btn_prev_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_PREV; return 0; }
+ if (PtInRect(&pane->btn_next_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_NEXT; return 0; }
+ if (PtInRect(&pane->btn_zoom_in_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_ZOOM_IN; return 0; }
+ if (PtInRect(&pane->btn_zoom_out_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_ZOOM_OUT; return 0; }
+ if (PtInRect(&pane->zoom_text_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_ZOOM_TEXT; return 0; }
+ if (PtInRect(&pane->btn_1to1_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_1TO1; return 0; }
+ if (PtInRect(&pane->btn_fit_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_FIT; return 0; }
+ if (PtInRect(&pane->btn_rotate_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_ROTATE; return 0; }
+ if (PtInRect(&pane->use_current_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_USE_CURRENT; return 0; }
+ if (PtInRect(&pane->btn_select_rect, (POINT){x, y})) { *out_btn = _VIV_COMPARE_BTN_SELECT; return 0; }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static void _viv_compare_zoom_pane(_viv_compare_pane_t *pane, int zoom_in, int center_x, int center_y)
+{
+ int vw, vh, old_rw, old_rh, rx, ry, old_cursor_px, old_cursor_py;
+ int new_rw, new_rh;
+ __int64 new_cursor_px, new_cursor_py;
+
+ if (!pane->hbitmap || pane->image_wide <= 0 || pane->image_high <= 0)
+ return;
+
+ vw = pane->viewport_rect.right - pane->viewport_rect.left;
+ vh = pane->viewport_rect.bottom - pane->viewport_rect.top;
+ if (vw <= 0 || vh <= 0) return;
+
+ old_rw = (int)((pane->image_wide * pane->zoom_percent) / 100.0 + 0.5);
+ old_rh = (int)((pane->image_high * pane->zoom_percent) / 100.0 + 0.5);
+ if (old_rw < 1) old_rw = 1;
+ if (old_rh < 1) old_rh = 1;
+
+ rx = pane->viewport_rect.left + (vw / 2) - (old_rw / 2) - pane->view_x;
+ ry = pane->viewport_rect.top + (vh / 2) - (old_rh / 2) - pane->view_y;
+
+ old_cursor_px = center_x - rx;
+ old_cursor_py = center_y - ry;
+
+ pane->zoom_percent = _viv_calculate_next_zoom(pane->zoom_percent, zoom_in);
+ pane->is_autofit = 0;
+ pane->is_custom_zoom = 1;
+ pane->is_1to1 = 0;
+
+ new_rw = (int)((pane->image_wide * pane->zoom_percent) / 100.0 + 0.5);
+ new_rh = (int)((pane->image_high * pane->zoom_percent) / 100.0 + 0.5);
+ if (new_rw < 1) new_rw = 1;
+ if (new_rh < 1) new_rh = 1;
+
+ new_cursor_px = old_rw ? ((__int64)old_cursor_px * new_rw) / old_rw : 0;
+ new_cursor_py = old_rh ? ((__int64)old_cursor_py * new_rh) / old_rh : 0;
+
+ pane->view_x = (vw / 2) - (new_rw / 2) - (center_x - pane->viewport_rect.left) + (int)new_cursor_px;
+ pane->view_y = (vh / 2) - (new_rh / 2) - (center_y - pane->viewport_rect.top) + (int)new_cursor_py;
+}
+
+static void _viv_compare_pick_image_for_pane(HWND hwnd, _viv_compare_pane_t *pane)
+{
+ OPENFILENAMEW ofn;
+ wchar_t filename[STRING_SIZE];
+ wchar_t filter[STRING_SIZE];
+ int i;
+
+ filename[0] = 0;
+ ZeroMemory(&ofn, sizeof(OPENFILENAMEW));
+ ofn.lStructSize = sizeof(OPENFILENAMEW);
+ ofn.hwndOwner = hwnd;
+
+ string_copy_utf8_string(filter, localization_get_string(LOCALIZATION_ID_OPEN_ALL_IMAGE_FILES));
+ i = string_get_length(filter);
+ filter[i++] = 0;
+ string_copy(filter + i, L"*.bmp;*.gif;*.ico;*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.webp");
+ i += string_get_length(filter + i);
+ filter[i++] = 0;
+ string_copy_utf8_string(filter + i, localization_get_string(LOCALIZATION_ID_OPEN_ALL_FILES));
+ i += string_get_length(filter + i);
+ filter[i++] = 0;
+ string_copy(filter + i, L"*.*");
+ i += string_get_length(filter + i);
+ filter[i++] = 0;
+ filter[i++] = 0;
+
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = STRING_SIZE;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+
+ if (GetOpenFileNameW(&ofn))
+ {
+ pane->use_current = 0;
+ _viv_compare_load_image(filename, pane);
+ pane->playlist_item = NULL;
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+}
+
+static void _viv_compare_draw_button(HDC hdc, const RECT *rect, const wchar_t *text, int is_hovered, HFONT font)
+{
+ HGDIOBJ old_font = SelectObject(hdc, font);
+ HBRUSH br = CreateSolidBrush(is_hovered ? RGB(215, 230, 250) : RGB(242, 242, 242));
+ HPEN pen = CreatePen(PS_SOLID, 1, is_hovered ? RGB(100, 150, 220) : RGB(190, 190, 190));
+ HGDIOBJ old_br = SelectObject(hdc, br);
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+
+ RoundRect(hdc, rect->left, rect->top, rect->right, rect->bottom, 4, 4);
+
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, RGB(20, 20, 20));
+ DrawTextW(hdc, text, -1, (LPRECT)rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+
+ SelectObject(hdc, old_pen);
+ SelectObject(hdc, old_br);
+ SelectObject(hdc, old_font);
+ DeleteObject(pen);
+ DeleteObject(br);
+}
+
+static void _viv_compare_draw_checkbox(HDC hdc, const RECT *rect, const wchar_t *text, int is_checked, int is_hovered, HFONT font)
+{
+ HGDIOBJ old_font = SelectObject(hdc, font);
+ RECT box_rect = {rect->left, rect->top + (rect->bottom - rect->top - 14) / 2, rect->left + 14, rect->top + (rect->bottom - rect->top - 14) / 2 + 14};
+ RECT text_rect = {rect->left + 18, rect->top, rect->right, rect->bottom};
+
+ HBRUSH br = CreateSolidBrush(RGB(255, 255, 255));
+ HPEN pen = CreatePen(PS_SOLID, 1, is_hovered ? RGB(50, 120, 220) : RGB(140, 140, 140));
+ HGDIOBJ old_br = SelectObject(hdc, br);
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+
+ Rectangle(hdc, box_rect.left, box_rect.top, box_rect.right, box_rect.bottom);
+
+ if (is_checked)
+ {
+ HPEN check_pen = CreatePen(PS_SOLID, 2, RGB(20, 120, 20));
+ HGDIOBJ old_cpen = SelectObject(hdc, check_pen);
+ MoveToEx(hdc, box_rect.left + 3, box_rect.top + 7, NULL);
+ LineTo(hdc, box_rect.left + 6, box_rect.top + 10);
+ LineTo(hdc, box_rect.left + 11, box_rect.top + 3);
+ SelectObject(hdc, old_cpen);
+ DeleteObject(check_pen);
+ }
+
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, RGB(30, 30, 30));
+ DrawTextW(hdc, text, -1, &text_rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+
+ SelectObject(hdc, old_pen);
+ SelectObject(hdc, old_br);
+ SelectObject(hdc, old_font);
+ DeleteObject(pen);
+ DeleteObject(br);
+}
+
+static void _viv_compare_draw_checkbox_ex(HDC hdc, const RECT *rect, const wchar_t *text, int is_checked, int is_hovered, int is_disabled, HFONT font)
+{
+ HGDIOBJ old_font = SelectObject(hdc, font);
+ RECT box_rect = {rect->left, rect->top + (rect->bottom - rect->top - 14) / 2, rect->left + 14, rect->top + (rect->bottom - rect->top - 14) / 2 + 14};
+ RECT text_rect = {rect->left + 18, rect->top, rect->right, rect->bottom};
+ COLORREF box_bg = is_disabled ? RGB(230, 230, 230) : RGB(255, 255, 255);
+ COLORREF border_col = is_disabled ? RGB(180, 180, 180) : (is_hovered ? RGB(50, 120, 220) : RGB(140, 140, 140));
+ COLORREF text_col = is_disabled ? RGB(150, 150, 150) : RGB(30, 30, 30);
+
+ HBRUSH br = CreateSolidBrush(box_bg);
+ HPEN pen = CreatePen(PS_SOLID, 1, border_col);
+ HGDIOBJ old_br = SelectObject(hdc, br);
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+
+ Rectangle(hdc, box_rect.left, box_rect.top, box_rect.right, box_rect.bottom);
+
+ if (is_checked && !is_disabled)
+ {
+ HPEN check_pen = CreatePen(PS_SOLID, 2, RGB(20, 120, 20));
+ HGDIOBJ old_cpen = SelectObject(hdc, check_pen);
+ MoveToEx(hdc, box_rect.left + 3, box_rect.top + 7, NULL);
+ LineTo(hdc, box_rect.left + 6, box_rect.top + 10);
+ LineTo(hdc, box_rect.left + 11, box_rect.top + 3);
+ SelectObject(hdc, old_cpen);
+ DeleteObject(check_pen);
+ }
+
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, text_col);
+ DrawTextW(hdc, text, -1, &text_rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
+
+ SelectObject(hdc, old_pen);
+ SelectObject(hdc, old_br);
+ SelectObject(hdc, old_font);
+ DeleteObject(pen);
+ DeleteObject(br);
+}
+
+static void _viv_compare_paint(HWND hwnd, _viv_compare_state_t *cs)
+{
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hwnd, &ps);
+ RECT rc;
+ int wide, high, i;
+ HDC mem_hdc;
+ HBITMAP mem_hbitmap;
+ HGDIOBJ old_mem_bitmap;
+
+ GetClientRect(hwnd, &rc);
+ wide = rc.right - rc.left;
+ high = rc.bottom - rc.top;
+
+ if (wide <= 0 || high <= 0)
+ {
+ EndPaint(hwnd, &ps);
+ return;
+ }
+
+ mem_hdc = CreateCompatibleDC(hdc);
+ mem_hbitmap = CreateCompatibleBitmap(hdc, wide, high);
+ old_mem_bitmap = SelectObject(mem_hdc, mem_hbitmap);
+
+ _viv_compare_update_layout(cs, wide, high);
+
+ // 1. Draw Top Bar Background
+ {
+ HBRUSH top_br = CreateSolidBrush(RGB(242, 242, 245));
+ HPEN top_pen = CreatePen(PS_SOLID, 1, RGB(185, 185, 195));
+ HGDIOBJ old_br = SelectObject(mem_hdc, top_br);
+ HGDIOBJ old_pen = SelectObject(mem_hdc, top_pen);
+
+ FillRect(mem_hdc, &cs->topbar_rect, top_br);
+ MoveToEx(mem_hdc, 0, cs->topbar_rect.bottom - 1, NULL);
+ LineTo(mem_hdc, wide, cs->topbar_rect.bottom - 1);
+
+ SelectObject(mem_hdc, old_pen);
+ SelectObject(mem_hdc, old_br);
+ DeleteObject(top_pen);
+ DeleteObject(top_br);
+
+ // Tips Text
+ {
+ wchar_t tips_wbuf[STRING_SIZE];
+ string_copy_utf8_string(tips_wbuf, localization_get_string(LOCALIZATION_ID_COMPARE_TIPS));
+ HGDIOBJ old_font = SelectObject(mem_hdc, cs->font_ui);
+ SetBkMode(mem_hdc, TRANSPARENT);
+ SetTextColor(mem_hdc, RGB(60, 60, 60));
+ DrawTextW(mem_hdc, tips_wbuf, -1, &cs->tips_rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
+ SelectObject(mem_hdc, old_font);
+ }
+
+ // Checkboxes
+ {
+ wchar_t label[STRING_SIZE];
+ string_copy_utf8_string(label, localization_get_string(LOCALIZATION_ID_COMPARE_AUTO_FIT));
+ _viv_compare_draw_checkbox(mem_hdc, &cs->autofit_rect, label, cs->opt_autofit, cs->hovered_top == _VIV_COMPARE_TOP_AUTOFIT, cs->font_ui);
+
+ string_copy_utf8_string(label, localization_get_string(LOCALIZATION_ID_COMPARE_SMOOTH));
+ _viv_compare_draw_checkbox(mem_hdc, &cs->smooth_rect, label, cs->opt_smooth, cs->hovered_top == _VIV_COMPARE_TOP_SMOOTH, cs->font_ui);
+
+ string_copy_utf8_string(label, localization_get_string(LOCALIZATION_ID_COMPARE_FILENAME));
+ _viv_compare_draw_checkbox(mem_hdc, &cs->filename_rect, label, cs->opt_filename, cs->hovered_top == _VIV_COMPARE_TOP_FILENAME, cs->font_ui);
+ }
+
+ // Close Button
+ {
+ wchar_t close_label[STRING_SIZE];
+ string_copy_utf8_string(close_label, localization_get_string(LOCALIZATION_ID_COMPARE_CLOSE));
+ _viv_compare_draw_button(mem_hdc, &cs->close_btn_rect, close_label, cs->hovered_top == _VIV_COMPARE_TOP_CLOSE, cs->font_bold);
+ }
+ }
+
+ // 2. Draw Panes
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ _viv_compare_pane_t *pane = &cs->panes[i];
+ int vw = pane->viewport_rect.right - pane->viewport_rect.left;
+ int vh = pane->viewport_rect.bottom - pane->viewport_rect.top;
+
+ // Toolbar
+ {
+ HBRUSH tb_br = CreateSolidBrush(RGB(236, 238, 242));
+ HPEN tb_pen = CreatePen(PS_SOLID, 1, RGB(195, 198, 205));
+ HGDIOBJ old_br = SelectObject(mem_hdc, tb_br);
+ HGDIOBJ old_pen = SelectObject(mem_hdc, tb_pen);
+
+ FillRect(mem_hdc, &pane->toolbar_rect, tb_br);
+ MoveToEx(mem_hdc, pane->toolbar_rect.left, pane->toolbar_rect.bottom - 1, NULL);
+ LineTo(mem_hdc, pane->toolbar_rect.right, pane->toolbar_rect.bottom - 1);
+
+ SelectObject(mem_hdc, old_pen);
+ SelectObject(mem_hdc, old_br);
+ DeleteObject(tb_pen);
+ DeleteObject(tb_br);
+
+ // Buttons
+ _viv_compare_draw_button(mem_hdc, &pane->btn_prev_rect, L"<", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_PREV), cs->font_bold);
+ _viv_compare_draw_button(mem_hdc, &pane->btn_next_rect, L">", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_NEXT), cs->font_bold);
+ _viv_compare_draw_button(mem_hdc, &pane->btn_zoom_in_rect, L"+", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_ZOOM_IN), cs->font_bold);
+ _viv_compare_draw_button(mem_hdc, &pane->btn_zoom_out_rect, L"-", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_ZOOM_OUT), cs->font_bold);
+
+ // Zoom text
+ {
+ wchar_t ztext[64];
+ string_printf(ztext, "%d%%", (int)(pane->zoom_percent + 0.5));
+ _viv_compare_draw_button(mem_hdc, &pane->zoom_text_rect, ztext, (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_ZOOM_TEXT), cs->font_ui);
+ }
+
+ _viv_compare_draw_button(mem_hdc, &pane->btn_1to1_rect, L"1:1", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_1TO1), cs->font_ui);
+ _viv_compare_draw_button(mem_hdc, &pane->btn_fit_rect, L"Fit", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_FIT), cs->font_ui);
+ _viv_compare_draw_button(mem_hdc, &pane->btn_rotate_rect, L"90°", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_ROTATE), cs->font_ui);
+
+ // Use Current Image checkbox — greyed out if another pane already uses current
+ {
+ int other_uses_current = 0;
+ int k;
+ wchar_t uc_label[STRING_SIZE];
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ if (k != i && cs->panes[k].use_current)
+ {
+ other_uses_current = 1;
+ break;
+ }
+ }
+ string_copy_utf8_string(uc_label, localization_get_string(LOCALIZATION_ID_COMPARE_USE_CURRENT));
+ _viv_compare_draw_checkbox_ex(mem_hdc, &pane->use_current_rect, uc_label,
+ pane->use_current,
+ (!other_uses_current) && (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_USE_CURRENT),
+ other_uses_current, /* disabled */
+ cs->font_ui);
+ }
+
+ _viv_compare_draw_button(mem_hdc, &pane->btn_select_rect, L"...", (cs->hovered_pane == i && cs->hovered_btn == _VIV_COMPARE_BTN_SELECT), cs->font_bold);
+ }
+
+ // Viewport
+ {
+ HBRUSH vp_br = CreateSolidBrush(RGB(75, 75, 80));
+ FillRect(mem_hdc, &pane->viewport_rect, vp_br);
+ DeleteObject(vp_br);
+
+ if (pane->hbitmap && pane->image_wide > 0 && pane->image_high > 0)
+ {
+ int rw, rh, rx, ry;
+ HRGN clip_rgn;
+
+ if (pane->is_autofit && vw > 0 && vh > 0)
+ {
+ double sx = (double)vw / (double)pane->image_wide;
+ double sy = (double)vh / (double)pane->image_high;
+ double sc = sx < sy ? sx : sy;
+ if (sc > 1.0) sc = 1.0;
+ pane->zoom_percent = sc * 100.0;
+ }
+
+ rw = (int)((pane->image_wide * pane->zoom_percent) / 100.0 + 0.5);
+ rh = (int)((pane->image_high * pane->zoom_percent) / 100.0 + 0.5);
+ if (rw < 1) rw = 1;
+ if (rh < 1) rh = 1;
+
+ rx = pane->viewport_rect.left + (vw / 2) - (rw / 2) - pane->view_x;
+ ry = pane->viewport_rect.top + (vh / 2) - (rh / 2) - pane->view_y;
+
+ clip_rgn = CreateRectRgn(pane->viewport_rect.left, pane->viewport_rect.top, pane->viewport_rect.right, pane->viewport_rect.bottom);
+ SelectClipRgn(mem_hdc, clip_rgn);
+
+ {
+ HDC img_dc = CreateCompatibleDC(mem_hdc);
+ int mip_w, mip_h;
+ HBITMAP mip = _viv_get_mipmap(pane->hbitmap, pane->image_wide, pane->image_high, rw, rh, &mip_w, &mip_h, &pane->mipmap);
+ if (mip)
+ {
+ HGDIOBJ old_img = SelectObject(img_dc, mip);
+ int last_mode = 0;
+ if (cs->opt_smooth)
+ {
+ last_mode = SetStretchBltMode(mem_hdc, HALFTONE);
+ SetBrushOrgEx(mem_hdc, -rx, -ry, NULL);
+ }
+ else
+ {
+ last_mode = SetStretchBltMode(mem_hdc, COLORONCOLOR);
+ }
+
+ if (rw == mip_w && rh == mip_h)
+ {
+ BitBlt(mem_hdc, rx, ry, rw, rh, img_dc, 0, 0, SRCCOPY);
+ }
+ else
+ {
+ StretchBlt(mem_hdc, rx, ry, rw, rh, img_dc, 0, 0, mip_w, mip_h, SRCCOPY);
+ }
+
+ SetStretchBltMode(mem_hdc, last_mode);
+ SelectObject(img_dc, old_img);
+ }
+ DeleteDC(img_dc);
+ }
+
+ // Green Text Overlay
+ if (cs->opt_filename && *pane->filename)
+ {
+ const wchar_t *filename_part;
+ wchar_t size_part[64];
+ wchar_t overlay_str[STRING_SIZE * 2];
+ RECT text_rc;
+
+ filename_part = string_get_filename_part(pane->filename);
+ _viv_compare_format_filesize(size_part, pane->filesize);
+ string_printf(overlay_str, "%S , %d x %d , %S", filename_part, pane->image_wide, pane->image_high, size_part);
+
+ text_rc = (RECT){pane->viewport_rect.left + 10, pane->viewport_rect.top + 10, pane->viewport_rect.right - 10, pane->viewport_rect.top + 40};
+
+ HGDIOBJ old_font = SelectObject(mem_hdc, cs->font_overlay);
+ SetBkMode(mem_hdc, TRANSPARENT);
+
+ // Text Shadow
+ SetTextColor(mem_hdc, RGB(0, 0, 0));
+ OffsetRect(&text_rc, 1, 1);
+ DrawTextW(mem_hdc, overlay_str, -1, &text_rc, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
+
+ // Green Text
+ SetTextColor(mem_hdc, RGB(0, 235, 0));
+ OffsetRect(&text_rc, -1, -1);
+ DrawTextW(mem_hdc, overlay_str, -1, &text_rc, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
+
+ SelectObject(mem_hdc, old_font);
+ }
+
+ SelectClipRgn(mem_hdc, NULL);
+ DeleteObject(clip_rgn);
+ }
+ else
+ {
+ wchar_t no_img_str[STRING_SIZE];
+ HGDIOBJ old_font = SelectObject(mem_hdc, cs->font_ui);
+ RECT center_rc = pane->viewport_rect;
+ string_copy_utf8_string(no_img_str, localization_get_string(LOCALIZATION_ID_COMPARE_NO_IMAGE_DROP));
+ SetBkMode(mem_hdc, TRANSPARENT);
+ SetTextColor(mem_hdc, RGB(180, 180, 180));
+ DrawTextW(mem_hdc, no_img_str, -1, ¢er_rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+ SelectObject(mem_hdc, old_font);
+ }
+ }
+
+ // Splitter line between panes
+ if (i < cs->pane_count - 1)
+ {
+ HPEN split_pen = CreatePen(PS_SOLID, 1, RGB(40, 40, 45));
+ HGDIOBJ old_spen = SelectObject(mem_hdc, split_pen);
+ MoveToEx(mem_hdc, pane->pane_rect.right, cs->topbar_rect.bottom, NULL);
+ LineTo(mem_hdc, pane->pane_rect.right, high);
+ SelectObject(mem_hdc, old_spen);
+ DeleteObject(split_pen);
+ }
+ }
+
+ BitBlt(hdc, 0, 0, wide, high, mem_hdc, 0, 0, SRCCOPY);
+
+ SelectObject(mem_hdc, old_mem_bitmap);
+ DeleteObject(mem_hbitmap);
+ DeleteDC(mem_hdc);
+
+ EndPaint(hwnd, &ps);
+}
+
+static LRESULT CALLBACK _viv_compare_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ _viv_compare_state_t *cs = _viv_compare_state;
+
+ switch (msg)
+ {
+ case WM_CREATE:
+ DragAcceptFiles(hwnd, TRUE);
+ return 0;
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_PAINT:
+ if (cs)
+ {
+ _viv_compare_paint(hwnd, cs);
+ }
+ return 0;
+
+ case WM_DROPFILES:
+ if (cs)
+ {
+ HDROP hDrop = (HDROP)wParam;
+ UINT count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
+ POINT pt;
+ int target_pane = 0;
+
+ if (DragQueryPoint(hDrop, &pt))
+ {
+ int i;
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ if (PtInRect(&cs->panes[i].pane_rect, pt))
+ {
+ target_pane = i;
+ break;
+ }
+ }
+ }
+
+ if (count >= 2)
+ {
+ wchar_t filename0[STRING_SIZE];
+ wchar_t filename1[STRING_SIZE];
+
+ DragQueryFileW(hDrop, 0, filename0, STRING_SIZE);
+ DragQueryFileW(hDrop, 1, filename1, STRING_SIZE);
+
+ cs->panes[0].use_current = 0;
+ cs->panes[0].playlist_item = NULL;
+ _viv_compare_load_image(filename0, &cs->panes[0]);
+
+ cs->panes[1].use_current = 0;
+ cs->panes[1].playlist_item = NULL;
+ _viv_compare_load_image(filename1, &cs->panes[1]);
+
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ else if (count == 1)
+ {
+ wchar_t filename[STRING_SIZE];
+ DragQueryFileW(hDrop, 0, filename, STRING_SIZE);
+
+ if (target_pane >= 0 && target_pane < cs->pane_count)
+ {
+ int other_pane = (target_pane == 0) ? 1 : 0;
+
+ // Reset use_current on target pane
+ cs->panes[target_pane].use_current = 0;
+ cs->panes[target_pane].playlist_item = NULL;
+
+ // If the other pane was using current image and this dropped file is that same file,
+ // reset use_current on the other pane so they don't conflict
+ if (other_pane >= 0 && other_pane < cs->pane_count)
+ {
+ if (cs->panes[other_pane].use_current && *_viv_current_fd->cFileName)
+ {
+ if (lstrcmpiW(filename, _viv_current_fd->cFileName) == 0)
+ {
+ cs->panes[other_pane].use_current = 0;
+ }
+ }
+ }
+
+ _viv_compare_load_image(filename, &cs->panes[target_pane]);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ }
+
+ DragFinish(hDrop);
+ }
+ return 0;
+
+ case WM_SIZE:
+ if (cs)
+ {
+ int wide = LOWORD(lParam);
+ int high = HIWORD(lParam);
+ _viv_compare_update_layout(cs, wide, high);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (cs)
+ {
+ int x = GET_X_LPARAM(lParam);
+ int y = GET_Y_LPARAM(lParam);
+
+ if (cs->is_dragging)
+ {
+ int dx = x - cs->drag_last_x;
+ int dy = y - cs->drag_last_y;
+ cs->drag_last_x = x;
+ cs->drag_last_y = y;
+
+ if (cs->drag_is_individual && cs->drag_pane_index >= 0 && cs->drag_pane_index < cs->pane_count)
+ {
+ cs->panes[cs->drag_pane_index].view_x -= dx;
+ cs->panes[cs->drag_pane_index].view_y -= dy;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ cs->panes[i].view_x -= dx;
+ cs->panes[i].view_y -= dy;
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ else
+ {
+ int pane_idx, btn_idx;
+ int top_res = _viv_compare_hit_test(cs, x, y, &pane_idx, &btn_idx);
+
+ if (top_res != cs->hovered_top || pane_idx != cs->hovered_pane || btn_idx != cs->hovered_btn)
+ {
+ cs->hovered_top = top_res;
+ cs->hovered_pane = pane_idx;
+ cs->hovered_btn = btn_idx;
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ if (cs)
+ {
+ int x = GET_X_LPARAM(lParam);
+ int y = GET_Y_LPARAM(lParam);
+ int pane_idx, btn_idx;
+ int top_res = _viv_compare_hit_test(cs, x, y, &pane_idx, &btn_idx);
+
+ if (top_res == _VIV_COMPARE_TOP_CLOSE)
+ {
+ DestroyWindow(hwnd);
+ return 0;
+ }
+ else if (top_res == _VIV_COMPARE_TOP_AUTOFIT)
+ {
+ int i;
+ cs->opt_autofit = !cs->opt_autofit;
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ cs->panes[i].is_autofit = cs->opt_autofit;
+ cs->panes[i].view_x = 0;
+ cs->panes[i].view_y = 0;
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (top_res == _VIV_COMPARE_TOP_SMOOTH)
+ {
+ cs->opt_smooth = !cs->opt_smooth;
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (top_res == _VIV_COMPARE_TOP_FILENAME)
+ {
+ cs->opt_filename = !cs->opt_filename;
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+
+ if (pane_idx >= 0 && pane_idx < cs->pane_count)
+ {
+ _viv_compare_pane_t *p = &cs->panes[pane_idx];
+ if (btn_idx == _VIV_COMPARE_BTN_PREV)
+ {
+ if (p->playlist_item && p->playlist_item->prev)
+ {
+ p->playlist_item = p->playlist_item->prev;
+ _viv_compare_load_image(p->playlist_item->fd.cFileName, p);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_NEXT)
+ {
+ if (p->playlist_item && p->playlist_item->next)
+ {
+ p->playlist_item = p->playlist_item->next;
+ _viv_compare_load_image(p->playlist_item->fd.cFileName, p);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_ZOOM_IN)
+ {
+ int is_indiv = (GetKeyState(VK_CONTROL) < 0);
+ if (is_indiv)
+ {
+ int cx = (p->viewport_rect.left + p->viewport_rect.right) / 2;
+ int cy = (p->viewport_rect.top + p->viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(p, 1, cx, cy);
+ }
+ else
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ int cx = (cs->panes[k].viewport_rect.left + cs->panes[k].viewport_rect.right) / 2;
+ int cy = (cs->panes[k].viewport_rect.top + cs->panes[k].viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(&cs->panes[k], 1, cx, cy);
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_ZOOM_OUT)
+ {
+ int is_indiv = (GetKeyState(VK_CONTROL) < 0);
+ if (is_indiv)
+ {
+ int cx = (p->viewport_rect.left + p->viewport_rect.right) / 2;
+ int cy = (p->viewport_rect.top + p->viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(p, 0, cx, cy);
+ }
+ else
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ int cx = (cs->panes[k].viewport_rect.left + cs->panes[k].viewport_rect.right) / 2;
+ int cy = (cs->panes[k].viewport_rect.top + cs->panes[k].viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(&cs->panes[k], 0, cx, cy);
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_1TO1)
+ {
+ int is_indiv = (GetKeyState(VK_CONTROL) < 0);
+ if (is_indiv)
+ {
+ p->zoom_percent = 100.0;
+ p->is_1to1 = 1;
+ p->is_autofit = 0;
+ p->view_x = 0;
+ p->view_y = 0;
+ }
+ else
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ cs->panes[k].zoom_percent = 100.0;
+ cs->panes[k].is_1to1 = 1;
+ cs->panes[k].is_autofit = 0;
+ cs->panes[k].view_x = 0;
+ cs->panes[k].view_y = 0;
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_FIT)
+ {
+ int is_indiv = (GetKeyState(VK_CONTROL) < 0);
+ if (is_indiv)
+ {
+ p->is_autofit = 1;
+ p->view_x = 0;
+ p->view_y = 0;
+ }
+ else
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ cs->panes[k].is_autofit = 1;
+ cs->panes[k].view_x = 0;
+ cs->panes[k].view_y = 0;
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_ROTATE)
+ {
+ if (p->hbitmap)
+ {
+ HBITMAP rot = _viv_orientate_hbitmap(p->hbitmap, 6); // 90 deg cw
+ if (rot)
+ {
+ int tmp;
+ DeleteObject(p->hbitmap);
+ p->hbitmap = rot;
+ if (p->mipmap)
+ {
+ _viv_mipmap_free(p->mipmap);
+ p->mipmap = NULL;
+ }
+ tmp = p->image_wide;
+ p->image_wide = p->image_high;
+ p->image_high = tmp;
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ }
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_USE_CURRENT)
+ {
+ // Only allow if no other pane already uses current
+ int other_uses = 0;
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ if (k != pane_idx && cs->panes[k].use_current)
+ {
+ other_uses = 1;
+ break;
+ }
+ }
+ if (!other_uses)
+ {
+ p->use_current = !p->use_current;
+ if (p->use_current && *_viv_current_fd->cFileName)
+ {
+ _viv_playlist_t *cur_item = _viv_playlist_from_fd(_viv_current_fd);
+ p->playlist_item = cur_item;
+ _viv_compare_load_image(_viv_current_fd->cFileName, p);
+ }
+ else if (!p->use_current)
+ {
+ _viv_compare_free_pane_image(p);
+ p->playlist_item = NULL;
+ p->filename[0] = 0;
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+ }
+ else if (btn_idx == _VIV_COMPARE_BTN_SELECT)
+ {
+ _viv_compare_pick_image_for_pane(hwnd, p);
+ return 0;
+ }
+
+ // Click inside viewport: Start Dragging
+ if (PtInRect(&p->viewport_rect, (POINT){x, y}))
+ {
+ cs->is_dragging = 1;
+ cs->drag_last_x = x;
+ cs->drag_last_y = y;
+ cs->drag_pane_index = pane_idx;
+ cs->drag_is_individual = (GetKeyState(VK_CONTROL) < 0);
+ SetCapture(hwnd);
+ }
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ if (cs && cs->is_dragging)
+ {
+ cs->is_dragging = 0;
+ ReleaseCapture();
+ }
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ if (cs)
+ {
+ int x = GET_X_LPARAM(lParam);
+ int y = GET_Y_LPARAM(lParam);
+ int pane_idx, btn_idx;
+ _viv_compare_hit_test(cs, x, y, &pane_idx, &btn_idx);
+
+ if (pane_idx >= 0 && pane_idx < cs->pane_count)
+ {
+ if (PtInRect(&cs->panes[pane_idx].viewport_rect, (POINT){x, y}))
+ {
+ cs->is_dragging = 1;
+ cs->drag_last_x = x;
+ cs->drag_last_y = y;
+ cs->drag_pane_index = pane_idx;
+ cs->drag_is_individual = 1; // Right-drag is always individual
+ SetCapture(hwnd);
+ }
+ }
+ }
+ return 0;
+
+ case WM_MOUSEWHEEL:
+ if (cs)
+ {
+ POINT pt;
+ int delta = GET_WHEEL_DELTA_WPARAM(wParam);
+ int zoom_in = (delta > 0);
+ int is_indiv;
+ int pane_idx, btn_idx;
+
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ ScreenToClient(hwnd, &pt);
+
+ _viv_compare_hit_test(cs, pt.x, pt.y, &pane_idx, &btn_idx);
+ is_indiv = (GetKeyState(VK_CONTROL) < 0) || (GetKeyState(VK_RBUTTON) < 0);
+
+ if (is_indiv && pane_idx >= 0 && pane_idx < cs->pane_count)
+ {
+ _viv_compare_zoom_pane(&cs->panes[pane_idx], zoom_in, pt.x, pt.y);
+ }
+ else
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ int cx = (cs->panes[k].viewport_rect.left + cs->panes[k].viewport_rect.right) / 2;
+ int cy = (cs->panes[k].viewport_rect.top + cs->panes[k].viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(&cs->panes[k], zoom_in, cx, cy);
+ }
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+
+ case WM_KEYDOWN:
+ if (cs)
+ {
+ switch (wParam)
+ {
+ case VK_ESCAPE:
+ DestroyWindow(hwnd);
+ return 0;
+
+ case VK_LEFT:
+ case VK_PRIOR:
+ {
+ int idx = (cs->hovered_pane >= 0 && cs->hovered_pane < cs->pane_count) ? cs->hovered_pane : 0;
+ _viv_compare_pane_t *p = &cs->panes[idx];
+ if (p->playlist_item && p->playlist_item->prev)
+ {
+ p->playlist_item = p->playlist_item->prev;
+ _viv_compare_load_image(p->playlist_item->fd.cFileName, p);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+ }
+
+ case VK_RIGHT:
+ case VK_NEXT:
+ case VK_SPACE:
+ {
+ int idx = (cs->hovered_pane >= 0 && cs->hovered_pane < cs->pane_count) ? cs->hovered_pane : 0;
+ _viv_compare_pane_t *p = &cs->panes[idx];
+ if (p->playlist_item && p->playlist_item->next)
+ {
+ p->playlist_item = p->playlist_item->next;
+ _viv_compare_load_image(p->playlist_item->fd.cFileName, p);
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ return 0;
+ }
+
+ case VK_ADD:
+ case VK_OEM_PLUS:
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ int cx = (cs->panes[k].viewport_rect.left + cs->panes[k].viewport_rect.right) / 2;
+ int cy = (cs->panes[k].viewport_rect.top + cs->panes[k].viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(&cs->panes[k], 1, cx, cy);
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+
+ case VK_SUBTRACT:
+ case VK_OEM_MINUS:
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ int cx = (cs->panes[k].viewport_rect.left + cs->panes[k].viewport_rect.right) / 2;
+ int cy = (cs->panes[k].viewport_rect.top + cs->panes[k].viewport_rect.bottom) / 2;
+ _viv_compare_zoom_pane(&cs->panes[k], 0, cx, cy);
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+
+ case '1':
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ cs->panes[k].zoom_percent = 100.0;
+ cs->panes[k].is_1to1 = 1;
+ cs->panes[k].is_autofit = 0;
+ cs->panes[k].view_x = 0;
+ cs->panes[k].view_y = 0;
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+
+ case 'F':
+ {
+ int k;
+ for (k = 0; k < cs->pane_count; k++)
+ {
+ cs->panes[k].is_autofit = 1;
+ cs->panes[k].view_x = 0;
+ cs->panes[k].view_y = 0;
+ }
+ InvalidateRect(hwnd, NULL, FALSE);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ if (cs)
+ {
+ int i;
+ for (i = 0; i < cs->pane_count; i++)
+ {
+ _viv_compare_free_pane_image(&cs->panes[i]);
+ }
+ if (cs->font_ui) DeleteObject(cs->font_ui);
+ if (cs->font_bold) DeleteObject(cs->font_bold);
+ if (cs->font_overlay) DeleteObject(cs->font_overlay);
+
+ mem_free(cs);
+ _viv_compare_state = NULL;
+ }
+ EnableWindow(_viv_hwnd, TRUE);
+ SetForegroundWindow(_viv_hwnd);
+ return 0;
+ }
+
+ return DefWindowProcW(hwnd, msg, wParam, lParam);
+}
+
+static void _viv_compare_show(void)
+{
+ _viv_compare_state_t *cs;
+ HWND hwnd;
+ RECT rc;
+ static int class_registered = 0;
+
+ if (_viv_compare_state && _viv_compare_state->hwnd)
+ {
+ SetForegroundWindow(_viv_compare_state->hwnd);
+ return;
+ }
+
+ if (!class_registered)
+ {
+ os_RegisterClassEx(
+ CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW,
+ _viv_compare_proc,
+ (HICON)LoadImage(os_hinstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0),
+ LoadCursor(NULL, IDC_ARROW),
+ (HBRUSH)(COLOR_BTNFACE + 1),
+ "_VIV_COMPARE",
+ (HICON)LoadImage(os_hinstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
+ class_registered = 1;
+ }
+
+ cs = (_viv_compare_state_t *)mem_alloc(sizeof(_viv_compare_state_t));
+ ZeroMemory(cs, sizeof(_viv_compare_state_t));
+ _viv_compare_state = cs;
+
+ cs->pane_count = _VIV_COMPARE_PANE_COUNT;
+ cs->opt_autofit = 1;
+ cs->opt_smooth = (config_shrink_blit_mode == CONFIG_SHRINK_BLIT_MODE_HALFTONE);
+ cs->opt_filename = 1;
+
+ cs->font_ui = CreateFontW(-12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI");
+ if (!cs->font_ui) cs->font_ui = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+
+ cs->font_bold = CreateFontW(-12, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI");
+ if (!cs->font_bold) cs->font_bold = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+
+ cs->font_overlay = CreateFontW(-14, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Segoe UI");
+ if (!cs->font_overlay) cs->font_overlay = cs->font_bold;
+
+ // Start with empty panes - user will select images manually via browse button
+
+ GetWindowRect(_viv_hwnd, &rc);
+
+ hwnd = os_CreateWindowEx(
+ WS_EX_APPWINDOW | WS_EX_ACCEPTFILES,
+ "_VIV_COMPARE",
+ localization_get_string(LOCALIZATION_ID_COMPARE_IMAGES),
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
+ rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
+ _viv_hwnd, 0, os_hinstance, NULL);
+
+ cs->hwnd = hwnd;
+
+ if (hwnd)
+ {
+ DragAcceptFiles(hwnd, TRUE);
+ ShowWindow(hwnd, SW_MAXIMIZE);
+ EnableWindow(_viv_hwnd, FALSE);
+ }
+}
+
+static void _viv_nav_item_free_all(void)
+{
+ if (_viv_nav_items)
+ {
+ int i;
+
+ for(i=0;i<_viv_nav_item_count;i++)
+ {
+ mem_free(_viv_nav_items[i]);
+ }
+
+ mem_free(_viv_nav_items);
+
+ __viv_nav_item_start = 0;
+ _viv_nav_item_last = 0;
+ _viv_nav_items = 0;
+ _viv_nav_item_count = 0;
+ }
+}
+
+static void _viv_nav_item_add(WIN32_FIND_DATA *fd)
+{
+ _viv_nav_item_t *navitem;
+
+ navitem = mem_alloc(sizeof(_viv_nav_item_t));
+
+ os_copy_memory(&navitem->fd,fd,sizeof(WIN32_FIND_DATA));
+
+ if (__viv_nav_item_start)
+ {
+ _viv_nav_item_last->next = navitem;
+ }
+ else
+ {
+ __viv_nav_item_start = navitem;
+ }
+
+ navitem->next = 0;
+ _viv_nav_item_last = navitem;
+ _viv_nav_item_count++;
+}
+
+static int _viv_nav_compare(const _viv_nav_item_t *a,const _viv_nav_item_t *b)
+{
+ return _viv_fd_compare_name(&a->fd,&b->fd);
+}
+
+static INT_PTR CALLBACK _viv_search_everything_proc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+
+ os_center_dialog(hwnd);
+
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam);
+
+ os_SetWindowText_localization_id(hwnd,lParam ? LOCALIZATION_ID_EVERYTHING_ADD_EVERYTHING_SEARCH_CAPTION : LOCALIZATION_ID_EVERYTHING_LOAD_EVERYTHING_SEARCH_CAPTION);
+ os_SetDlgItemText_localization_id(hwnd,IDC_SEARCH_EVERYTHING_RANDOM_CHECKBOX,LOCALIZATION_ID_RANDOMIZE);
+
+ return TRUE;
case WM_COMMAND:
@@ -13668,6 +15648,22 @@ static void _viv_update_1to1_scroll(int x,int y)
// #define PHOTO_ORIENTATION_ROTATE270 6u
// #define PHOTO_ORIENTATION_TRANSVERSE 7u
// #define PHOTO_ORIENTATION_ROTATE90 8u
+static HBITMAP _viv_create_bitmap(HDC hdc, int width, int height)
+{
+ BITMAPINFO bmi;
+ void* bits;
+
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = width;
+ bmi.bmiHeader.biHeight = -height;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ return CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+}
+
static HBITMAP _viv_orientate_hbitmap(HBITMAP hbitmap,int orientation)
{
BITMAP bitmap;
@@ -13706,7 +15702,7 @@ static HBITMAP _viv_orientate_hbitmap(HBITMAP hbitmap,int orientation)
break;
}
- ret_hbitmap = CreateCompatibleBitmap(screen_hdc,ret_wide,ret_high);
+ ret_hbitmap = _viv_create_bitmap(screen_hdc,ret_wide,ret_high);
if (ret_hbitmap)
{
int ret;
@@ -14008,70 +16004,42 @@ static void _viv_do_mousewheel_action(int action,int delta,int x,int y)
if (_viv_1to1)
{
_viv_1to1 = 0;
-
- if (delta > 0)
+ }
+
+ {
+ double current_percent;
+ double new_percent;
+
+ if (_viv_image_wide > 0)
{
- for(_viv_zoom_pos = 0;_viv_zoom_pos<_VIV_ZOOM_MAX;_viv_zoom_pos++)
- {
- _viv_get_render_size(&rw,&rh);
-
- if (rw > old_rw)
- {
- break;
- }
- }
+ current_percent = ((double)old_rw / (double)_viv_image_wide) * 100.0;
}
else
{
- for(_viv_zoom_pos = _VIV_ZOOM_MAX-1;_viv_zoom_pos>=0;_viv_zoom_pos--)
- {
- _viv_get_render_size(&rw,&rh);
-
- if (rw < old_rw)
- {
- break;
- }
- }
+ current_percent = 100.0;
}
- }
- else
- {
+
if (delta > 0)
{
- _viv_zoom_pos++;
+ new_percent = _viv_calculate_next_zoom(current_percent, 1);
}
- else
+ else if (delta < 0)
{
- _viv_zoom_pos--;
+ new_percent = _viv_calculate_next_zoom(current_percent, 0);
}
- }
-
- if (_viv_zoom_pos < 0) _viv_zoom_pos = 0;
- if (_viv_zoom_pos > _VIV_ZOOM_MAX-1) _viv_zoom_pos = _VIV_ZOOM_MAX-1;
-
- if (_viv_zoom_pos != old_zoom_pos)
- {
- _viv_get_render_size(&rw,&rh);
- /*
- if (_viv_zoom_pos == 1)
+ else
{
- if ((rw < _viv_image_wide) || (rw < _viv_image_wide))
- {
- rw = _viv_image_wide;
- rh = _viv_image_high;
- }
+ new_percent = current_percent;
}
- */
- //
- // new_cursor_x =
- // rx = (wide / 2) - (rw / 2)
- //debug_printf("%d %d\n",(wide / 2) - (rw / 2),(high / 2) - (rh / 2));
- //debug_printf("%d %d\n",old_cursor_px,(old_cursor_px * 100) / old_rw);
-
- //debug_printf("old %d %d new %d %d\n",old_cursor_px,old_cursor_py,(wide / 2) - (rw / 2) - cursor_x + ((old_cursor_px * rw) / old_rw),(high / 2) - (rh / 2) - cursor_y + ((old_cursor_py * rh) / old_rh)) ;
- //debug_printf("wide / 2 = %d, rw/2=%d, old_cursor_px * rw=%d\n",wide/2,rw/2,old_cursor_px * rw) ;
+ _viv_is_custom_zoom = 1;
+ _viv_custom_zoom_percent = new_percent;
+ }
+
+ _viv_get_render_size(&rw,&rh);
+ if ((rw != old_rw) || (rh != old_rh))
+ {
if (old_rw)
{
new_cursor_x = ((__int64)old_cursor_px * (__int64)rw) / (__int64)old_rw;
@@ -14093,6 +16061,7 @@ static void _viv_do_mousewheel_action(int action,int delta,int x,int y)
_viv_view_set((wide / 2) - (rw / 2) - cursor_x + new_cursor_x,(high / 2) - (rh / 2) - cursor_y + new_cursor_y,1);
InvalidateRect(_viv_hwnd,0,FALSE);
+ _viv_toolbar_update_buttons();
}
}
else
@@ -14223,7 +16192,7 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int
if (mem2_hdc)
{
(*pmip)->mipmap = NULL;
- (*pmip)->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high);
+ (*pmip)->hbitmap = _viv_create_bitmap(screen_hdc,mip_wide,mip_high);
last_hbitmap = SelectObject(mem_hdc,(*pmip)->hbitmap);
last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap);
@@ -14883,7 +16852,7 @@ static void _viv_stretch_blt(HDC dst_hdc,int dst_x,int dst_y,int dst_wide,int ds
{
HBITMAP dst_hbitmap;
- dst_hbitmap = CreateCompatibleBitmap(dst_hdc,clip_wide,clip_high);
+ dst_hbitmap = _viv_create_bitmap(dst_hdc,clip_wide,clip_high);
if (dst_hbitmap)
{
HGDIOBJ last_dst_hbitmap;
@@ -15321,3 +17290,4 @@ static int _viv_safe_copy_data(const void *base,SIZE_T src_size,const void *src,
return 1;
}
+
diff --git a/src/viv.h b/src/viv.h
index 16daba6a..89801043 100644
--- a/src/viv.h
+++ b/src/viv.h
@@ -57,9 +57,25 @@ typedef unsigned __int64 VIV_UINT64;
#include
#include
//#include
+#define IDC_STATIC -1
+#define IDC_ASSOCIATIONS_GROUPBOX IDC_STATIC
+#define IDC_ADD_KEY_BUTTON IDC_ADD_KEY
+#define IDC_EDIT_KEY_BUTTON IDC_EDIT_KEY
+#define IDC_REMOVE_KEY_BUTTON IDC_REMOVE_KEY
+#define IDC_LEFTCLICKACTION_COMBOBOX IDC_LEFTCLICKACTION
+#define IDC_RIGHTCLICKACTION_COMBOBOX IDC_RIGHTCLICKACTION
+#define IDC_MOUSEWHEELACTION_COMBOBOX IDC_MOUSEWHEELACTION
+#define IDC_SHRINK_BLIT_MODE_COMBOBOX IDC_COMBO1
+#define IDC_MAGNIFY_BLIT_MODE_COMBOBOX IDC_COMBO2
+#define IDC_TITLE_BAR_FORMAT_COMBOBOX IDC_TITLE_BAR_FORMAT
+#define IDC_AUTO_SIZE_WINDOW_COMBOBOX IDC_COMBO4
+#define IDC_WINDOWEDBACKGROUNDCOLOR_BUTTON IDC_WINDOWEDBACKGROUNDCOLOR
+#define IDC_FULLSCREENBACKGROUNDCOLOR_BUTTON IDC_FULLSCREENBACKGROUNDCOLOR
+#define IDC_CUSTOM_RATE_TYPE_COMBOBOX IDC_CUSTOM_RATE_TYPE_COMBO
+#define IDC_SEARCH_EVERYTHING_RANDOM_CHECKBOX IDC_SEARCH_EVERYTHING_RANDOM
#include "../res/resource.h"
//#include
-//#include
+#include
//#include
//#include
#include // OPENFILENAME
@@ -93,10 +109,12 @@ enum
VIV_ID_VIEW_ALLOW_SHRINKING,
VIV_ID_VIEW_KEEP_ASPECT_RATIO,
VIV_ID_VIEW_FILL_WINDOW,
+ VIV_ID_VIEW_SMOOTH,
VIV_ID_VIEW_1TO1,
VIV_ID_VIEW_BESTFIT,
VIV_ID_VIEW_FULLSCREEN,
VIV_ID_VIEW_SLIDESHOW,
+ VIV_ID_VIEW_COMPARE_IMAGES,
VIV_ID_VIEW_ONTOP_ALWAYS,
VIV_ID_VIEW_ONTOP_WHILE_PLAYING_OR_ANIMATING,
VIV_ID_VIEW_ONTOP_NEVER,
@@ -200,6 +218,7 @@ enum
VIV_ID_OPTIONS_GENERAL,
VIV_ID_OPTIONS_VIEW,
VIV_ID_OPTIONS_CONTROLS,
+ VIV_ID_OPTIONS_ZOOM,
VIV_ID_FILE_OPEN_FILE,
VIV_ID_FILE_OPEN_FOLDER,
diff --git a/test_math.c b/test_math.c
new file mode 100644
index 00000000..441c4d4d
--- /dev/null
+++ b/test_math.c
@@ -0,0 +1,6 @@
+#include
+#include
+int main() {
+ printf("%f\n", pow(2.0, 3.0));
+ return 0;
+}
diff --git a/vs2026/voidImageViewer.vcxproj b/vs2026/voidImageViewer.vcxproj
index a5f40a8e..8d6323f7 100644
--- a/vs2026/voidImageViewer.vcxproj
+++ b/vs2026/voidImageViewer.vcxproj
@@ -465,7 +465,7 @@
/MP %(AdditionalOptions)
Disabled
..\libwebp;%(AdditionalIncludeDirectories)
- BZ_NO_STDIO;VERSION_DEBUG;BZ_NO_STDIO;%(PreprocessorDefinitions)
+ WIN32;_DEBUG;_WINDOWS;BZ_NO_STDIO;VERSION_DEBUG;VERSION_X86;%(PreprocessorDefinitions)
false
Async
MultiThreadedDebug
@@ -606,7 +606,7 @@
false
true
..\libwebp;%(AdditionalIncludeDirectories)
- BZ_NO_STDIO;BZ_NO_STDIO;%(PreprocessorDefinitions)
+ WIN32;NDEBUG;_WINDOWS;BZ_NO_STDIO;VERSION_X86;%(PreprocessorDefinitions)
true
MultiThreaded
Fast