Issue #1458 - Small border on side of single-window screenshot on...

... Windows.

Reviewer note (Jehan): I have not built on Windows because I need to
refresh my crossbuild environment, but it looks sane enough, and Gil
previously did good patches. I push as-is, hoping it still builds fine
on Windows. :-)
This commit is contained in:
Gil Eliyahu 2018-05-20 18:38:36 +03:00 committed by Jehan
parent 0c3659c5f1
commit 2caa400a96
2 changed files with 186 additions and 95 deletions

View file

@ -0,0 +1,72 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* dwm-api-win32.h
* Copyright (C) 2018 Gil Eliyahu <gileli121@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
void UnloadRequiredDwmFunctions (void);
BOOL LoadRequiredDwmFunctions (void);
typedef HRESULT (WINAPI* DWMGETWINDOWATTRIBUTE)(HWND, DWORD, _Out_ PVOID, DWORD);
DWMGETWINDOWATTRIBUTE DwmGetWindowAttribute;
typedef enum _DWMWINDOWATTRIBUTE {
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
} DWMWINDOWATTRIBUTE;
static HMODULE dwmApi = NULL;
void
UnloadRequiredDwmFunctions (void)
{
if (! dwmApi) return;
FreeLibrary(dwmApi);
dwmApi = NULL;
}
BOOL
LoadRequiredDwmFunctions (void)
{
if (dwmApi) return TRUE;
dwmApi = LoadLibrary ("dwmapi");
if (! dwmApi) return FALSE;
DwmGetWindowAttribute = (DWMGETWINDOWATTRIBUTE) GetProcAddress (dwmApi, "DwmGetWindowAttribute");
if (! DwmGetWindowAttribute)
{
UnloadRequiredDwmFunctions ();
return FALSE;
}
return TRUE;
}

View file

@ -5,6 +5,7 @@
* Copyright 1998-2007 Sven Neumann <sven@gimp.org>
* Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
* Copyright 2012 Simone Karin Lehmann - OS X patches
* Copyright 2018 Gil Eliyahu <gileli121@gmail.com>
*
* 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
@ -49,6 +50,7 @@
#include "libgimp/stdplugins-intl.h"
#include "magnification-api-win32.h"
#include "dwm-api-win32.h"
/*
* Application definitions
@ -104,8 +106,6 @@ static gboolean doCaptureBitBlt (HWND selectedHwnd
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 {
gint root;
@ -469,6 +469,112 @@ primDoWindowCapture (HDC hdcWindow,
return hbmCopy;
}
static BOOL CALLBACK
GetAccurateWindowRect_MonitorEnumProc (HMONITOR hMonitor,
HDC hdcMonitor,
LPRECT lprcMonitor,
LPARAM dwData)
{
RECT **rectScreens = (RECT**) dwData;
if (!lprcMonitor) return FALSE;
if (! (*rectScreens))
*rectScreens = (RECT*) g_malloc (sizeof (RECT)*(rectScreensCount+1));
else
*rectScreens = (RECT*) g_realloc (rectScreens,
sizeof (RECT)*(rectScreensCount+1));
if (*rectScreens == NULL)
{
rectScreensCount = 0;
return FALSE;
}
(*rectScreens)[rectScreensCount] = *lprcMonitor;
rectScreensCount++;
return TRUE;
}
static BOOL
GetAccurateWindowRect (HWND hwndTarget,
RECT *outRect)
{
HRESULT dwmResult;
WINDOWPLACEMENT windowplacment;
ZeroMemory (outRect, sizeof (RECT));
/* First we try to get the rect using the dwm api. it will also avoid us from doing more ugly fixes when the window is maximized */
if (LoadRequiredDwmFunctions ())
{
dwmResult = DwmGetWindowAttribute (hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, (PVOID) outRect, sizeof (RECT));
UnloadRequiredDwmFunctions ();
if (dwmResult == S_OK) return TRUE;
}
/* In this case, we did not got the rect from the dwm api so we try to get the rect with the normal function */
if (GetWindowRect (hwndTarget, outRect))
{
/* If the window is maximized then we need and can fix the rect variable (we need to do this if the rect not comming from dwm api) */
ZeroMemory (&windowplacment, sizeof (WINDOWPLACEMENT));
if (GetWindowPlacement (hwndTarget, &windowplacment) && windowplacment.showCmd == SW_SHOWMAXIMIZED)
{
RECT *rectScreens = NULL;
/* if this is not the first time we call this function for some
* reason then we reset the rectScreens count
*/
if (rectScreensCount)
rectScreensCount = 0;
/* Get the screens rects */
EnumDisplayMonitors (NULL, NULL, GetAccurateWindowRect_MonitorEnumProc,
(LPARAM) &rectScreens);
/* If for some reason the array size is 0 then we fill it with the desktop rect */
if (! rectScreensCount)
{
rectScreens = (RECT*) g_malloc (sizeof (RECT));
if (! GetWindowRect (GetDesktopWindow (), rectScreens))
{
/* error: could not get rect screens */
g_free (rectScreens);
return FALSE;
}
rectScreensCount = 1;
}
int xCenter = outRect->left + (outRect->right - outRect->left) / 2;
int yCenter = outRect->top + (outRect->bottom - outRect->top) / 2;
/* find on which screen the window exist */
int i;
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 > outRect->left) outRect->left = rectScreens[i].left;
if (rectScreens[i].right < outRect->right) outRect->right = rectScreens[i].right;
if (rectScreens[i].top > outRect->top) outRect->top = rectScreens[i].top;
if (rectScreens[i].bottom < outRect->bottom) outRect->bottom = rectScreens[i].bottom;
g_free (rectScreens);
}
return TRUE;
}
return FALSE;
}
/*
* doCapture
@ -490,7 +596,7 @@ doCapture (HWND selectedHwnd)
/* Are we capturing a window or the whole screen */
if (selectedHwnd)
{
if (!GetWindowRect (selectedHwnd, &rect))
if (! GetAccurateWindowRect (selectedHwnd, &rect))
/* For some reason it works only if we return here TRUE. strange... */
return TRUE;
@ -598,8 +704,6 @@ doCaptureMagnificationAPI_callback (HWND hwnd,
returnedSrcheader = srcheader;
}
static BOOL
isWindowIsAboveCaptureRegion (HWND hwndWindow,
RECT rectCapture)
@ -629,32 +733,6 @@ isWindowIsAboveCaptureRegion (HWND hwndWindow,
return FALSE;
}
static BOOL CALLBACK
doCaptureMagnificationAPI_MonitorEnumProc (HMONITOR hMonitor,
HDC hdcMonitor,
LPRECT lprcMonitor,
LPARAM dwData)
{
RECT **rectScreens = (RECT**) dwData;
if (!lprcMonitor) return FALSE;
if (! (*rectScreens))
*rectScreens = (RECT*) g_malloc (sizeof (RECT)*(rectScreensCount+1));
else
*rectScreens = (RECT*) g_realloc (rectScreens,
sizeof (RECT)*(rectScreensCount+1));
if (*rectScreens == NULL)
return FALSE;
(*rectScreens)[rectScreensCount] = *lprcMonitor;
rectScreensCount++;
return TRUE;
}
static gboolean
doCaptureMagnificationAPI (HWND selectedHwnd,
RECT rect)
@ -663,68 +741,15 @@ doCaptureMagnificationAPI (HWND selectedHwnd,
HWND hwndHost;
HWND nextWindow;
HWND excludeWins[24];
RECT round4Rect;
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)
{
RECT *rectScreens = NULL;
/* if this is not the first time we call this function for some
* reason then we reset the rectScreens count
*/
if (rectScreensCount)
rectScreensCount = 0;
/* Get the screens rects */
EnumDisplayMonitors (NULL, NULL, doCaptureMagnificationAPI_MonitorEnumProc,
(LPARAM) &rectScreens);
/* If for some reason the array size is 0 then we fill it with the desktop rect */
if (! rectScreensCount)
{
rectScreens = (RECT*) g_malloc (sizeof (RECT));
if (! GetWindowRect (GetDesktopWindow (), rectScreens))
{
/* error: could not get rect screens */
g_free (rectScreens);
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;
g_free (rectScreens);
}
rect.right = rect.left + ROUND4(rect.right - rect.left);
round4Rect = rect;
round4Rect.right = round4Rect.left + ROUND4(round4Rect.right - round4Rect.left);
/* Create the host window that will store the mag child window */
hwndHost = CreateWindowEx (0x08000000 | 0x080000 | 0x80 | 0x20, APP_NAME, NULL, 0x80000000,
@ -741,7 +766,7 @@ doCaptureMagnificationAPI (HWND selectedHwnd,
/* Create the mag child window inside the host window */
hwndMag = CreateWindow (WC_MAGNIFIER, TEXT ("MagnifierWindow"),
WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR*/ /*| WS_VISIBLE*/,
0, 0, rect.right - rect.left, rect.bottom - rect.top,
0, 0, round4Rect.right - round4Rect.left, round4Rect.bottom - round4Rect.top,
hwndHost, NULL, GetModuleHandle (NULL), NULL);
/* Set the callback function that will be called by the api to get the pixels */
@ -767,19 +792,13 @@ doCaptureMagnificationAPI (HWND selectedHwnd,
/* Call the api to capture the window */
capBytes = NULL;
if (!MagSetWindowSource (hwndMag, rect) || !capBytes)
if (!MagSetWindowSource (hwndMag, round4Rect) || !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);