From e04e6a11d1ad4ddb48daf97a014fb9f45e60b0d9 Mon Sep 17 00:00:00 2001 From: Mark Russinovich Date: Tue, 7 Oct 2025 11:20:00 -0700 Subject: [PATCH] ZoomIt smooth image zooming (#42200) Added smooth image option that results in GDI+ image smoothing for static zoom and Magnifier API image smoothing for live zoom. --------- Co-authored-by: Mark Russinovich Co-authored-by: Clint Rutkas Co-authored-by: Niels Laute --- .github/actions/spell-check/expect.txt | 2 + src/modules/ZoomIt/ZoomIt/ZoomIt.h | 14 +- src/modules/ZoomIt/ZoomIt/ZoomIt.rc | 61 ++++----- src/modules/ZoomIt/ZoomIt/ZoomItSettings.h | 2 + src/modules/ZoomIt/ZoomIt/Zoomit.cpp | 128 ++++++++++++------ src/modules/ZoomIt/ZoomIt/resource.h | 3 +- .../Settings.UI.Library/ZoomItProperties.cs | 2 + .../SettingsXAML/Views/ZoomItPage.xaml | 3 + .../Settings.UI/Strings/en-us/Resources.resw | 3 + .../Settings.UI/ViewModels/ZoomItViewModel.cs | 14 ++ 10 files changed, 159 insertions(+), 73 deletions(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 41b05c2606..b4c01efeb9 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -856,6 +856,7 @@ linkid LINKOVERLAY LINQTo listview +LIVEDRAW LIVEZOOM LLKH llkhf @@ -1237,6 +1238,7 @@ PATPAINT pbc pbi PBlob +pbrush pcb pcch pcelt diff --git a/src/modules/ZoomIt/ZoomIt/ZoomIt.h b/src/modules/ZoomIt/ZoomIt/ZoomIt.h index 5abbc21039..2687ba2b65 100644 --- a/src/modules/ZoomIt/ZoomIt/ZoomIt.h +++ b/src/modules/ZoomIt/ZoomIt/ZoomIt.h @@ -96,7 +96,10 @@ typedef struct { #define SHALLOW_DESTROY 2 #define LIVE_DRAW_ZOOM 3 -#define PEN_COLOR_HIGHLIGHT(Pencolor) (Pencolor >> 24) != 0xFF +#define PEN_COLOR_HIGHLIGHT(Pencolor) ((Pencolor >> 24) != 0xFF) +#define PEN_COLOR_BLUR(Pencolor) ((Pencolor & 0x00FFFFFF) == COLOR_BLUR) + +#define CURSOR_SAVE_MARGIN 4 typedef BOOL (__stdcall *type_pGetMonitorInfo)( @@ -143,7 +146,14 @@ typedef BOOL(__stdcall *type_pMagSetWindowFilterList)( int count, HWND* pHWND ); -typedef BOOL (__stdcall *type_pMagInitialize)(VOID); +typedef BOOL(__stdcall* type_pMagSetLensUseBitmapSmoothing)( + _In_ HWND, + _In_ BOOL +); +typedef BOOL(__stdcall* type_MagSetFullscreenUseBitmapSmoothing)( + BOOL fUseBitmapSmoothing +); +typedef BOOL(__stdcall* type_pMagInitialize)(VOID); typedef BOOL(__stdcall *type_pGetPointerType)( _In_ UINT32 pointerId, diff --git a/src/modules/ZoomIt/ZoomIt/ZoomIt.rc b/src/modules/ZoomIt/ZoomIt/ZoomIt.rc index 7862538c4a..8b8cab11eb 100644 --- a/src/modules/ZoomIt/ZoomIt/ZoomIt.rc +++ b/src/modules/ZoomIt/ZoomIt/ZoomIt.rc @@ -2,13 +2,6 @@ // #include "resource.h" -// version.h and branding.h are different in the Sysinternals repository, -// keep the includes as such, here. -// From $(MSBuildThisFileDirectory)..\..\..\common\version -#include "version.h" -// From $(MSBuildThisFileDirectory)PowerToys -#include "branding.h" - #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // @@ -75,8 +68,8 @@ APPICON ICON "appicon.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION FILE_VERSION - PRODUCTVERSION PRODUCT_VERSION + FILEVERSION 9,10,0,0 + PRODUCTVERSION 9,10,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -91,14 +84,14 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "CompanyName", COMPANY_NAME - VALUE "FileDescription", FILE_DESCRIPTION - VALUE "FileVersion", FILE_VERSION_STRING - VALUE "InternalName", INTERNAL_NAME - VALUE "LegalCopyright", COPYRIGHT_NOTE - VALUE "OriginalFilename", ORIGINAL_FILENAME - VALUE "ProductName", ZOOMIT_PRODUCT_NAME - VALUE "ProductVersion", PRODUCT_VERSION_STRING + VALUE "CompanyName", "Microsoft Corporation" + VALUE "FileDescription", "Sysinternals Screen Magnifier" + VALUE "FileVersion", "9.10" + VALUE "InternalName", "ZoomIt" + VALUE "LegalCopyright", "Copyright (C) Microsoft Corporation. All rights reserved." + VALUE "OriginalFilename", "PowerToys.ZoomIt.exe" + VALUE "ProductName", "PowerToys Sysinternals ZoomIt" + VALUE "ProductVersion", "9.10" END END BLOCK "VarFileInfo" @@ -122,7 +115,7 @@ BEGIN DEFPUSHBUTTON "OK",IDOK,166,306,50,14 PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14 LTEXT "ZoomIt v9.01",IDC_VERSION,42,7,73,10 - LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8 + LTEXT "Copyright � 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8 CONTROL "Sysinternals - www.sysinternals.com",IDC_LINK, "SysLink",WS_TABSTOP,42,26,150,9 ICON "APPICON",IDC_STATIC,12,9,20,20 @@ -149,7 +142,8 @@ BEGIN CONTROL "",IDC_TIMER_POS7,"Button",BS_AUTORADIOBUTTON,63,108,10,10 CONTROL "",IDC_TIMER_POS8,"Button",BS_AUTORADIOBUTTON,79,108,10,10 CONTROL "",IDC_TIMER_POS9,"Button",BS_AUTORADIOBUTTON,97,108,10,10 - CONTROL "Show background bitmap:",IDC_CHECK_BACKGROUND_FILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,122,99,10,WS_EX_RIGHT + CONTROL "Show background bitmap:",IDC_CHECK_BACKGROUND_FILE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,122,99,10,WS_EX_RIGHT CONTROL "Use faded desktop as background",IDC_STATIC_DESKTOP_BACKGROUND, "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,46,135,125,10 CONTROL "Use image file as background",IDC_STATIC_BACKGROUND_FILE, @@ -165,23 +159,25 @@ BEGIN CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,7,196,193,1,WS_EX_CLIENTEDGE END -ZOOM DIALOGEX 0, 0, 260, 158 +ZOOM DIALOGEX 0, 0, 260, 170 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,59,57,80,12 LTEXT "After toggling ZoomIt you can zoom in with the mouse wheel or up and down arrow keys. Exit zoom mode with Escape or by pressing the right mouse button.",IDC_STATIC,7,6,246,26 LTEXT "Zoom Toggle:",IDC_STATIC,7,59,51,8 - CONTROL "",IDC_ZOOM_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,53,104,150,15,WS_EX_TRANSPARENT - LTEXT "Specify the initial level of magnification when zooming in:",IDC_STATIC,7,91,215,10 - LTEXT "1.25",IDC_STATIC,52,122,16,8 - LTEXT "1.5",IDC_STATIC,82,122,12,8 - LTEXT "1.75",IDC_STATIC,108,122,16,8 - LTEXT "2.0",IDC_STATIC,138,122,12,8 - LTEXT "3.0",IDC_STATIC,164,122,12,8 - LTEXT "4.0",IDC_STATIC,190,122,12,8 + CONTROL "",IDC_ZOOM_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,53,118,150,15,WS_EX_TRANSPARENT + LTEXT "Specify the initial level of magnification when zooming in:",IDC_STATIC,7,105,215,10 + LTEXT "1.25",IDC_STATIC,52,136,16,8 + LTEXT "1.5",IDC_STATIC,82,136,12,8 + LTEXT "1.75",IDC_STATIC,108,136,16,8 + LTEXT "2.0",IDC_STATIC,138,136,12,8 + LTEXT "3.0",IDC_STATIC,164,136,12,8 + LTEXT "4.0",IDC_STATIC,190,136,12,8 CONTROL "Animate zoom in and zoom out:",IDC_ANIMATE_ZOOM,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,74,116,10 - LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,7,34,246,17 + CONTROL "Smooth zoomed image:",IDC_SMOOTH_IMAGE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,88,116,10 + LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,7,148,246,17 + LTEXT "Copy a zoomed screen with Ctrl+C or save it by typing Ctrl+S. Crop the copy or save region by entering Ctrl+Shift instead of Ctrl.",IDC_STATIC,6,34,246,18 END DRAW DIALOGEX 0, 0, 260, 228 @@ -295,7 +291,8 @@ BEGIN LTEXT "DemoType toggle:",IDC_STATIC,7,157,63,8 PUSHBUTTON "&...",IDC_DEMOTYPE_BROWSE,231,137,16,13 CONTROL "",IDC_DEMOTYPE_SPEED_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,52,202,150,11,WS_EX_TRANSPARENT - CONTROL "Drive input with typing:",IDC_DEMOTYPE_USER_DRIVEN,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,173,88,10 + CONTROL "Drive input with typing:",IDC_DEMOTYPE_USER_DRIVEN, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,173,88,10 LTEXT "DemoType typing speed:",IDC_STATIC,7,189,215,10 LTEXT "Slow",IDC_DEMOTYPE_STATIC1,51,213,18,8 LTEXT "Fast",IDC_DEMOTYPE_STATIC2,186,213,17,8 @@ -413,8 +410,8 @@ ACCELERATORS ACCELERATORS BEGIN "C", IDC_COPY, VIRTKEY, CONTROL, NOINVERT "S", IDC_SAVE, VIRTKEY, CONTROL, NOINVERT - "C", IDC_COPY_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT - "S", IDC_SAVE_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT + "C", IDC_COPY_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT + "S", IDC_SAVE_CROP, VIRTKEY, SHIFT, CONTROL, NOINVERT END diff --git a/src/modules/ZoomIt/ZoomIt/ZoomItSettings.h b/src/modules/ZoomIt/ZoomIt/ZoomItSettings.h index 952314796f..96e2f19e5b 100644 --- a/src/modules/ZoomIt/ZoomIt/ZoomItSettings.h +++ b/src/modules/ZoomIt/ZoomIt/ZoomItSettings.h @@ -14,6 +14,7 @@ DWORD g_SnipToggleKey = ((HOTKEYF_CONTROL) << 8) | '6'; DWORD g_ShowExpiredTime = 1; DWORD g_SliderZoomLevel = 3; BOOLEAN g_AnimateZoom = TRUE; +BOOLEAN g_SmoothImage = TRUE; DWORD g_PenColor = COLOR_RED; DWORD g_BreakPenColor = COLOR_RED; DWORD g_RootPenWidth = PEN_WIDTH; @@ -72,6 +73,7 @@ REG_SETTING RegSettings[] = { { L"ShowTrayIcon", SETTING_TYPE_BOOLEAN, 0, &g_ShowTrayIcon, static_cast(g_ShowTrayIcon) }, // NOTE: AnimateZoom is misspelled, but since it is a user setting stored in the registry we must continue to misspell it. { L"AnimnateZoom", SETTING_TYPE_BOOLEAN, 0, &g_AnimateZoom, static_cast(g_AnimateZoom) }, + { L"SmoothImage", SETTING_TYPE_BOOLEAN, 0, &g_SmoothImage, static_cast(g_SmoothImage) }, { L"TelescopeZoomOut", SETTING_TYPE_BOOLEAN, 0, &g_TelescopeZoomOut, static_cast(g_TelescopeZoomOut) }, { L"SnapToGrid", SETTING_TYPE_BOOLEAN, 0, &g_SnapToGrid, static_cast(g_SnapToGrid) }, { L"ZoominSliderLevel", SETTING_TYPE_DWORD, 0, &g_SliderZoomLevel, static_cast(g_SliderZoomLevel) }, diff --git a/src/modules/ZoomIt/ZoomIt/Zoomit.cpp b/src/modules/ZoomIt/ZoomIt/Zoomit.cpp index b5660ec7ff..fadc760339 100644 --- a/src/modules/ZoomIt/ZoomIt/Zoomit.cpp +++ b/src/modules/ZoomIt/ZoomIt/Zoomit.cpp @@ -170,6 +170,8 @@ type_pMagSetFullscreenTransform pMagSetFullscreenTransform; type_pMagSetInputTransform pMagSetInputTransform; type_pMagShowSystemCursor pMagShowSystemCursor; type_pMagSetWindowFilterList pMagSetWindowFilterList; +type_MagSetFullscreenUseBitmapSmoothing pMagSetFullscreenUseBitmapSmoothing; +type_pMagSetLensUseBitmapSmoothing pMagSetLensUseBitmapSmoothing; type_pMagInitialize pMagInitialize; type_pDwmIsCompositionEnabled pDwmIsCompositionEnabled; type_pGetPointerType pGetPointerType; @@ -1099,6 +1101,8 @@ void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBr // Create a new bitmap that's the size of the area covered by the line + 2 * g_PenWidth Gdiplus::Rect lineBounds(min(x1, x2), min(y1, y2), abs(x2 - x1), abs(y2 - y1)); + OutputDebug(L"DrawHighlightedShape\n"); + // Expand for line drawing if (Shape == DRAW_LINE) lineBounds.Inflate(static_cast(g_PenWidth / 2), static_cast(g_PenWidth / 2)); @@ -1186,7 +1190,7 @@ void DrawHighlightedShape( DWORD Shape, HDC hdcScreenCompat, Gdiplus::Brush *pBr DeleteDC(hdcDIBOrig); // Invalidate the updated rectangle - // InvalidateGdiplusRect(hWnd, lineBounds); + //InvalidateGdiplusRect(hWnd, lineBounds); } //---------------------------------------------------------------------------- @@ -1284,7 +1288,12 @@ void ScaleImage( HDC hdcDst, float xDst, float yDst, float wDst, float hDst, { Gdiplus::Bitmap srcBitmap( bmSrc, NULL ); - dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality ); + // Use high quality interpolation when smooth image is enabled + if (g_SmoothImage) { + dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeHighQuality ); + } else { + dstGraphics.SetInterpolationMode( Gdiplus::InterpolationModeLowQuality ); + } dstGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf ); dstGraphics.DrawImage( &srcBitmap, Gdiplus::RectF(xDst,yDst,wDst,hDst), xSrc, ySrc, wSrc, hSrc, Gdiplus::UnitPixel ); @@ -2071,6 +2080,8 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message, IsAutostartConfigured() ? BST_CHECKED: BST_UNCHECKED ); CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM, g_AnimateZoom ? BST_CHECKED: BST_UNCHECKED ); + CheckDlgButton( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE, + g_SmoothImage ? BST_CHECKED: BST_UNCHECKED ); SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETRANGE, false, MAKELONG(0,_countof(g_ZoomLevels)-1) ); SendMessage( GetDlgItem(g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ZOOM_SLIDER), TBM_SETPOS, true, g_SliderZoomLevel ); @@ -2210,6 +2221,7 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message, } g_ShowTrayIcon = IsDlgButtonChecked( hDlg, IDC_SHOW_TRAY_ICON ) == BST_CHECKED; g_AnimateZoom = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_ANIMATE_ZOOM ) == BST_CHECKED; + g_SmoothImage = IsDlgButtonChecked( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_SMOOTH_IMAGE ) == BST_CHECKED; g_DemoTypeUserDriven = IsDlgButtonChecked( g_OptionsTabs[DEMOTYPE_PAGE].hPage, IDC_DEMOTYPE_USER_DRIVEN ) == BST_CHECKED; newToggleKey = static_cast(SendMessage( GetDlgItem( g_OptionsTabs[ZOOM_PAGE].hPage, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0 )); @@ -2723,7 +2735,6 @@ VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false ) bool isBlur = false; Gdiplus::Graphics dstGraphics(hDc); - if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 ) { dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); @@ -2746,6 +2757,7 @@ VOID DrawShape( DWORD Shape, HDC hDc, RECT *Rect, bool UseGdiPlus = false ) InflateRect(Rect, g_PenWidth / 2, g_PenWidth / 2); isBlur = true; } + OutputDebug(L"Draw shape: highlight: %d pbrush: %d\n", PEN_COLOR_HIGHLIGHT(g_PenColor), pBrush != NULL); switch (Shape) { case DRAW_RECTANGLE: @@ -2920,7 +2932,7 @@ void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height { int x, y; RECT rc; - int invWidth = g_PenWidth; + int invWidth = g_PenWidth + CURSOR_SAVE_MARGIN; if( DrawHighlightedCursor( zoomLevel, width, height ) ) { @@ -2945,7 +2957,7 @@ void InvalidateCursorMoveArea( HWND hWnd, float zoomLevel, int width, int height void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt ) { OutputDebug( L"SaveCursorArea\n"); - int penWidth = g_PenWidth + 2; + int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN; BitBlt( hDcTarget, 0, 0, penWidth +CURSOR_ARM_LENGTH*2, penWidth +CURSOR_ARM_LENGTH*2, hDcSource, static_cast (pt.x- penWidth /2)-CURSOR_ARM_LENGTH, static_cast(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, SRCCOPY|CAPTUREBLT ); @@ -2959,7 +2971,7 @@ void SaveCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt ) void RestoreCursorArea( HDC hDcTarget, HDC hDcSource, POINT pt ) { OutputDebug( L"RestoreCursorArea\n"); - int penWidth = g_PenWidth + 2; + int penWidth = g_PenWidth + CURSOR_SAVE_MARGIN; BitBlt( hDcTarget, static_cast(pt.x- penWidth /2)-CURSOR_ARM_LENGTH, static_cast(pt.y- penWidth /2)-CURSOR_ARM_LENGTH, penWidth +CURSOR_ARM_LENGTH*2, penWidth + CURSOR_ARM_LENGTH*2, hDcSource, 0, 0, SRCCOPY|CAPTUREBLT ); @@ -4178,6 +4190,11 @@ LRESULT APIENTRY MainWndProc( } #endif } + OutputDebug(L"LIVEDRAW SMOOTHING: %d\n", g_SmoothImage); + if (!pMagSetLensUseBitmapSmoothing(g_hWndLiveZoomMag, g_SmoothImage)) + { + OutputDebug(L"MagSetLensUseBitmapSmoothing failed: %d\n", GetLastError()); + } if ( g_RecordToggle ) { @@ -5296,6 +5313,8 @@ LRESULT APIENTRY MainWndProc( if( g_Drawing ) { + OutputDebug(L"Mousemove: Drawing\n"); + POINT currentPt; // Are we in pen mode on a tablet? @@ -5334,7 +5353,15 @@ LRESULT APIENTRY MainWndProc( } else { - DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle ); + if (PEN_COLOR_HIGHLIGHT(g_PenColor)) + { + // copy original bitmap to screen bitmap to erase previous highlight + BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth, bmp.bmHeight, drawUndoList->hDc, 0, 0, SRCCOPY | CAPTUREBLT); + } + else + { + DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor)); + } } } @@ -5380,7 +5407,7 @@ LRESULT APIENTRY MainWndProc( g_rcRectangle.top != g_rcRectangle.bottom) { // Draw the new target rectangle. - DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle); + DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle, PEN_COLOR_HIGHLIGHT(g_PenColor)); OutputDebug(L"SHAPE: (%d, %d) - (%d, %d)\n", g_rcRectangle.left, g_rcRectangle.top, g_rcRectangle.right, g_rcRectangle.bottom); } @@ -5418,9 +5445,6 @@ LRESULT APIENTRY MainWndProc( Gdiplus::BitmapData* lineData = LockGdiPlusBitmap(lineBitmap); BYTE* pPixels = static_cast(lineData->Scan0); - // Copy the contents of the screen bitmap to the temporary bitmap - GetOldestUndo(drawUndoList); - // Create a GDI bitmap that's the size of the lineBounds rectangle Gdiplus::Bitmap *blurBitmap = CreateGdiplusBitmap( hdcScreenCompat, // oldestUndo->hDc, lineBounds.X, lineBounds.Y, lineBounds.Width, lineBounds.Height); @@ -5445,6 +5469,8 @@ LRESULT APIENTRY MainWndProc( } else if(PEN_COLOR_HIGHLIGHT(g_PenColor)) { + OutputDebug(L"HIGHLIGHT\n"); + // This is a highlighting pen color Gdiplus::Rect lineBounds = GetLineBounds(prevPt, currentPt, g_PenWidth); Gdiplus::Bitmap* lineBitmap = DrawBitmapLine(lineBounds, prevPt, currentPt, &pen); @@ -5784,26 +5810,30 @@ LRESULT APIENTRY MainWndProc( if( !g_DrawingShape ) { // If the point has changed, draw a line to it - if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam)) { - Gdiplus::Graphics dstGraphics(hdcScreenCompat); - if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0) + if (!PEN_COLOR_HIGHLIGHT(g_PenColor)) + { + if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam)) { - dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); + Gdiplus::Graphics dstGraphics(hdcScreenCompat); + if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0) + { + dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); + } + Gdiplus::Color color = ColorFromColorRef(g_PenColor); + Gdiplus::Pen pen(color, static_cast(g_PenWidth)); + Gdiplus::GraphicsPath path; + pen.SetLineJoin(Gdiplus::LineJoinRound); + path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam)); + dstGraphics.DrawPath(&pen, &path); + } + // Draw a dot at the current point, if the point hasn't changed + else + { + MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL); + LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam)); + InvalidateRect(hWnd, NULL, FALSE); } - Gdiplus::Color color = ColorFromColorRef(g_PenColor); - Gdiplus::Pen pen(color, static_cast(g_PenWidth)); - Gdiplus::GraphicsPath path; - pen.SetLineJoin(Gdiplus::LineJoinRound); - path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam)); - dstGraphics.DrawPath(&pen, &path); } - // Draw a dot at the current point, if the point hasn't changed - else { - MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL); - LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam)); - InvalidateRect(hWnd, NULL, FALSE); - } - prevPt.x = LOWORD( lParam ); prevPt.y = HIWORD( lParam ); @@ -5818,8 +5848,11 @@ LRESULT APIENTRY MainWndProc( g_rcRectangle.left != g_rcRectangle.right ) { // erase previous - SetROP2(hdcScreenCompat, R2_NOTXORPEN); - DrawShape( g_DrawingShape, hdcScreenCompat, &g_rcRectangle ); + if (!PEN_COLOR_HIGHLIGHT(g_PenColor)) + { + SetROP2(hdcScreenCompat, R2_NOTXORPEN); + DrawShape(g_DrawingShape, hdcScreenCompat, &g_rcRectangle); + } // Draw the final shape HBRUSH hBrush = static_cast(GetStockObject( NULL_BRUSH )); @@ -6185,8 +6218,14 @@ LRESULT APIENTRY MainWndProc( SetStretchBltMode( hInterimSaveDc, HALFTONE ); SetStretchBltMode( hSaveDc, HALFTONE ); #else - SetStretchBltMode( hInterimSaveDc, COLORONCOLOR ); - SetStretchBltMode( hSaveDc, COLORONCOLOR ); + // Use HALFTONE for better quality when smooth image is enabled + if (g_SmoothImage) { + SetStretchBltMode( hInterimSaveDc, HALFTONE ); + SetStretchBltMode( hSaveDc, HALFTONE ); + } else { + SetStretchBltMode( hInterimSaveDc, COLORONCOLOR ); + SetStretchBltMode( hSaveDc, COLORONCOLOR ); + } #endif StretchBlt( hInterimSaveDc, 0, 0, @@ -6309,7 +6348,12 @@ LRESULT APIENTRY MainWndProc( #if SCALE_HALFTONE SetStretchBltMode( hSaveDc, HALFTONE ); #else - SetStretchBltMode( hSaveDc, COLORONCOLOR ); + // Use HALFTONE for better quality when smooth image is enabled + if (g_SmoothImage) { + SetStretchBltMode( hSaveDc, HALFTONE ); + } else { + SetStretchBltMode( hSaveDc, COLORONCOLOR ); + } #endif StretchBlt( hSaveDc, 0, 0, @@ -6646,8 +6690,8 @@ LRESULT APIENTRY MainWndProc( (float)x, (float)y, width/zoomLevel, height/zoomLevel ); } else { - // do a fast, less accurate render - SetStretchBltMode( hDc, HALFTONE ); + // do a fast, less accurate render (but use smooth if enabled) + SetStretchBltMode( hDc, g_SmoothImage ? HALFTONE : COLORONCOLOR ); StretchBlt( ps.hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, @@ -6660,7 +6704,12 @@ LRESULT APIENTRY MainWndProc( #if SCALE_HALFTONE SetStretchBltMode( hDc, zoomLevel == zoomTelescopeTarget ? HALFTONE : COLORONCOLOR ); #else - SetStretchBltMode( hDc, COLORONCOLOR ); + // Use HALFTONE for better quality when smooth image is enabled + if (g_SmoothImage) { + SetStretchBltMode( hDc, HALFTONE ); + } else { + SetStretchBltMode( hDc, COLORONCOLOR ); + } #endif StretchBlt( ps.hdc, 0, 0, @@ -6683,7 +6732,7 @@ LRESULT APIENTRY MainWndProc( BITMAP local_bmp; GetObject(g_hBackgroundBmp, sizeof(local_bmp), &local_bmp); - SetStretchBltMode( hdcScreenCompat, HALFTONE ); + SetStretchBltMode( hdcScreenCompat, g_SmoothImage ? HALFTONE : COLORONCOLOR ); if( g_BreakBackgroundStretch ) { StretchBlt( hdcScreenCompat, 0, 0, width, height, g_hDcBackgroundFile, 0, 0, local_bmp.bmWidth, local_bmp.bmHeight, SRCCOPY|CAPTUREBLT ); @@ -6842,7 +6891,6 @@ LRESULT CALLBACK LiveZoomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, g_hInstance, NULL ); } - ShowWindow( hWnd, SW_SHOW ); InvalidateRect( g_hWndLiveZoomMag, NULL, TRUE ); @@ -7555,6 +7603,10 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance "MagSetWindowTransform" ); pMagSetFullscreenTransform = (type_pMagSetFullscreenTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetFullscreenTransform"); + pMagSetFullscreenUseBitmapSmoothing = (type_MagSetFullscreenUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), + "MagSetFullscreenUseBitmapSmoothing"); + pMagSetLensUseBitmapSmoothing = (type_pMagSetLensUseBitmapSmoothing)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), + "MagSetLensUseBitmapSmoothing"); pMagSetInputTransform = (type_pMagSetInputTransform)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), "MagSetInputTransform"); pMagShowSystemCursor = (type_pMagShowSystemCursor)GetProcAddress(LoadLibrarySafe(L"magnification.dll", DLL_LOAD_LOCATION_SYSTEM), diff --git a/src/modules/ZoomIt/ZoomIt/resource.h b/src/modules/ZoomIt/ZoomIt/resource.h index c4c0cfad87..568eebda4b 100644 --- a/src/modules/ZoomIt/ZoomIt/resource.h +++ b/src/modules/ZoomIt/ZoomIt/resource.h @@ -95,6 +95,7 @@ #define IDC_COPYRIGHT 1075 #define IDC_PEN_WIDTH 1105 #define IDC_TIMER 1106 +#define IDC_SMOOTH_IMAGE 1107 #define IDC_SAVE 40002 #define IDC_COPY 40004 #define IDC_RECORD 40006 @@ -109,7 +110,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 118 #define _APS_NEXT_COMMAND_VALUE 40013 -#define _APS_NEXT_CONTROL_VALUE 1076 +#define _APS_NEXT_CONTROL_VALUE 1078 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/settings-ui/Settings.UI.Library/ZoomItProperties.cs b/src/settings-ui/Settings.UI.Library/ZoomItProperties.cs index aaaa80f9f7..9325907a72 100644 --- a/src/settings-ui/Settings.UI.Library/ZoomItProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ZoomItProperties.cs @@ -81,6 +81,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("AnimnateZoom")] public BoolProperty AnimateZoom { get; set; } + public BoolProperty SmoothImage { get; set; } + public IntProperty ZoominSliderLevel { get; set; } public IntProperty RecordScaling { get; set; } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml index cb1c8605eb..9b938d14a2 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ZoomItPage.xaml @@ -54,6 +54,9 @@ + + + Animate zoom in and zoom out + + Smooth zoomed image + Specify the initial level of magnification when zooming in diff --git a/src/settings-ui/Settings.UI/ViewModels/ZoomItViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ZoomItViewModel.cs index 8a277bbfa5..9e99c0e22f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ZoomItViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ZoomItViewModel.cs @@ -197,6 +197,20 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public bool SmoothImage + { + get => _zoomItSettings.Properties.SmoothImage.Value; + set + { + if (_zoomItSettings.Properties.SmoothImage.Value != value) + { + _zoomItSettings.Properties.SmoothImage.Value = value; + OnPropertyChanged(nameof(SmoothImage)); + NotifySettingsChanged(); + } + } + } + public int ZoominSliderLevel { get => _zoomItSettings.Properties.ZoominSliderLevel.Value;