From f4e6f06b8e1d61a90d618bc23ccd2a95429f644e Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 5 Jul 2026 01:22:48 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E5=B9=B3=E6=BB=91=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JarkViewer/src/main.cpp | 69 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 3fd044f..30ddf69 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -1900,30 +1900,26 @@ class JarkViewerApp : public D3D11App { } break; case ActionENUM::zoomIn: { - if (curPar.zoomIndex < curPar.zoomList.size() - 1) { - curPar.zoomIndex++; - - auto zoomNext = curPar.zoomList[curPar.zoomIndex]; - if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { - computeZoomSlide(zoomNext); - } + // 无极缩放,步长 30% + constexpr double zoomFactor = 1.3; + int64_t zoomNext = (int64_t)(curPar.zoomTarget * zoomFactor); + if (zoomNext <= curPar.zoomTarget) zoomNext = curPar.zoomTarget + 1; + if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { + computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; smoothShift = true; } } break; case ActionENUM::zoomOut: { + // 无极缩放,步长 30% + constexpr double zoomFactor = 1.3; // 不宜缩太小 - if (curPar.zoomTarget <= curPar.ZOOM_BASE && (curPar.zoomTarget * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) + int64_t zoomNext = std::max((int64_t)(curPar.zoomTarget / zoomFactor), 1); + if (zoomNext <= curPar.ZOOM_BASE && (zoomNext * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) break; - - if (curPar.zoomIndex > 0) { - curPar.zoomIndex--; - - auto zoomNext = curPar.zoomList[curPar.zoomIndex]; - if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { - computeZoomSlide(zoomNext); - } + if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { + computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; smoothShift = true; } @@ -2019,36 +2015,37 @@ class JarkViewerApp : public D3D11App { } if (curPar.zoomCur != curPar.zoomTarget || curPar.slideCur != curPar.slideTarget) { - if (GlobalVar::settingParameter.isAllowZoomAnimation && smoothShift) { // 简单缩放动画 - const int progressMax = 1 << 8; - static int progressCnt = progressMax; + if (GlobalVar::settingParameter.isAllowZoomAnimation && smoothShift) { + // 250ms 固定时长动画 + using namespace std::chrono; + static constexpr auto animDuration = milliseconds(250); + static steady_clock::time_point animStartTime; static int64_t zoomInit = 0; static int64_t zoomTargetInit = 0; static Cood slideInit{}, slideTargetInit{}; - //未开始进行动画 或 动画未完成就有新缩放操作 - if (progressCnt >= progressMax || zoomTargetInit != curPar.zoomTarget || slideTargetInit != curPar.slideTarget) { - progressCnt = 1; + // 动画重新开始(新操作打断旧动画) + if (zoomTargetInit != curPar.zoomTarget || slideTargetInit != curPar.slideTarget) { + animStartTime = steady_clock::now(); zoomInit = curPar.zoomCur; zoomTargetInit = curPar.zoomTarget; slideInit = curPar.slideCur; slideTargetInit = curPar.slideTarget; } + + auto elapsed = duration_cast(steady_clock::now() - animStartTime); + if (elapsed >= animDuration) { + curPar.zoomCur = curPar.zoomTarget; + curPar.slideCur = curPar.slideTarget; + smoothShift = false; + } else { - auto addDelta = ((progressMax - progressCnt) / 4); - if (addDelta <= 1) { - progressCnt = progressMax; - curPar.zoomCur = curPar.zoomTarget; - curPar.slideCur = curPar.slideTarget; - smoothShift = false; - } - else { - progressCnt += addDelta; - curPar.zoomCur = zoomInit + (curPar.zoomTarget - zoomInit) * progressCnt / progressMax; - const double t = (double)progressCnt / progressMax; - curPar.slideCur.x = (int)std::round(slideInit.x + (curPar.slideTarget.x - slideInit.x) * t); - curPar.slideCur.y = (int)std::round(slideInit.y + (curPar.slideTarget.y - slideInit.y) * t); - } + // 归一化时间 [0, 1],三次方缓出 (cubic ease-out) + double t = (double)elapsed.count() / (double)animDuration.count(); + double easedT = 1.0 - (1.0 - t) * (1.0 - t) * (1.0 - t); + curPar.zoomCur = zoomInit + (int64_t)((curPar.zoomTarget - zoomInit) * easedT); + curPar.slideCur.x = (int)std::round(slideInit.x + (curPar.slideTarget.x - slideInit.x) * easedT); + curPar.slideCur.y = (int)std::round(slideInit.y + (curPar.slideTarget.y - slideInit.y) * easedT); } } else { From ebd858ffb8cdd708b9cb13a6e2fa2f63abff59bc Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 5 Jul 2026 01:30:26 +0800 Subject: [PATCH 02/16] Change line color based on dark mode setting --- JarkViewer/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 30ddf69..7cf4433 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -1112,7 +1112,7 @@ class JarkViewerApp : public D3D11App { // 内置的用于提示的图像 } else { // 普通图像 画边框 - const uint32_t lineColor = 0xFF808080; + const uint32_t lineColor = GlobalVar::isCurrentUIDarkMode ? 0xFF333333 : 0xFF000000;//我更喜欢这个颜色 if (0 < xStart and xStart < canvasW) { const int yMax = std::min(yEnd + 1, canvasH); for (int y = std::max(yStart - 1, 0); y < yMax; y++) { From c0b4a4201577dcc9317f98c1c4b539184951c63d Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:14:58 +0800 Subject: [PATCH 03/16] Add full-screen mask for smoother window transitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全屏时加个遮罩挡住窗口框体降级显示 --- JarkViewer/src/jarkUtils.cpp | 94 +++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/JarkViewer/src/jarkUtils.cpp b/JarkViewer/src/jarkUtils.cpp index b8de581..0fa5404 100644 --- a/JarkViewer/src/jarkUtils.cpp +++ b/JarkViewer/src/jarkUtils.cpp @@ -445,6 +445,53 @@ void jarkUtils::copyImageToClipboard(const cv::Mat& image) { } +// 全屏切换遮挡窗口的窗口过程:纯色填充(颜色匹配当前主题),不响应任何交互。 +// 作用:在主窗口切换样式/位置时盖住整个屏幕,让用户看不到 DWM 的过渡帧 +// (DWM 在 SWP_FRAMECHANGED 后会异步重算非客户区,期间用 Aero 兼容模板作为回退帧, +// 这就是标题栏/最小化最大化关闭按钮短暂降级成 aero 样式的根因)。 +static LRESULT CALLBACK JarkFullScreenMaskWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { + switch (msg) { + case WM_ERASEBKGND: { + HDC dc = (HDC)wp; + RECT rc; GetClientRect(hwnd, &rc); + // currentTheme.BG 是 0xAARRGGBB 形式(小端 BGRA as uint32) + uint32_t bg = GlobalVar::currentTheme.BG; + HBRUSH brush = CreateSolidBrush(RGB((bg >> 16) & 0xFF, (bg >> 8) & 0xFF, bg & 0xFF)); + FillRect(dc, &rc, brush); + DeleteObject(brush); + return 1; + } + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + RECT rc; GetClientRect(hwnd, &rc); + uint32_t bg = GlobalVar::currentTheme.BG; + HBRUSH brush = CreateSolidBrush(RGB((bg >> 16) & 0xFF, (bg >> 8) & 0xFF, bg & 0xFF)); + FillRect(ps.hdc, &rc, brush); + DeleteObject(brush); + EndPaint(hwnd, &ps); + return 0; + } + case WM_SETCURSOR: + // 遮挡期间隐藏鼠标,避免在纯色块上出现突兀的光标 + SetCursor(nullptr); + return TRUE; + } + return DefWindowProcW(hwnd, msg, wp, lp); +} + +static void JarkEnsureFullScreenMaskClassRegistered() { + static bool registered = false; + if (registered) return; + WNDCLASSEXW wc = { sizeof(wc) }; + wc.lpfnWndProc = JarkFullScreenMaskWndProc; + wc.hInstance = GetModuleHandleW(nullptr); + wc.lpszClassName = L"JarkFullScreenMaskWnd"; + wc.hCursor = nullptr; + RegisterClassExW(&wc); + registered = true; +} + void jarkUtils::ToggleFullScreen(HWND hwnd) { static RECT preRect{}; static DWORD preStyle = 0; @@ -452,8 +499,31 @@ void jarkUtils::ToggleFullScreen(HWND hwnd) { static bool isFullScreen = false; + // 取主窗口所在显示器,遮挡窗口只盖这一个屏幕即可 + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO monitorInfo = { sizeof(monitorInfo) }; + GetMonitorInfo(monitor, &monitorInfo); + + // 先创建并显示全屏遮挡窗口,盖住整个屏幕。 + // 关键:遮挡窗口本身是 WS_POPUP(无标题栏),DWM 不会为它画 Aero 过渡帧; + // 它盖在主窗口之上,主窗口切换样式时 DWM 产生的 Aero 回退帧用户就看不到了。 + JarkEnsureFullScreenMaskClassRegistered(); + HWND maskWnd = CreateWindowExW( + WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE, + L"JarkFullScreenMaskWnd", L"", + WS_POPUP, + monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, + nullptr, nullptr, GetModuleHandleW(nullptr), nullptr); + ShowWindow(maskWnd, SW_SHOWNOACTIVATE); + // 强制遮挡窗口立即绘制一帧,确保切换前屏幕已被纯色覆盖 + UpdateWindow(maskWnd); + // 让 DWM 把这一帧合成上屏 + DwmFlush(); + + // 此时屏幕已被遮挡窗口覆盖,安全地切换主窗口样式/位置 if (isFullScreen) { - // 退出全屏模式,恢复之前的窗口状态 SetWindowLong(hwnd, GWL_STYLE, preStyle); SetWindowLong(hwnd, GWL_EXSTYLE, preExStyle); SetWindowPos(hwnd, nullptr, preRect.left, preRect.top, @@ -462,16 +532,10 @@ void jarkUtils::ToggleFullScreen(HWND hwnd) { SWP_NOZORDER | SWP_FRAMECHANGED); } else { - // 保存当前窗口状态 GetWindowRect(hwnd, &preRect); preStyle = GetWindowLong(hwnd, GWL_STYLE); preExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); - // 切换到全屏模式 - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO monitorInfo = { sizeof(monitorInfo) }; - GetMonitorInfo(monitor, &monitorInfo); - SetWindowLong(hwnd, GWL_STYLE, preStyle & ~(WS_CAPTION | WS_THICKFRAME)); SetWindowLong(hwnd, GWL_EXSTYLE, preExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); @@ -482,6 +546,22 @@ void jarkUtils::ToggleFullScreen(HWND hwnd) { SWP_NOZORDER | SWP_FRAMECHANGED); } + // 同步排空待处理消息,确保窗口过程已处理完样式/位置变更 + MSG msg; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + // 让 DWM 完成过渡帧合成(此时主窗口的 Aero 回退帧被遮挡窗口盖住,用户看不到) + DwmFlush(); + // 给 DWM 足够时间完成最终样式渲染(经验值,约一帧多) + Sleep(30); + // 再 flush 一次确保最终帧已上屏 + DwmFlush(); + + // 销毁遮挡窗口,露出已经完成切换的主窗口 + DestroyWindow(maskWnd); + isFullScreen = !isFullScreen; } From 73533f0bad3c2ddcd70ac5819b354a514bd84bbe Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:15:22 +0800 Subject: [PATCH 04/16] Update main.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲时突然滚动导致的轻微卡顿 --- JarkViewer/src/main.cpp | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 7cf4433..419b52b 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -609,9 +609,15 @@ class JarkViewerApp : public D3D11App { void OnMouseWheel(UINT nFlags, short zDelta, int x, int y) override { switch (cursorPos) { - case CursorPos::centerArea: - operateQueue.push({ zDelta < 0 ? ActionENUM::zoomOut : ActionENUM::zoomIn }); - break; + case CursorPos::centerArea: { + // 快速拨动时 zDelta 可能为几百甚至上千。按每“一格(120)”折算步数, + // 既让快速滚轮缩放更跟手,也避免把一次猛拨只当成一格 30% 缩放。 + constexpr int WHEEL_DELTA_STEP = 120; + int deltaMag = (zDelta < 0 ? -(int)zDelta : (int)zDelta); + int zoomSteps = std::max(1, deltaMag / WHEEL_DELTA_STEP); + if (zoomSteps > 10) zoomSteps = 10; // 防止单条消息步数过大 + operateQueue.push({ zDelta < 0 ? ActionENUM::zoomOut : ActionENUM::zoomIn, zoomSteps }); + } break; case CursorPos::leftEdge: case CursorPos::rightEdge: @@ -1900,10 +1906,16 @@ class JarkViewerApp : public D3D11App { } break; case ActionENUM::zoomIn: { - // 无极缩放,步长 30% + // 无极缩放,步长 30%;一次动作可能包含多步(快速滚轮或队列合并而来) constexpr double zoomFactor = 1.3; - int64_t zoomNext = (int64_t)(curPar.zoomTarget * zoomFactor); - if (zoomNext <= curPar.zoomTarget) zoomNext = curPar.zoomTarget + 1; + int steps = std::max(1, operateAction.value1); + if (steps > 30) steps = 30; // 限制单帧跨度,避免数值过大 + int64_t zoomNext = curPar.zoomTarget; + for (int i = 0; i < steps; i++) { + int64_t z = (int64_t)(zoomNext * zoomFactor); + if (z <= zoomNext) z = zoomNext + 1; + zoomNext = z; + } if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; @@ -1912,12 +1924,18 @@ class JarkViewerApp : public D3D11App { } break; case ActionENUM::zoomOut: { - // 无极缩放,步长 30% + // 无极缩放,步长 30%;一次动作可能包含多步(快速滚轮或队列合并而来) constexpr double zoomFactor = 1.3; - // 不宜缩太小 - int64_t zoomNext = std::max((int64_t)(curPar.zoomTarget / zoomFactor), 1); - if (zoomNext <= curPar.ZOOM_BASE && (zoomNext * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) - break; + int steps = std::max(1, operateAction.value1); + if (steps > 30) steps = 30; // 限制单帧跨度,避免数值过大 + int64_t zoomNext = curPar.zoomTarget; + for (int i = 0; i < steps; i++) { + // 不宜缩太小 + int64_t z = std::max((int64_t)(zoomNext / zoomFactor), 1); + if (z <= curPar.ZOOM_BASE && (z * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) + break; + zoomNext = z; + } if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; From b1bd1bb1bdff316224b10d4bd82edc756020a9ac Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:17:19 +0800 Subject: [PATCH 05/16] Update jarkUtils.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲时突然滚动导致的轻微卡顿 --- JarkViewer/include/jarkUtils.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/JarkViewer/include/jarkUtils.h b/JarkViewer/include/jarkUtils.h index 45ef9a7..0443988 100644 --- a/JarkViewer/include/jarkUtils.h +++ b/JarkViewer/include/jarkUtils.h @@ -379,13 +379,19 @@ class OperateQueue { void push(Action action) { std::lock_guard lock(mtx); - if (!queue.empty() && action.action == ActionENUM::slide) { + if (!queue.empty()) { Action& back = queue.back(); - if (back.action == ActionENUM::slide) { + if (action.action == ActionENUM::slide && back.action == ActionENUM::slide) { back.x += action.x; back.y += action.y; } + // 连续同向滚轮缩放合并为一步(步数累加到 value1),避免快速拨动时 + // 队列堆积大量 zoom 动作,导致手势刚开始要连渲多帧而出现卡顿。 + else if ((action.action == ActionENUM::zoomIn && back.action == ActionENUM::zoomIn) || + (action.action == ActionENUM::zoomOut && back.action == ActionENUM::zoomOut)) { + back.value1 += action.value1; + } else { queue.push(action); } From b4817ccef452e71eed9a8fd15f84e9359d05315c Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 06:17:05 +0800 Subject: [PATCH 06/16] Update main.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿 --- JarkViewer/src/main.cpp | 83 ++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 419b52b..fed9cbf 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -27,6 +27,9 @@ std::wstring_view RepositoryLink = L"https://github.com/jark006/JarkViewer"; std::wstring_view BaiduLink = L"https://pan.baidu.com/s/1ka7p__WVw2du3mnOfqWceQ?pwd=6666"; // 密码 6666 std::wstring_view LanzouLink = L"https://jark006.lanzout.com/b0ko7mczg"; // 密码 6666 +// 空闲后首次真实交互前需一次性预热 GPU/线程池(懒预热标志) +static bool g_gpuWarmupPending = true; + static constexpr auto generate_zoom_list() { // 原始缩放级别数组(2^10 到 2^22) @@ -609,15 +612,9 @@ class JarkViewerApp : public D3D11App { void OnMouseWheel(UINT nFlags, short zDelta, int x, int y) override { switch (cursorPos) { - case CursorPos::centerArea: { - // 快速拨动时 zDelta 可能为几百甚至上千。按每“一格(120)”折算步数, - // 既让快速滚轮缩放更跟手,也避免把一次猛拨只当成一格 30% 缩放。 - constexpr int WHEEL_DELTA_STEP = 120; - int deltaMag = (zDelta < 0 ? -(int)zDelta : (int)zDelta); - int zoomSteps = std::max(1, deltaMag / WHEEL_DELTA_STEP); - if (zoomSteps > 10) zoomSteps = 10; // 防止单条消息步数过大 - operateQueue.push({ zDelta < 0 ? ActionENUM::zoomOut : ActionENUM::zoomIn, zoomSteps }); - } break; + case CursorPos::centerArea: + operateQueue.push({ zDelta < 0 ? ActionENUM::zoomOut : ActionENUM::zoomIn }); + break; case CursorPos::leftEdge: case CursorPos::rightEdge: @@ -1666,13 +1663,51 @@ class JarkViewerApp : public D3D11App { if (operateAction.action == ActionENUM::none && curPar.zoomCur == curPar.zoomTarget && curPar.slideCur == curPar.slideTarget && - (curPar.imageAssetPtr->format != ImageFormat::Animated || + (curPar.imageAssetPtr->format != ImageFormat::Animated || (curPar.imageAssetPtr->format == ImageFormat::Animated && curPar.isAnimationPause))) { + // 空闲时仅标记"下次交互前需预热",让 GPU/线程池真正睡死(零额外功耗)。 + // 预热在一次真实交互开始时一次性完成,把唤醒代价在渲染前付清,首帧即顺滑。 + g_gpuWarmupPending = true; Sleep(1); // Windows机制限制,实际时长最小只能 15.6ms return; } + // 懒预热:空闲后首次真实交互前,一次性付清"空闲→激活"的全部冷启动代价, + // 首帧渲染即顺滑;空闲时 GPU/CPU 完全睡死,零额外功耗。覆盖: + // ① GPU/交换链唤醒(含 DWM 退出低功耗)② PPL 线程池唤醒 + CPU 提频 + // ③ 当前原图常驻(消除 drawCanvas 采样 page fault)④ CPU 画布常驻(消除 std::fill/写入冷页) + if (g_gpuWarmupPending) { + g_gpuWarmupPending = false; + + // ① 唤醒交换链 / GPU(不重传 32MB 画布);尺寸不符时跳过,下一帧真实渲染会重建 + if (mainCanvas.cols == winWidth && mainCanvas.rows == winHeight) { + PresentLastFrame(); + } + + // 并行触碰辅助函数:按 4KB 页读取首字节,唤醒 PPL 线程池、触发 CPU 提频、并让内存常驻 + auto touchByPage = [&](const cv::Mat& m) { + if (m.empty()) return; + const size_t bytes = m.total() * m.elemSize(); + const size_t pageSize = 4096; + const size_t nChunks = (bytes + pageSize - 1) / pageSize; + concurrency::parallel_for((size_t)0, nChunks, [&](size_t i) { + volatile uint8_t touch = m.data[i * pageSize]; + (void)touch; + }); + }; + + // ③ 当前原图(still/none 用 primaryFrame,动图用当前帧) + auto& asset = *curPar.imageAssetPtr; + const cv::Mat& warmImg = (asset.format == ImageFormat::Still || asset.format == ImageFormat::None) + ? asset.primaryFrame + : asset.frames[curPar.curFrameIdx]; + touchByPage(warmImg); + + // ④ CPU 画布(消除 drawCanvas 开头 std::fill 与采样写入的冷页代价) + touchByPage(mainCanvas); + } + if (operateAction.action == ActionENUM::printImage) { if (Printer::isWorking) { jarkUtils::activateWindow(Printer::hwnd); @@ -1906,16 +1941,10 @@ class JarkViewerApp : public D3D11App { } break; case ActionENUM::zoomIn: { - // 无极缩放,步长 30%;一次动作可能包含多步(快速滚轮或队列合并而来) + // 无极缩放,步长 30% constexpr double zoomFactor = 1.3; - int steps = std::max(1, operateAction.value1); - if (steps > 30) steps = 30; // 限制单帧跨度,避免数值过大 - int64_t zoomNext = curPar.zoomTarget; - for (int i = 0; i < steps; i++) { - int64_t z = (int64_t)(zoomNext * zoomFactor); - if (z <= zoomNext) z = zoomNext + 1; - zoomNext = z; - } + int64_t zoomNext = (int64_t)(curPar.zoomTarget * zoomFactor); + if (zoomNext <= curPar.zoomTarget) zoomNext = curPar.zoomTarget + 1; if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; @@ -1924,18 +1953,12 @@ class JarkViewerApp : public D3D11App { } break; case ActionENUM::zoomOut: { - // 无极缩放,步长 30%;一次动作可能包含多步(快速滚轮或队列合并而来) + // 无极缩放,步长 30% constexpr double zoomFactor = 1.3; - int steps = std::max(1, operateAction.value1); - if (steps > 30) steps = 30; // 限制单帧跨度,避免数值过大 - int64_t zoomNext = curPar.zoomTarget; - for (int i = 0; i < steps; i++) { - // 不宜缩太小 - int64_t z = std::max((int64_t)(zoomNext / zoomFactor), 1); - if (z <= curPar.ZOOM_BASE && (z * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) - break; - zoomNext = z; - } + // 不宜缩太小 + int64_t zoomNext = std::max((int64_t)(curPar.zoomTarget / zoomFactor), 1); + if (zoomNext <= curPar.ZOOM_BASE && (zoomNext * std::min(curPar.width, curPar.height) / curPar.ZOOM_BASE) < 4) + break; if (curPar.zoomTarget && zoomNext != curPar.zoomTarget) { computeZoomSlide(zoomNext); curPar.zoomTarget = zoomNext; From f02a637aad2a66838052ab73ee95b11e6f5665c6 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 06:18:30 +0800 Subject: [PATCH 07/16] Update D3D11App.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿 --- JarkViewer/include/D3D11App.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/JarkViewer/include/D3D11App.h b/JarkViewer/include/D3D11App.h index 7614314..fd93fc6 100644 --- a/JarkViewer/include/D3D11App.h +++ b/JarkViewer/include/D3D11App.h @@ -38,6 +38,10 @@ class D3D11App { // CPU 画布数据呈现到屏幕 void PresentCanvas(const uint8_t* data, int width, int height, int stride); + // 仅把已上传的暂存纹理再提交一次(GPU 内部 CopyResource + Present),不重传画布。 + // 用于空闲保活:零 CPU 画布拷贝代价,让交换链/GPU 始终活跃,避免空闲后首次交互的唤醒卡顿。 + void PresentLastFrame(); + template void SafeRelease(Interface*& pInterfaceToRelease); From 99a213231fdd59991bc6484229b05a2bd1508a6d Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 06:19:19 +0800 Subject: [PATCH 08/16] Update D3D11App.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿 --- JarkViewer/src/D3D11App.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/JarkViewer/src/D3D11App.cpp b/JarkViewer/src/D3D11App.cpp index 962dabe..767dca9 100644 --- a/JarkViewer/src/D3D11App.cpp +++ b/JarkViewer/src/D3D11App.cpp @@ -358,6 +358,20 @@ void D3D11App::PresentCanvas(const uint8_t* data, int width, int height, int str m_pSwapChain->Present(0, 0); } +void D3D11App::PresentLastFrame() { + if (!m_pStagingTexture || !m_pSwapChain || !m_pD3DDeviceContext) + return; + + // 直接复用已上传到暂存纹理的上一帧,仅做一次 GPU 内部拷贝并呈现,不触碰 CPU 画布。 + ID3D11Texture2D* pBackBuffer = nullptr; + HRESULT hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer); + if (SUCCEEDED(hr)) { + m_pD3DDeviceContext->CopyResource(pBackBuffer, m_pStagingTexture); + pBackBuffer->Release(); + } + m_pSwapChain->Present(0, 0); +} + void D3D11App::DiscardDeviceResources() { SafeRelease(m_pStagingTexture); SafeRelease(m_pSwapChain); From 0dbcc9d392f853a743ab7e82ad60c0a2a1c6b41b Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 06:24:25 +0800 Subject: [PATCH 09/16] Update jarkUtils.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿-回退 --- JarkViewer/include/jarkUtils.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/JarkViewer/include/jarkUtils.h b/JarkViewer/include/jarkUtils.h index 0443988..45ef9a7 100644 --- a/JarkViewer/include/jarkUtils.h +++ b/JarkViewer/include/jarkUtils.h @@ -379,19 +379,13 @@ class OperateQueue { void push(Action action) { std::lock_guard lock(mtx); - if (!queue.empty()) { + if (!queue.empty() && action.action == ActionENUM::slide) { Action& back = queue.back(); - if (action.action == ActionENUM::slide && back.action == ActionENUM::slide) { + if (back.action == ActionENUM::slide) { back.x += action.x; back.y += action.y; } - // 连续同向滚轮缩放合并为一步(步数累加到 value1),避免快速拨动时 - // 队列堆积大量 zoom 动作,导致手势刚开始要连渲多帧而出现卡顿。 - else if ((action.action == ActionENUM::zoomIn && back.action == ActionENUM::zoomIn) || - (action.action == ActionENUM::zoomOut && back.action == ActionENUM::zoomOut)) { - back.value1 += action.value1; - } else { queue.push(action); } From a56f72470ba9ec9ccf36c95a38204feb6b5860e4 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 07:35:39 +0800 Subject: [PATCH 10/16] Update D3D11App.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回退 --- JarkViewer/include/D3D11App.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/JarkViewer/include/D3D11App.h b/JarkViewer/include/D3D11App.h index fd93fc6..7614314 100644 --- a/JarkViewer/include/D3D11App.h +++ b/JarkViewer/include/D3D11App.h @@ -38,10 +38,6 @@ class D3D11App { // CPU 画布数据呈现到屏幕 void PresentCanvas(const uint8_t* data, int width, int height, int stride); - // 仅把已上传的暂存纹理再提交一次(GPU 内部 CopyResource + Present),不重传画布。 - // 用于空闲保活:零 CPU 画布拷贝代价,让交换链/GPU 始终活跃,避免空闲后首次交互的唤醒卡顿。 - void PresentLastFrame(); - template void SafeRelease(Interface*& pInterfaceToRelease); From efe810c48a16726f3d1c534e3b2ea7a2cef75e76 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 07:43:23 +0800 Subject: [PATCH 11/16] Update main.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿 --- JarkViewer/src/main.cpp | 46 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index fed9cbf..0e176c4 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -27,8 +27,8 @@ std::wstring_view RepositoryLink = L"https://github.com/jark006/JarkViewer"; std::wstring_view BaiduLink = L"https://pan.baidu.com/s/1ka7p__WVw2du3mnOfqWceQ?pwd=6666"; // 密码 6666 std::wstring_view LanzouLink = L"https://jark006.lanzout.com/b0ko7mczg"; // 密码 6666 -// 空闲后首次真实交互前需一次性预热 GPU/线程池(懒预热标志) -static bool g_gpuWarmupPending = true; +// 空闲后首次真实交互前,预热一次真实渲染上传路径(避免首帧冷启动卡顿) +static bool s_renderWarmed = false; static constexpr auto generate_zoom_list() { @@ -1666,46 +1666,20 @@ class JarkViewerApp : public D3D11App { (curPar.imageAssetPtr->format != ImageFormat::Animated || (curPar.imageAssetPtr->format == ImageFormat::Animated && curPar.isAnimationPause))) { - // 空闲时仅标记"下次交互前需预热",让 GPU/线程池真正睡死(零额外功耗)。 - // 预热在一次真实交互开始时一次性完成,把唤醒代价在渲染前付清,首帧即顺滑。 - g_gpuWarmupPending = true; + // 空闲:标记下次交互需预热一次真实渲染路径,并让线程真正睡死(零额外功耗)。 + s_renderWarmed = false; Sleep(1); // Windows机制限制,实际时长最小只能 15.6ms return; } - // 懒预热:空闲后首次真实交互前,一次性付清"空闲→激活"的全部冷启动代价, - // 首帧渲染即顺滑;空闲时 GPU/CPU 完全睡死,零额外功耗。覆盖: - // ① GPU/交换链唤醒(含 DWM 退出低功耗)② PPL 线程池唤醒 + CPU 提频 - // ③ 当前原图常驻(消除 drawCanvas 采样 page fault)④ CPU 画布常驻(消除 std::fill/写入冷页) - if (g_gpuWarmupPending) { - g_gpuWarmupPending = false; - - // ① 唤醒交换链 / GPU(不重传 32MB 画布);尺寸不符时跳过,下一帧真实渲染会重建 + // 空闲后首次真实交互:用"当前已渲染帧"走一次真实上传路径(Map+memcpy+CopyResource+Present), + // 把 D3D11 暂存纹理映射 / 显存 DMA / DWM 重组合这一帧的冷启动代价在缩放渲染之前付清, + // 首帧缩放即顺滑;空闲时完全不占用(线程睡死)。只做一次,连续交互期间不再重复。 + if (!s_renderWarmed) { + s_renderWarmed = true; if (mainCanvas.cols == winWidth && mainCanvas.rows == winHeight) { - PresentLastFrame(); + PresentCanvas(mainCanvas.ptr(), mainCanvas.cols, mainCanvas.rows, (int)mainCanvas.step); } - - // 并行触碰辅助函数:按 4KB 页读取首字节,唤醒 PPL 线程池、触发 CPU 提频、并让内存常驻 - auto touchByPage = [&](const cv::Mat& m) { - if (m.empty()) return; - const size_t bytes = m.total() * m.elemSize(); - const size_t pageSize = 4096; - const size_t nChunks = (bytes + pageSize - 1) / pageSize; - concurrency::parallel_for((size_t)0, nChunks, [&](size_t i) { - volatile uint8_t touch = m.data[i * pageSize]; - (void)touch; - }); - }; - - // ③ 当前原图(still/none 用 primaryFrame,动图用当前帧) - auto& asset = *curPar.imageAssetPtr; - const cv::Mat& warmImg = (asset.format == ImageFormat::Still || asset.format == ImageFormat::None) - ? asset.primaryFrame - : asset.frames[curPar.curFrameIdx]; - touchByPage(warmImg); - - // ④ CPU 画布(消除 drawCanvas 开头 std::fill 与采样写入的冷页代价) - touchByPage(mainCanvas); } if (operateAction.action == ActionENUM::printImage) { From 82bac1f371346d5e7ee92c27082bdd7f82fa9571 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 07:46:27 +0800 Subject: [PATCH 12/16] Update D3D11App.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复空闲到突然滚动的卡顿 --- JarkViewer/src/D3D11App.cpp | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/JarkViewer/src/D3D11App.cpp b/JarkViewer/src/D3D11App.cpp index 767dca9..d9d9242 100644 --- a/JarkViewer/src/D3D11App.cpp +++ b/JarkViewer/src/D3D11App.cpp @@ -358,20 +358,6 @@ void D3D11App::PresentCanvas(const uint8_t* data, int width, int height, int str m_pSwapChain->Present(0, 0); } -void D3D11App::PresentLastFrame() { - if (!m_pStagingTexture || !m_pSwapChain || !m_pD3DDeviceContext) - return; - - // 直接复用已上传到暂存纹理的上一帧,仅做一次 GPU 内部拷贝并呈现,不触碰 CPU 画布。 - ID3D11Texture2D* pBackBuffer = nullptr; - HRESULT hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer); - if (SUCCEEDED(hr)) { - m_pD3DDeviceContext->CopyResource(pBackBuffer, m_pStagingTexture); - pBackBuffer->Release(); - } - m_pSwapChain->Present(0, 0); -} - void D3D11App::DiscardDeviceResources() { SafeRelease(m_pStagingTexture); SafeRelease(m_pSwapChain); @@ -382,13 +368,13 @@ void D3D11App::DiscardDeviceResources() { void D3D11App::Run() { while (m_fRunning) { MSG msg; + // 每条待处理消息只取一条并分发,随后必定渲染一帧:避免快速滚轮等输入洪流 + // 把 DrawScene 饿死,导致缩放开头出现“卡顿/无响应”后才突然追平。 if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } - else { - DrawScene(); - } + DrawScene(); } } From 5d49a2d4fe72cd810b1819a99d789fecb82d03b1 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 16:42:33 +0800 Subject: [PATCH 13/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E6=97=B6=E7=AA=81=E7=84=B6=E6=BB=9A=E5=8A=A8=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E5=8D=A1=E9=A1=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JarkViewer/src/main.cpp | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 0e176c4..312b932 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -27,9 +27,6 @@ std::wstring_view RepositoryLink = L"https://github.com/jark006/JarkViewer"; std::wstring_view BaiduLink = L"https://pan.baidu.com/s/1ka7p__WVw2du3mnOfqWceQ?pwd=6666"; // 密码 6666 std::wstring_view LanzouLink = L"https://jark006.lanzout.com/b0ko7mczg"; // 密码 6666 -// 空闲后首次真实交互前,预热一次真实渲染上传路径(避免首帧冷启动卡顿) -static bool s_renderWarmed = false; - static constexpr auto generate_zoom_list() { // 原始缩放级别数组(2^10 到 2^22) @@ -1666,22 +1663,33 @@ class JarkViewerApp : public D3D11App { (curPar.imageAssetPtr->format != ImageFormat::Animated || (curPar.imageAssetPtr->format == ImageFormat::Animated && curPar.isAnimationPause))) { - // 空闲:标记下次交互需预热一次真实渲染路径,并让线程真正睡死(零额外功耗)。 - s_renderWarmed = false; + // 空闲保活:每秒做一次完整的真实渲染管线(drawCanvas + Present), + // 让 DWM 持续看到"内容在更新"的新帧,阻止窗口被降级。 + // 每秒一次,能耗占空比 ~0.4%,可忽略。 + static ULONGLONG s_lastKeepAlive = 0; + ULONGLONG nowMs = GetTickCount64(); + if (nowMs - s_lastKeepAlive >= 1000) { + s_lastKeepAlive = nowMs; + cv::Mat srcImg; + if (curPar.imageAssetPtr->format == ImageFormat::None || + curPar.imageAssetPtr->format == ImageFormat::Still) { + srcImg = curPar.imageAssetPtr->primaryFrame; + } + else { + srcImg = curPar.imageAssetPtr->frames[curPar.curFrameIdx]; + } + if (!srcImg.empty() && mainCanvas.cols == winWidth && mainCanvas.rows == winHeight) { + drawCanvas(srcImg, mainCanvas); + drawExifInfo(mainCanvas); + drawExtraUI(mainCanvas); + updateMainCanvas(); + } + } + Sleep(1); // Windows机制限制,实际时长最小只能 15.6ms return; } - // 空闲后首次真实交互:用"当前已渲染帧"走一次真实上传路径(Map+memcpy+CopyResource+Present), - // 把 D3D11 暂存纹理映射 / 显存 DMA / DWM 重组合这一帧的冷启动代价在缩放渲染之前付清, - // 首帧缩放即顺滑;空闲时完全不占用(线程睡死)。只做一次,连续交互期间不再重复。 - if (!s_renderWarmed) { - s_renderWarmed = true; - if (mainCanvas.cols == winWidth && mainCanvas.rows == winHeight) { - PresentCanvas(mainCanvas.ptr(), mainCanvas.cols, mainCanvas.rows, (int)mainCanvas.step); - } - } - if (operateAction.action == ActionENUM::printImage) { if (Printer::isWorking) { jarkUtils::activateWindow(Printer::hwnd); From 4dbfdc69a5b2143933f7358bf9b9b9010f50e435 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Sun, 26 Jul 2026 16:46:38 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A9=BA=E9=97=B2?= =?UTF-8?q?=E6=97=B6=E7=AA=81=E7=84=B6=E6=BB=9A=E5=8A=A8=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E7=9A=84=E5=8D=A1=E9=A1=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JarkViewer/src/D3D11App.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/JarkViewer/src/D3D11App.cpp b/JarkViewer/src/D3D11App.cpp index d9d9242..5b24a60 100644 --- a/JarkViewer/src/D3D11App.cpp +++ b/JarkViewer/src/D3D11App.cpp @@ -192,6 +192,9 @@ HRESULT D3D11App::Initialize(HINSTANCE hInstance) { if (SUCCEEDED(hr)) { CreateDeviceResources(); + // 保持系统激活(阻止 DWM/GPU/线程因空闲被降级),但不阻止屏幕关闭。 + SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + BOOL themeMode = GlobalVar::isCurrentUIDarkMode; DwmSetWindowAttribute(m_hWnd, 20, &themeMode, sizeof(BOOL)); DragAcceptFiles(m_hWnd, TRUE); @@ -368,13 +371,13 @@ void D3D11App::DiscardDeviceResources() { void D3D11App::Run() { while (m_fRunning) { MSG msg; - // 每条待处理消息只取一条并分发,随后必定渲染一帧:避免快速滚轮等输入洪流 - // 把 DrawScene 饿死,导致缩放开头出现“卡顿/无响应”后才突然追平。 if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } - DrawScene(); + else { + DrawScene(); + } } } From e5fe46bc04e8cd1edc312792b44502735b1e3a3c Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Mon, 27 Jul 2026 01:38:42 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- JarkViewer/src/main.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/JarkViewer/src/main.cpp b/JarkViewer/src/main.cpp index 312b932..de0b0a6 100644 --- a/JarkViewer/src/main.cpp +++ b/JarkViewer/src/main.cpp @@ -1663,29 +1663,6 @@ class JarkViewerApp : public D3D11App { (curPar.imageAssetPtr->format != ImageFormat::Animated || (curPar.imageAssetPtr->format == ImageFormat::Animated && curPar.isAnimationPause))) { - // 空闲保活:每秒做一次完整的真实渲染管线(drawCanvas + Present), - // 让 DWM 持续看到"内容在更新"的新帧,阻止窗口被降级。 - // 每秒一次,能耗占空比 ~0.4%,可忽略。 - static ULONGLONG s_lastKeepAlive = 0; - ULONGLONG nowMs = GetTickCount64(); - if (nowMs - s_lastKeepAlive >= 1000) { - s_lastKeepAlive = nowMs; - cv::Mat srcImg; - if (curPar.imageAssetPtr->format == ImageFormat::None || - curPar.imageAssetPtr->format == ImageFormat::Still) { - srcImg = curPar.imageAssetPtr->primaryFrame; - } - else { - srcImg = curPar.imageAssetPtr->frames[curPar.curFrameIdx]; - } - if (!srcImg.empty() && mainCanvas.cols == winWidth && mainCanvas.rows == winHeight) { - drawCanvas(srcImg, mainCanvas); - drawExifInfo(mainCanvas); - drawExtraUI(mainCanvas); - updateMainCanvas(); - } - } - Sleep(1); // Windows机制限制,实际时长最小只能 15.6ms return; } From 2918b9d77ced4b257837b807480f1e8558a9abd6 Mon Sep 17 00:00:00 2001 From: Celestialsapien <145863291+Celestialsapien@users.noreply.github.com> Date: Mon, 27 Jul 2026 01:43:00 +0800 Subject: [PATCH 16/16] Update D3D11App.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回退 --- JarkViewer/src/D3D11App.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/JarkViewer/src/D3D11App.cpp b/JarkViewer/src/D3D11App.cpp index 5b24a60..962dabe 100644 --- a/JarkViewer/src/D3D11App.cpp +++ b/JarkViewer/src/D3D11App.cpp @@ -192,9 +192,6 @@ HRESULT D3D11App::Initialize(HINSTANCE hInstance) { if (SUCCEEDED(hr)) { CreateDeviceResources(); - // 保持系统激活(阻止 DWM/GPU/线程因空闲被降级),但不阻止屏幕关闭。 - SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); - BOOL themeMode = GlobalVar::isCurrentUIDarkMode; DwmSetWindowAttribute(m_hWnd, 20, &themeMode, sizeof(BOOL)); DragAcceptFiles(m_hWnd, TRUE);