diff --git a/plug-ins/screenshot/magnification-api-win32.h b/plug-ins/screenshot/magnification-api-win32.h new file mode 100644 index 0000000000..8c699e32d8 --- /dev/null +++ b/plug-ins/screenshot/magnification-api-win32.h @@ -0,0 +1,154 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Magnification-win32.h + * Copyright (C) 2018 Gil Eliyahu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#pragma region Desktop Family +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#ifndef __wincodec_h__ +#include +#endif + +#define WC_MAGNIFIERA "Magnifier" +#define WC_MAGNIFIERW L"Magnifier" + +#ifdef UNICODE +#define WC_MAGNIFIER WC_MAGNIFIERW +#else +#define WC_MAGNIFIER WC_MAGNIFIERA +#endif + +#else +#define WC_MAGNIFIER "Magnifier" +#endif + +/* Magnifier Window Styles */ +#define MS_SHOWMAGNIFIEDCURSOR 0x0001L +#define MS_CLIPAROUNDCURSOR 0x0002L +#define MS_INVERTCOLORS 0x0004L + + +/* Filter Modes */ +#define MW_FILTERMODE_EXCLUDE 0 +#define MW_FILTERMODE_INCLUDE 1 + + +/* Structures */ +typedef struct tagMAGTRANSFORM +{ + float v[3][3]; +} MAGTRANSFORM, *PMAGTRANSFORM; + +typedef struct tagMAGIMAGEHEADER +{ + UINT width; + UINT height; + WICPixelFormatGUID format; + UINT stride; + UINT offset; + SIZE_T cbSize; +} MAGIMAGEHEADER, *PMAGIMAGEHEADER; + +typedef struct tagMAGCOLOREFFECT +{ + float transform[5][5]; +} MAGCOLOREFFECT, *PMAGCOLOREFFECT; + + +/* Proptypes for the public functions */ +typedef BOOL(WINAPI* MAGINITIALIZE)(); +MAGINITIALIZE MagInitialize; + +typedef BOOL(WINAPI* MAGUNINITIALIZE)(); +MAGUNINITIALIZE MagUninitialize; + +typedef BOOL(WINAPI* MAGSETWINDOWSOURCE)(HWND, RECT); +MAGSETWINDOWSOURCE MagSetWindowSource; + +typedef BOOL(WINAPI* MAGSETWINDOWFILTERLIST)(HWND, DWORD, int, HWND*); +MAGSETWINDOWFILTERLIST MagSetWindowFilterList; + +typedef BOOL(CALLBACK* MagImageScalingCallback)(HWND hwnd, void * srcdata, MAGIMAGEHEADER srcheader, void * destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty); + +typedef BOOL(WINAPI* MAGSETIMAGESCALINGCALLBACK)(HWND, MagImageScalingCallback); +MAGSETIMAGESCALINGCALLBACK MagSetImageScalingCallback; + + +/* Library DLL */ +static HINSTANCE magnificationLibrary; + + +void UnLoadMagnificationLibrary(void); +BOOL LoadMagnificationLibrary(void); + + +void UnLoadMagnificationLibrary() +{ + if (!magnificationLibrary) return; + FreeLibrary(magnificationLibrary); +} + + + +BOOL LoadMagnificationLibrary() +{ + if (magnificationLibrary) return TRUE; + + magnificationLibrary = LoadLibrary("Magnification"); + if (!magnificationLibrary) return FALSE; + + MagInitialize = (MAGINITIALIZE)GetProcAddress(magnificationLibrary,"MagInitialize"); + if (!MagInitialize) + { + UnLoadMagnificationLibrary(); + return FALSE; + } + + MagUninitialize = (MAGUNINITIALIZE)GetProcAddress(magnificationLibrary, "MagUninitialize"); + if (!MagUninitialize) + { + UnLoadMagnificationLibrary(); + return FALSE; + } + + MagSetWindowSource = (MAGSETWINDOWSOURCE)GetProcAddress(magnificationLibrary, "MagSetWindowSource"); + if (!MagSetWindowSource) + { + UnLoadMagnificationLibrary(); + return FALSE; + } + + MagSetWindowFilterList = (MAGSETWINDOWFILTERLIST)GetProcAddress(magnificationLibrary, "MagSetWindowFilterList"); + if (!MagSetWindowFilterList) + { + UnLoadMagnificationLibrary(); + return FALSE; + } + + MagSetImageScalingCallback = (MAGSETIMAGESCALINGCALLBACK)GetProcAddress(magnificationLibrary, "MagSetImageScalingCallback"); + if (!MagSetImageScalingCallback) + { + UnLoadMagnificationLibrary(); + return FALSE; + } + + return TRUE; +} diff --git a/plug-ins/screenshot/screenshot-win32.c b/plug-ins/screenshot/screenshot-win32.c index d3075a1c33..d2ccaee74c 100644 --- a/plug-ins/screenshot/screenshot-win32.c +++ b/plug-ins/screenshot/screenshot-win32.c @@ -39,6 +39,7 @@ #include "screenshot-win32-resource.h" #include "libgimp/stdplugins-intl.h" +#include "magnification-api-win32.h" /* * Application definitions @@ -59,24 +60,43 @@ BOOL InitInstance (HINSTANCE hInstance, int winsnapWinMain (void); /* File variables */ -static int captureType; -static char buffer[512]; -static guchar *capBytes = NULL; -static HWND mainHwnd = NULL; -static HINSTANCE hInst = NULL; -static HCURSOR selectCursor = 0; -static ICONINFO iconInfo; +static int captureType; +static char buffer[512]; +static guchar *capBytes = NULL; +static HWND mainHwnd = NULL; +static HINSTANCE hInst = NULL; +static HCURSOR selectCursor = 0; +static ICONINFO iconInfo; +static MAGIMAGEHEADER returnedSrcheader; +static RECT *rectScreens = NULL; +static int rectScreensCount = 0; static gint32 *image_id; -static void sendBMPToGimp (HBITMAP hBMP, - HDC hDC, - RECT rect); -static void doWindowCapture (void); -static int doCapture (HWND selectedHwnd); +static void sendBMPToGimp (HBITMAP hBMP, + HDC hDC, + RECT rect); +static void doWindowCapture (void); +static int doCapture (HWND selectedHwnd); +static BOOL isWindowIsAboveCaptureRegion (HWND hwndWindow, + RECT rectCapture); +static int doCaptureMagnificationAPI (HWND selectedHwnd, + RECT rect); +static void doCaptureMagnificationAPI_callback (HWND hwnd, + void *srcdata, + MAGIMAGEHEADER srcheader, + void *destdata, + MAGIMAGEHEADER destheader, + RECT unclipped, + RECT clipped, + HRGN dirty); +static int doCaptureBitBlt (HWND selectedHwnd, + RECT rect); -BOOL CALLBACK dialogProc(HWND, UINT, WPARAM, LPARAM); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +BOOL CALLBACK dialogProc (HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); + +static BOOL CALLBACK doCaptureMagnificationAPI_MonitorEnumProc (HMONITOR, HDC, LPRECT, LPARAM); /* Data structure holding data between runs */ typedef struct { @@ -223,6 +243,26 @@ flipRedAndBlueBytes (int width, } } +/* + * rgbaToRgbBytes + * + * Convert rgba array to rgb array + */ +static void +rgbaToRgbBytes (guchar *rgbBufp, + guchar *rgbaBufp, + int rgbaBufSize) +{ + int rgbPoint = 0, rgbaPoint; + + for (rgbaPoint = 0; rgbaPoint < rgbaBufSize; rgbaPoint += 4) + { + rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint]; + rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 1]; + rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 2]; + } +} + /* * sendBMPToGIMP * @@ -421,6 +461,7 @@ primDoWindowCapture (HDC hdcWindow, return hbmCopy; } + /* * doCapture * @@ -431,37 +472,43 @@ primDoWindowCapture (HDC hdcWindow, static int doCapture (HWND selectedHwnd) { - HDC hdcSrc; - HDC hdcCompat; - HWND oldForeground; - RECT rect; - HBITMAP hbm; + RECT rect; /* Try and get everything out of the way before the * capture. */ Sleep (500 + winsnapvals.delay * 1000); - /* Get the device context for the whole screen - * even if we just want to capture a window. - * this will allow to capture applications that - * don't render to their main window's device - * context (e.g. browsers). - */ - hdcSrc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL); - /* Are we capturing a window or the whole screen */ if (selectedHwnd) { - /* Set to foreground window */ - oldForeground = GetForegroundWindow (); - SetForegroundWindow (selectedHwnd); - BringWindowToTop (selectedHwnd); + if (!GetWindowRect (selectedHwnd, &rect)) + /* For some reason it works only if we return here TRUE. strange... */ + return TRUE; - Sleep (500); + /* First try to capture the window with the magnification api. + * This will solve the bug https://bugzilla.gnome.org/show_bug.cgi?id=793722/ + */ - /* Build a region for the capture */ - GetWindowRect (selectedHwnd, &rect); + if (!doCaptureMagnificationAPI (selectedHwnd, rect)) + { + /* If for some reason this capture method failed then + * capture the window with the normal method: + */ + + /* Get the device context for the whole screen + * even if we just want to capture a window. + * this will allow to capture applications that + * don't render to their main window's device + * context (e.g. browsers). + */ + SetForegroundWindow (selectedHwnd); + BringWindowToTop (selectedHwnd); + + return doCaptureBitBlt (selectedHwnd, rect); + } + + return TRUE; } else @@ -471,8 +518,31 @@ doCapture (HWND selectedHwnd) rect.bottom = GetSystemMetrics (SM_YVIRTUALSCREEN) + GetSystemMetrics (SM_CYVIRTUALSCREEN); rect.left = GetSystemMetrics (SM_XVIRTUALSCREEN); rect.right = GetSystemMetrics (SM_XVIRTUALSCREEN) + GetSystemMetrics (SM_CXVIRTUALSCREEN); + + return doCaptureBitBlt (selectedHwnd, rect); + } + return FALSE; /* we should never get here... */ +} + +static int +doCaptureBitBlt (HWND selectedHwnd, + RECT rect) +{ + + HDC hdcSrc; + HDC hdcCompat; + HWND oldForeground; + HBITMAP hbm; + /* Get the device context for the whole screen + * even if we just want to capture a window. + * this will allow to capture applications that + * don't render to their main window's device + * context (e.g. browsers). + */ + hdcSrc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL); + if (!hdcSrc) { formatWindowsError(buffer, sizeof buffer); @@ -495,18 +565,212 @@ doCapture (HWND selectedHwnd) /* Release the device context */ ReleaseDC(selectedHwnd, hdcSrc); - /* Replace the previous foreground window */ - if (selectedHwnd && oldForeground) - SetForegroundWindow (oldForeground); + if (hbm == NULL) return FALSE; - /* Send the bitmap - * TODO: Change this - */ - if (hbm != NULL) + sendBMPToGimp (hbm, hdcCompat, rect); + + return TRUE; + +} + +static void +doCaptureMagnificationAPI_callback (HWND hwnd, + void *srcdata, + MAGIMAGEHEADER srcheader, + void *destdata, + MAGIMAGEHEADER destheader, + RECT unclipped, + RECT clipped, + HRGN dirty) +{ + if (!srcdata) return; + capBytes = (guchar*) malloc (sizeof (guchar)*srcheader.cbSize); + rgbaToRgbBytes (capBytes, srcdata, srcheader.cbSize); + returnedSrcheader = srcheader; +} + + + +static BOOL +isWindowIsAboveCaptureRegion (HWND hwndWindow, + RECT rectCapture) +{ + RECT rectWindow; + ZeroMemory (&rectWindow, sizeof (RECT)); + if (!GetWindowRect (hwndWindow, &rectWindow)) return FALSE; + if ( + ( + (rectWindow.left >= rectCapture.left && rectWindow.left < rectCapture.right) + || + (rectWindow.right <= rectCapture.right && rectWindow.right > rectCapture.left) + || + (rectWindow.left <= rectCapture.left && rectWindow.right >= rectCapture.right) + ) + && + ( + (rectWindow.top >= rectCapture.top && rectWindow.top < rectCapture.bottom) + || + (rectWindow.bottom <= rectCapture.bottom && rectWindow.bottom > rectCapture.top) + || + (rectWindow.top <= rectCapture.top && rectWindow.bottom >= rectCapture.bottom) + ) + ) + return TRUE; + else + return FALSE; +} + +static BOOL CALLBACK +doCaptureMagnificationAPI_MonitorEnumProc (HMONITOR hMonitor, + HDC hdcMonitor, + LPRECT lprcMonitor, + LPARAM dwData) +{ + if (!lprcMonitor) return FALSE; + + if (!rectScreens) + rectScreens = (RECT*)malloc(sizeof(RECT)*(rectScreensCount+1)); + else + rectScreens = (RECT*)realloc(rectScreens,sizeof(RECT)*(rectScreensCount+1)); + + if (rectScreens == NULL) return FALSE; + + rectScreens[rectScreensCount] = *lprcMonitor; + + rectScreensCount++; + + return TRUE; +} + +static BOOL +doCaptureMagnificationAPI (HWND selectedHwnd, + RECT rect) +{ + HWND hwndMag; + HWND hwndHost; + HWND nextWindow; + HWND excludeWins[24]; + int excludeWinsCount = 0; + WINDOWPLACEMENT windowplacment; + int i; + int xCenter; + int yCenter; + + if (!LoadMagnificationLibrary ()) return FALSE; + + if (!MagInitialize ()) return FALSE; + + /* If the window is maximized then we need to fix the rect variable */ + ZeroMemory (&windowplacment, sizeof (WINDOWPLACEMENT)); + if (GetWindowPlacement (selectedHwnd, &windowplacment) && windowplacment.showCmd == SW_SHOWMAXIMIZED) + { + /* if this is not the first time we call this function for some reason then we reset the rectScreens array */ + if (rectScreensCount) { - sendBMPToGimp (hbm, hdcCompat, rect); + free (rectScreens); + rectScreens = NULL; + rectScreensCount = 0; } + /* Get the screens rects */ + EnumDisplayMonitors (NULL, NULL, doCaptureMagnificationAPI_MonitorEnumProc, NULL); + + + /* If for some reason the array size is 0 then we fill it with the desktop rect */ + if (!rectScreensCount) + { + rectScreens = (RECT*)malloc (sizeof(RECT)); + if (!GetWindowRect (GetDesktopWindow (),rectScreens)) + /* error: could not get rect screens */ + return FALSE; + + rectScreensCount = 1; + } + + xCenter = rect.left + (rect.right - rect.left) / 2; + yCenter = rect.top + (rect.bottom - rect.top) / 2; + + /* find on which screen the window exist */ + for (i = 0; i < rectScreensCount; i++) + if (xCenter > rectScreens[i].left && xCenter < rectScreens[i].right && + yCenter > rectScreens[i].top && yCenter < rectScreens[i].bottom) + break; + + if (i == rectScreensCount) + /* Error: did not found on which screen the window exist */ + return FALSE; + + if (rectScreens[i].left > rect.left) rect.left = rectScreens[i].left; + if (rectScreens[i].right < rect.right) rect.right = rectScreens[i].right; + if (rectScreens[i].top > rect.top) rect.top = rectScreens[i].top; + if (rectScreens[i].bottom < rect.bottom) rect.bottom = rectScreens[i].bottom; + + } + + + rect.right = rect.left + ROUND4(rect.right - rect.left); + + + /* Create the host window that will store the mag child window */ + hwndHost = CreateWindowEx (0x08000000 | 0x080000 | 0x80 | 0x20, APP_NAME, NULL, 0x80000000, + NULL, NULL, NULL, NULL, NULL, NULL, GetModuleHandle (NULL), NULL); + + if (!hwndHost) + { + MagUninitialize (); + return FALSE; + } + + SetLayeredWindowAttributes (hwndHost, (COLORREF)0, (BYTE)255, (DWORD)0x02); + + /* Create the mag child window inside the host window */ + hwndMag = CreateWindow (WC_MAGNIFIER, TEXT ("MagnifierWindow"), + WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR*/ /*| WS_VISIBLE*/, + NULL, NULL, rect.right - rect.left, rect.bottom - rect.top, + hwndHost, NULL, GetModuleHandle (NULL), NULL); + + /* Set the callback function that will be called by the api to get the pixels */ + if (!MagSetImageScalingCallback (hwndMag, (MagImageScalingCallback)doCaptureMagnificationAPI_callback)) + { + DestroyWindow (hwndHost); + MagUninitialize (); + return FALSE; + } + + /* Add only windows that above the target window */ + for (nextWindow = GetNextWindow (selectedHwnd, GW_HWNDPREV); nextWindow != NULL; nextWindow = GetNextWindow (nextWindow, GW_HWNDPREV)) + if (isWindowIsAboveCaptureRegion (nextWindow, rect)) + { + excludeWins[excludeWinsCount++] = nextWindow; + /* This api can't work with more than 24 windows. we stop on the 24 window */ + if (excludeWinsCount >= 24) break; + } + + if (excludeWinsCount) + MagSetWindowFilterList (hwndMag, MW_FILTERMODE_EXCLUDE, excludeWinsCount, excludeWins); + + /* Call the api to capture the window */ + capBytes = NULL; + + if (!MagSetWindowSource (hwndMag, rect) || !capBytes) + { + DestroyWindow (hwndHost); + MagUninitialize (); + return FALSE; + } + + /* Just to be safe, we reset the image size the size dimensions that the api gave*/ + rect.left = 0; + rect.top = 0; + rect.right = ROUND4(returnedSrcheader.width); + rect.bottom = returnedSrcheader.height; + + /* Send it to Gimp */ + sendBMPToGimp (NULL, NULL, rect); + + DestroyWindow (hwndHost); + MagUninitialize (); + return TRUE; } @@ -868,6 +1132,9 @@ BOOL InitInstance (HINSTANCE hInstance, int nCmdShow) { + /* This line fix bug: https://bugzilla.gnome.org/show_bug.cgi?id=796121#c4 */ + SetProcessDPIAware(); + /* Create our window */ mainHwnd = CreateWindow (APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,