[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 16, 2022 11:33 AM UTC
Owner: nobody
Windows by default performs GDI scaling for applications which are DPI unaware when using a display with device scaling set to more than 100 %.
My MFC app is DPI unaware. While text with SC_TECHNOLOGY_DEFAULT looks crisp with GDI scaling at 200 %, text looks very blurry when using DirectWrite as the rendering technology. Obviously Windows continues rendering text at 96 dpi and then scales this bitmap.
I've enhanced ScintillaWin::EnsureRenderTarget() to take the scale factor of the monitor into account:
#include <shellscalingapi.h>
#pragma comment(lib, "Shcore.lib")
void ScintillaWin::EnsureRenderTarget(HDC hdc) {
if (!renderTargetValid) {
DropRenderTarget();
renderTargetValid = true;
}
if (!pRenderTarget) {
HWND hw = MainHWND();
RECT rc;
::GetClientRect(hw, &rc);
float scaleFactor = 1.0f;
if (DPI_AWARENESS_UNAWARE == ::GetAwarenessFromDpiAwarenessContext(::GetWindowDpiAwarenessContext(hw)))
{
DEVICE_SCALE_FACTOR deviceScaleFactor;
if (S_OK == ::GetScaleFactorForMonitor(hCurrentMonitor, &deviceScaleFactor))
scaleFactor = deviceScaleFactor / 100.0f;
}
const auto size = D2D1::SizeU((UINT32)(scaleFactor * (rc.right - rc.left)), (UINT32)(scaleFactor * (rc.bottom - rc.top)));
const auto windowDpi = scaleFactor * dpi;
// Create a Direct2D render target.
D2D1_RENDER_TARGET_PROPERTIES drtp {};
drtp.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
drtp.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
drtp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
drtp.dpiX = drtp.dpiY = windowDpi;
drtp.usage = D2D1_RENDER_TARGET_USAGE_NONE;
drtp.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
...
This patch makes text looks crisp again. Alas, this is not the only place to make adjustments: the calltip appears slightly blurry and the autocompletion listbox looks very blurry. At a quick glance, I couldn't find similar Direct2D code for calltips and the listbox.
Is this the correct way to tackle the issue? What do you think?
Sent from sourceforge.net because scintill...@googlegroups.com is subscribed to https://sourceforge.net/p/scintilla/bugs/
To unsubscribe from further messages, a project admin can change settings at https://sourceforge.net/p/scintilla/admin/bugs/options. Or, if this is a mailing list, you can unsubscribe from the mailing list.
(not tested) What about leave dpiX and dpiY as zero, e.g. "Using Default DPI Settings":
https://docs.microsoft.com/en-us/windows/win32/api/d2d1/ns-d2d1-d2d1_render_target_properties#using-default-dpi-settings
Because of the app being DPI unaware, the DPI remains effectively at 96 dpi.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 16, 2022 12:01 PM UTC
Owner: nobody
Wouldn't you be better off making your app DPI-aware?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 16, 2022 12:04 PM UTC
Owner: nobody
After turning off DPI awareness in SciTE by editing the win32\SciTE.exe.manifest file, the results with the patch appear quite poor at 200% and 125% to me.


These could be because of something unusual in SciTE but it doesn't look like the proposal is a simple fix for all.
Since Scintilla still supports Windows XP and the GetWindowDpiAwarenessContext, GetAwarenessFromDpiAwarenessContext and GetScaleFactorForMonitor APIs are not available there, those calls should be enabled through (GetModuleHandle/LoadLibraryEx)+DLLFunction.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 16, 2022 09:53 PM UTC
Owner: nobody
You highly underestimate the work of making a nearly 30 year old MFC enterprise application with 500.000 lines of code DPI-aware.
If it were that simple, Microsoft would not have come up with the concept of Mixed-Mode DPI Scaling and DPI-aware APIs either.
Taken from High DPI Desktop Application Development on Windows:
When updating an application to support per-monitor DPI awareness, it can sometimes become impractical or impossible to update every window in the application in one go. This can simply be due to the time and effort required to update and test all UI, or because you do not own all of the UI code that you need to run (if your application perhaps loads third-party UI). In these situations, Windows offers a way to ease into the world of per-monitor awareness by letting you run some of your application windows (top-level only) in their original DPI-awareness mode while you focus your time and energy updating the more important parts of your UI.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 16, 2022 11:32 PM UTC
Owner: nobody
My patch gave me excellent results in my app. I will take a look at Scite tomorrow to see why things don't work out there.
My patch was not a 100% "turn in as is" patch. I wanted to make you aware of the fact that DirectWrite looks very blurry when used with a GDI scaled app. I'm also aware of the fact those newer APIs are not available on all versions of Windows that Scintilla supports. I first wanted to know whether this would be the correct approach that you approve of before providing you with code to check in.
Would you please point me to the pieces of code where the calltip and the listbox are setup Direct2D wise? They obviously also need some size and DPI treatment as they looked (very) blurry while buffer text looked awesome.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 17, 2022 06:33 AM UTC
Owner: nobody
screenshots for SciTE seems does not has GDI scaling enabled in manifest.
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">
<gdiScaling>true</gdiScaling>
</asmv3:windowsSettings>
</asmv3:application>
I think function like FLOAT GetDPIUnawareScaleFactor(HWND hwnd, HMONITOR monitor) noexcept could be added into PlatWin.cxx (where shcore.dll is loaded). however it appears to me the improvement of the code is not much but I can't test higher scaling like 200%.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 17, 2022 06:42 AM UTC
Owner: nobody
where the calltip and the listbox are setup Direct2D
Autocompletion lists draw text with GDI, not DirectWrite but they do draw images with Direct2D.
All Direct2D setup creates a render target so grepping Create.*RenderTarget will find them. For calltips the call is in ScintillaWin::CTWndProc and for autocompletion lists in ListBoxX::Draw.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 17, 2022 08:03 AM UTC
Owner: nobody
You can quickly change DPI awareness settings via the properties dialog of the application file in File Explorer, tab "Compatibility": see section Configuring a local app to run with enhanced system scaling for a screenshot in English (I'm running the German locale of Windows).
When selecting "System (Enhanced)", Windows performs GDI scaling. The official Scite 5.2.4 with "Cascadia Mono" as the monospace font then looks like this (200 %):

With my patch, the rendering is crisp:

I was wrong in my original post that Windows performs GDI scaling by default for DPI-unaware apps: Windows does perform scaling (option "System"), but it simply stretches the bitmap that was rendered at 96 dpi, while you explicitly have to opt into GDI scaling where it renders with an integral scaling factor (2x, 3x ...) and optionally scales it down to a lower DPI (e.g. for 150 %).
With my patch, I observe artefacs with non integral scale factors when changing the scaling from 100 % to a non integral value. Here's an example at 225%:

A simple redraw (by scrolling the buffer) fixes this issue, but the artefacts keep coming back after resizing the window. Obviously, another redraw is required here too.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 17, 2022 10:39 PM UTC
Owner: nobody
I have an update on the blurry autompletion list: it looks absolutely fine without registered images:
![]()
Alas, when the listbox shows images, it really blows up:
![]()
This behaviour also shows without my code changes (i.e. Direct2D with GDI scaling). As expected, Technology::Default with GDI scaling looks fine.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Aug 18, 2022 04:12 PM UTC
Owner: nobody
Taking the same approach in ScintillaWin::CTWndProc() as already in ScintillaWin::EnsureRenderTarget(), the calltip shows no longer blurry (225%):

[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Aug 18, 2022 07:08 PM UTC
Owner: nobody
artefacs with non integral scale factors
That could be caused by the text rectangles not quite meeting and showing background which has been seen in buggy versions before but its not normally as consistent as the picture which appears to be exactly one pixel for each run. Maybe there is rounding occurring below where it can be seen by Scintilla.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Aug 18, 2022 07:27 PM UTC
Owner: nobody
If Scintilla still wants to support Windows XP, then WINVER and _WIN32_WINNT should be defined as _WIN32_WINNT_WINXP instead of _WIN32_WINNT_WIN10 (= 0x0A00) in ScintillaWin.cxx and PlatWin.cxx.
In ScintillaDll.cxx, the constants point to _WIN32_WINNT_WIN2K (= 0x0500).
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Aug 18, 2022 10:28 PM UTC
Owner: nobody
Setting WINVER to an older version also excludes type definitions which are required for the function pointer signature, hence staying with the latest version is fine. ;-)
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Aug 19, 2022 02:21 PM UTC
Owner: nobody
In my merge request I rounded up the scaled rectangles, alas to no avail.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Aug 19, 2022 05:14 PM UTC
Owner: nobody
The blown up autocompletion list is not the result of the actual drawing of the bitmaps: even without the call surfaceItem->DrawRGBAImage() the list items are blurred. No idea why.
My workaround consists of limiting the canvas which is exposed to Direct2D to the bitmap strip at the left hand side of the list box which prevents the text from getting blurred.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Aug 19, 2022 05:17 PM UTC
Owner: nobody
While investigating the issue of the blurry autocompletion list, I had a look at SurfaceD2D::DrawRGBAImage() which features this line:
D2D1_BITMAP_PROPERTIES props = {{DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED}, 72.0, 72.0};
Shouldn't the DPI be set to 96 instead of 72?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Aug 19, 2022 05:26 PM UTC
Owner: nobody
Does it make a difference?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Aug 19, 2022 05:30 PM UTC
Owner: nobody
Its possible that cached measurements aren't being removed from cache (InvalidateStyleRedraw) when DPI set. Check that the handling of WM_DPICHANGED and WM_DPICHANGED_AFTERPARENT are behaving correctly with the change.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 20, 2022 11:18 AM UTC
Owner: nobody
As GDI scaling works transparently to the application, the application does not receive WM_DPICHANGED.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 20, 2022 11:24 AM UTC
Owner: nobody
I cannot spot any difference for a 16x16 image. It only feels wrong to see some code using 72 and other code using 96 dpi.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 20, 2022 07:09 PM UTC
Owner: nobody
As the scale factor changes, the render target should be recreated to match the new value. Is there a message that indicates the scale factor has changed?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 20, 2022 07:10 PM UTC
Owner: nobody
The artefacts do not result from changing DPI: they are already visible initially (with non-integral scale factors) when the render target is created.
When the application window is moved from one monitor to another one featuring a different device scale factor, Windows resizes the application window. ScintillaWin::SizeWindow() reacts to this event by dropping the render target. ScintillaWin::EnsureRenderTarget() in turn recreates the render target where my patch get to see the new device scale factor.
As already stated, resizing the window on a monitor with non integral scale factor also makes the artefacts reappear until a redraw happens (initiated e.g. by scrolling or moving the caret between lines).
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 20, 2022 10:19 PM UTC
Owner: nobody
Scintilla relies on the background being completely drawn by rectangular tiles. In these non-integral modes, it appears the initial drawing surface state is leaking through on the edges of the rectangles. The initial state may be black or grey and may be transparent.
There isn't really a single background colour for the whole window but StyleDefault can be used as an approximation that may sometimes work poorly. Filling with this background before calling Paint decreases the artefacts.
@@ -943,6 +955,7 @@
if (surfaceWindow) {
SetRenderingParams(surfaceWindow);
pRenderTarget->BeginDraw();
+ surfaceWindow->FillRectangle(rcPaint, vs.styles[StyleDefault].back);
Paint(surfaceWindow, rcPaint);
surfaceWindow->Release();
const HRESULT hr = pRenderTarget->EndDraw();
This will be slower and could have visual effects - it may need to take the update region into account instead of just the update rectangle. It should only be run when needed. The runtime cost of the scaling check should be examined and potentially cached.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Aug 21, 2022 08:03 AM UTC
Owner: nobody
The correct way to fix this issue was to enhance PixelAlign() in Geometry.cxx to support non-integral scale factors.
The virtual method PixelDivisions() now returns the factor as hundreth; the implementation of SurfaceGDIreturns 100 whereas SurfaceD2D returns the device scale factor in case of "system enhanced scaling". It is up to you to provide the correct implementions for the other platforms.
This fix is included in my second merge request where changes in ScintillaWin.cxx fix wrong DPI values for DPI-aware applications that introduced with my first merge request.
Credits also go to my colleague and brother Reinhard Nißl.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 23, 2022 01:48 AM UTC
Owner: nobody
With the patch, 200% looks crisp but non-integral scales don't. Do you see this?
Compare DPI-aware and GDI scaling at 125%:

The text shimmers as the window is resized horizontally and there are lines on the left (initial, disappears) and bottom borders.
While most uses of PixelDivisions are handled with the PixelAlign* functions, there is one missed in LineMarker::DrawFoldingMark to calculate widthSymbol.
It is up to you to provide the correct implementions for the other platforms.
This is not possible due to externally maintained platform layers which are independent projects. The Surface API is stable and avoids changes between major versions. Since current implementations only return 1 or 2 (possibly 3 on iPhones but the only known iPhone implementer went dark) from PixelDivisions a shim could be added to convert 1 and 2 to 100 and 200.
Determining the scale factor when creating a measurement surface adds about 30% to the time taken. Haven't yet found a situation where this is significant.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 23, 2022 05:17 PM UTC
Owner: nobody
The patch makes text too large with Technology::DirectWriteDC (technology=3 in SciTE). Since you likely only want Technology::DirectWrite, that can just be a documentation warning.
Checking the DPI awareness will be unnecessary for almost all applications so it may minimize costs to add a new technology mode like Technology::DirectWriteGdiScaled.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 23, 2022 11:57 PM UTC
Owner: nobody
The GetScaleFactorForMonitor call is the heavy bit at around 7x the time of GetDpiForWindow. Its protected by the quite light GetWindowDpiAwarenessContext + AreDpiAwarenessContextsEqual.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 24, 2022 02:00 AM UTC
Owner: nobody
Our third merge request addresses all your remarks:
GetDeviceScaleFactorWhenGdiScalingActive() returns the next integral multiple of 100. Observed line artefacts in the margin region disappeared. (But this is actually an indication that some PixelAlign* treatment is necessary for this code, too.)PixelDivisions() fixed in LineMarker::DrawFoldingMark().ShimPixelDivisions() added to compensate for the shift to hundreths.Technology::DirectWriteDC is used to preserve the correct size (alas with the same blurry outcome as before). Hence, we don't think a dedicated Technology::DirectWriteGdiScaled is necessary.SurfaceD2D, it's now ScintillaWin that provides the device scale factor in a lazy manner to avoid expensive calls to GetScaleFactorForMonitor() for each paint request.The new approach in (1) makes changes to PixelDivisions() to support non-integral values superfluous, but IMHO should be kept as the work is now done and could be taken advantage of by future requirements.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 24, 2022 05:15 AM UTC
Owner: nobody
For quite a long time already, we wanted to activate DirectWrite in Scintilla for our application, but it always gave unsatisfactory results for our default font Consolas (10 pt), even at 100 % (so no GDI scaling involved): the rendering was less crisp as it was with GDI, also text at the same font size occupied more space (both horizontally and vertically) compared to the default rendering with GDI.
Our observation of this inferior rendering is backed by the Mozilla folks, as can be read in an 11 year old article: DirectWrite Text Rendering in Firefox 6
They discussed this issue also in Bug 664055 - Consider including Microsoft Sans Serif and Consolas in the list of typefaces that fall back to GDI classic where they come to the conclusion not to go for DirectWrite for certain font families.
This exception list is still valid in the current version:
pref("gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
"Arial,Consolas,Courier New,Microsoft Sans Serif,Segoe UI,Tahoma,Trebuchet MS,Verdana");
We found out that GDI scaling (e.g. at 200 %) has some rendering glitches with Consolas 9pt and Cascadia Mono 10 pt. Fortunately DirectWrite has not, alas it is ignored by GDI scaling, hence the reason for our patch.
Though, we will follow the example given by Mozilla and won't use DirectWrite for the same list of fonts, at least not at 100 %. We found Consolas, 10 pt looks good at 200 % with DirectWrite, though our new preference is Cascadia Mono which looks good both at 100 and 200 %, both with DirectWrite and GDI.
BTW: As Scite for Windows uses DirectWrite with Verdana and Consolas in its default configuration, I'm wondering how you came up with the font sizes 9.4 for Verdana and 8.9 for Consolas.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 24, 2022 05:08 PM UTC
Owner: nobody
Merge request #31 draws the image in autocompletionslists vertically centered again.
In this regard: drawing of the image is not DPI-aware, i.e. the images appear too small with a scale factor greater 100 % with DPI-aware apps -- so this is not an issue with GDI scaling and hence not part of our merge requests.
Similar to SCI_RGBAIMAGESETWIDTH and SCI_RGBAIMAGESETHEIGHT, a new API called SCI_RGBAIMAGESETDPI would be necessary to make Scintilla know about the DPI of the image strip.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 24, 2022 05:47 PM UTC
Owner: nobody
For the PixelDivisions shim, the merge puts the implementation inside the Windows platform layer so won't work on the other platforms. To work on all platforms it could be something like inline inside the Platform header and I'd use a more descriptive name like PixelDivisionsScaled or PixelScale:
diff -r 123ae5de5bd2 src/Platform.h
--- a/src/Platform.h Sat Aug 27 08:02:08 2022 +1000
+++ b/src/Platform.h Sat Aug 27 13:38:49 2022 +1000
@@ -189,6 +189,10 @@
Surface &operator=(const Surface &) = delete;
Surface &operator=(Surface &&) = delete;
virtual ~Surface() noexcept = default;
+ int ShimPixelDivisions() {
+ const int value = PixelDivisions();
+ return value < 10 ? value * 100 : value;
+ }
static std::unique_ptr<Surface> Allocate(Scintilla::Technology technology);
virtual void Init(WindowID wid)=0;
how you came up with the font sizes 9.4 for Verdana and 8.9 for Consolas
I like to have more lines of text but don't care much about line width and prefer fatter text a little, so increased the numbers until just before they produced fewer lines on screen.
On macOS with retina 2x display (PixelDivisions() -> 2), there are differences to previous versions. This is with the fold margin set to 5 logical (10 physical) pixels wide and the old version to the left (ignore darkening as that is just window layers):

[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Aug 25, 2022 10:19 AM UTC
Owner: nobody
Merge request #32 renames ShimPixelDivisions() to PixelDivisionsScaled() and puts the implementation into Platform.h.
In regards to your reported issue on macOS, LineMarker::DrawFoldingMark() is to be investigated, isn't it?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 27, 2022 04:02 AM UTC
Owner: nobody
It may be clearer to use a floating point pixelDivisions value (1.0, 1.25, ... 2.0) for most calculations, since every use is multiplying and dividing by pixelDivisions / 100.
The value returned from Surface::PixelDivisions() has to remain int for compatibility (and so *100 for GDI scaled) but platform layers that are themselves calling PixelAlign*(...PixelDivisions()) will continue OK without needing to apply 100. This also makes the unit tests (in test/unit/testGeometry.cxx) stay green although tests for non-integer scaling should be added.
It would be worthwhile committing the changes to PixelAlign(Point...), PixelAlign(PRectangle...), and PixelAlignOutside(PRectangle...) even without the rest of these changes as they avoid repeating the detailed calculations in favour of a higher-level view.
In regards to your reported issue on macOS, LineMarker::DrawFoldingMark() is to be investigated, isn't it?
Yes.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Aug 27, 2022 04:46 PM UTC
Owner: nobody
Please see our merge request 33: "Surface::PixelDivisionsScaled() allows for non-integral scale factors".
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Aug 29, 2022 10:59 PM UTC
Owner: nobody
On macOS with retina 2x display (PixelDivisions() -> 2), there are differences to previous versions. This is with the fold margin set to 5 logical (10 physical) pixels wide and the old version to the left (ignore darkening as that is just window layers):
We could reproduce this issue also on Windows. It was due to a bug in my earlier fix in LineMarker::DrawFoldingMark() (line 198):
minDimension : minDimension - 1.0f / pixelDivisions / 100;
I missed to add parenthesis (or could have multiplied with 100 instead of divided by 100):
minDimension : minDimension - 1.0f / (pixelDivisions / 100);
With the new approach of having a floating point pixelDivisions value, this code doesn't have to be touched.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 30, 2022 02:46 PM UTC
Owner: nobody
I finally worked out
The new approach in (1) makes changes to PixelDivisions() to support non-integral values superfluous,
So there is no need to change the interface to platform layers, eliminating the potential for new bugs.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Aug 30, 2022 02:58 PM UTC
Owner: nobody
Merge request 33 updated. Once you merged it, I will adapt the original GDI scaling patch accordingly.
PS: Sorry for too many merge requests: I've been working with Git and Github now for three years, but I'm new to Mercurial and SourgeForge. Things are slightly different 😉
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 31, 2022 06:48 AM UTC
Owner: nobody
Since this has now resolved down to integral pixel divisions again, it appears that the scope of the feature can be reduced significantly. There is no need for any changes to platform-independent code and no need to modify the interface to platform layers. Instead, the feature can be isolated to just the win32 platform layer and just for Direct2D / DirectWrite.
The various DPI awareness system APIs allow controlling DPI awareness in different scopes to allow updating an application in parts. Perhaps the Scintilla windows can be created as DPI aware then revert back to GDI scaling for other windows.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 31, 2022 08:13 AM UTC
Owner: nobody
Since this has now resolved down to integral pixel divisions again, it appears that the scope of the feature can be reduced significantly. There is no need for any changes to platform-independent code and no need to modify the interface to platform layers. Instead, the feature can be isolated to just the win32 platform layer and just for Direct2D / DirectWrite.
Yes, you are right. Merge request 33 is now a mere refactoring patch for the various PixelAlign* functions in Geometry.cxx, it adds the missing PixelAlignCeil() function to streamline the code and besides tests for this new function, a missing test for PixelAlign(Point...)) with factor 1 was added to testGeometry.cxx as well.
Perhaps the Scintilla windows can be created as DPI aware then revert back to GDI scaling for other windows.
We looked at this approach first, but alas it is not supported as stated in the article on DPI_HOSTING_BEHAVIOR enumeration:
DPI_HOSTING_BEHAVIOR enables a mixed content hosting behavior, which allows parent windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT value. [...]
This hosting behavior does not allow for windows with per-monitor DPI_AWARENESS_CONTEXT values to be hosted by windows with DPI_AWARENESS_CONTEXT values of system or unaware.
Original merge request 28 which adds GDI scaling support is now updated.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Aug 31, 2022 09:55 PM UTC
Owner: nobody
Committed a squashed merge request 33 as [e141bc].
Retaining the HWND in SurfaceD2D seems like poor modularity. The performance issue mentioned above was about measurement surfaces (which are for measuring text layout) which are supposed to be lightweight and do not support drawing. It should be fine to always set the scale factor for surfaces that will be used for drawing which is when pixelDivisions is applied. The cases can be differentiated by the SurfaceID parameter which is present for drawing surfaces.
Attached a patch that makes the current state of merge request #28 conform to Scintilla conventions - indentation, brace positions, sorting of includes, unnamed namespace, no C casts, almost never auto, no multiple assignments.
Attachments:
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 01, 2022 08:58 AM UTC
Owner: nobody
Our goal was to avoid unnecessary calls to retrieve the scale factor every time painting takes place. Linking SurfaceD2D to ScintillaWin didn't look like tight coupling to us as SurfaceD2Dis the dedicated tool to get painting done, especially since the surface instance exists solely for the time of painting.
Merge request 28 updated accordingly.
PS: Changes to ListBoxX::Draw() could be committed separately as they are correct also without GDI scaling being active, but GDI scaling support depends on them.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Sep 02, 2022 01:04 AM UTC
Owner: nobody
There's a build failure when Direct2D is turned off with -DDISABLE_D2D:
1>C:\u\hg\cop\scintilla\win32\ScintillaWin.cxx(595,80): error C2039: 'GetDeviceScaleFactor': is not a member of 'Scintilla::Internal::ScintillaWin'
1>C:\u\hg\cop\scintilla\win32\ScintillaWin.cxx(285): message : see declaration of 'Scintilla::Internal::ScintillaWin'
A deviceScaleFactor of 1 seems reasonable for GDI, so moving it outside the #if defined(USE_D2D) block and removing the similar check around GetDeviceScaleFactor minimizes the pre-processor noise.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Sep 02, 2022 06:39 AM UTC
Owner: nobody
Fixed in the updated merge request 28.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 03, 2022 01:48 AM UTC
Owner: nobody
Our goal was to avoid unnecessary calls to retrieve the scale factor every time painting takes place. Linking SurfaceD2D to ScintillaWin didn't look like tight coupling to us as SurfaceD2Dis the dedicated tool to get painting done, especially since the surface instance exists solely for the time of painting.
I got a little lost ... of course, SurfaceD2D doesn't have to keep a pointer to ScintillaWin: retrieving the deviceScaleFactor from ScintillaWin in SurfaceD2D::Init() is sufficient.
[Merge request 28] updated not to call the expensive GetDeviceScaleFactorWhenGdiScalingActive() with every paint process.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 03, 2022 06:50 AM UTC
Owner: nobody
Ensure the effect of the force parameter to UpdateRenderingParams is OK for the device scaling factor - if this should be updated for WM_WINDOWPOSCHANGED and not just WM_SETTINGCHANGE then the order of code made need to be changed.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 03, 2022 08:53 PM UTC
Owner: nobody
Well yes, WM_WINDOWPOSCHANGED needs to be handled as stated for example in the documentation on GetScaleFactorForMonitor():
Your code needs to handle the WM_WINDOWPOSCHANGED message in addition to the scale change event registered through RegisterScaleChangeEvent, because the app window can be moved between monitors. In response to the WM_WINDOWPOSCHANGED message, call MonitorFromWindow, followed by GetScaleFactorForMonitor to get the scale factor of the monitor which the app window is on.
Though, I'm observing a strange behaviour in UpdateRenderingParams():
When kicking my app window between my 1x and 2x monitors using Win+Shift+Left/Right, then MonitorFromWindow() returns the handle of the new monitor which differs from hCurrentMonitor, so the early exit in UpdateRenderingParams() is not taken.
When dragging my app window between my two monitors using the mouse, then MonitorFromWindow() returns the same value that hCurrentMonitor holds, hence no update to the device scale factor. (Calling GetDeviceScaleFactorWhenGdiScalingActive() nonetheless in this case returns the same old value, given the same wrong monitor handle.)
I cannot explain this difference in behaviour. Anyway, this circumstance is not subject to this GDI scaling patch for DirectWrite as I'm also observing this behaviour when configuring my app as PerMonitorV2-aware.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Sep 04, 2022 12:07 AM UTC
Owner: nobody
The difference in behaviour stems back to the wrong HWND being supplied to MonitorFromWindow(): it has to be the handle of the application window, not the one of the Scintilla control. A small Scintilla control can still reside on the old monitor while the majority of the application window is already positioned on the new monitor.
While this fix made the correct monitor be detected most times, I still observed cases where it wasn't the case: it depended on where at the edge of two adjacents monitors the crossover happens.
My idea is to determine the monitors from the top left and the right bottom corner of the application window and compare these results to the monitor that was determined for the application window. If either of them differs, I go for the new value.
See merge request 34 for a first "quick and dirty" implementation.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Sep 04, 2022 08:08 AM UTC
Owner: nobody
Finding a main window for a process through EnumWindows seems complex, slow and liable to have problems when an application has more than one top-level window. The top-level parent of a window would seem a more accurate choice possibly using GetParent or GetAncestor.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Sep 07, 2022 04:35 PM UTC
Owner: nobody
Going for GetAncestor(hWnd, GA_ROOT) now in merge request 34.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Sep 07, 2022 09:44 PM UTC
Owner: nobody
GetWindowDPI() may has similar behavior, as GetDpiForWindow returns the DPI of the monitor where the window is located for DPI_AWARENESS_PER_MONITOR_AWARE.
Not sure when GetWindowDPI(hwnd) diffs from GetWindowDPI(GetAncestor(hwnd, GA_ROOT).
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 08, 2022 06:49 AM UTC
Owner: nobody
My monitor setup is as follows:

Monitor 1 is the built-in monitor of my laptop which is turned off.
Monitor 2 is the primary monitor, a 4K monitor with scale factor 150 %.
Monitor 3 is a WQHD monitor with scale factor 100 %.
When I drag my app window between monitor 2 and 3 around the vertical middle of the edge between those two monitors, MonitorFromWindow() works as expected.
Though when I drag the app window in the red rectangular area between the two monitors, MonitorFromWindow() does not detect the new monitor.
As I had to find out: this misbehaviour only happens when GDI scaling is active. When I set my app to be DPI-aware (PerMonitorV2), MonitorFromWindow() always worked correctly.
I suppose MonitorFromWindow() is somehow flawed in GDI scaling mode. Even the following trick to temporarily switch to PerMonitorV2 mode didn't work out:
const DPI_AWARENESS_CONTEXT oldContext = ::SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
PLATFORM_ASSERT(oldContext != nullptr);
HMONITOR monitor = ::MonitorFromWindow(hRootWnd, MONITOR_DEFAULTTONEAREST);
::SetThreadDpiAwarenessContext(oldContext);
I might file a bug report to Microsoft.
I've updated my merge request 34 which boils down to a simple GetAncestor() call to retrieve the root of the window hierarchy. This fix is correct and necessary for DPI-aware apps as well.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 08, 2022 11:42 AM UTC
Owner: nobody
My brother got the idea to go for the trick, but to use MonitorFromRect() instead ... and this worked out!
Merge request 34 updated.
If you approve of this approach, I will still have to make it compatible API-wise with older versions of Windows.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 08, 2022 08:05 PM UTC
Owner: nobody
Flicking the ThreadDpiAwarenessContext adds the potential for side-effects such as Windows (or a future / past version) sending or posting setting or position change notification messages. Possibly restrict calling SetThreadDpiAwarenessContext to only situations that need it. The change currently wraps both the GetWindowRect and MonitorFromRect so was is it needed for both?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Sep 09, 2022 12:02 PM UTC
Owner: nobody
In Mixed-Mode DPI Scaling and DPI-aware APIs, Microsoft explains the use case of SetThreadDpiAwarenessContext(), also giving an example:
A common scenario for the use of SetThreadDpiAwarenessContext is as follows: Begin with a thread that is running with one context (such as DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) temporarily switch to a different context (DPI_AWARENESS_CONTEXT_UNAWARE), create a window, and then immediately switch the thread context back to its previous state. The created window will have a DPI context of DPI_AWARENESS_CONTEXT_UNAWARE, while the calling thread's context will be restored to DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE with a subsequent call to SetThreadDpiAwarenessContext.
My patch needs to wrap the two calls to GetWindowRect() and MonitorFromRect() into the new DPI awareness context:
GetWindowRect() returns logical window coordinates and MonitorFromRect() returns the wrong monitor in those special cases at corners of adjacent monitors.GetWindowRect() returns physical window coordinates and MonitorFromRect() (which in this mode expects physcial coordinates) returns the correct monitor.Only the GetAncestor() call could be placed outside, but its functionality is mere logical, independent of window and monitor positions.
One could check whether the current DPI awareness context is GDI scaling (i.e. the case where I observed the flaw) and only then go for the MonitorFromRect() workaround. But IMHO, going for the temporary PerMonitorV2 switch will always work as this is the mode where Windows doesn't have to go for quirks as it's all up to the application to render itself correctly, being a DPI-aware app.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 10, 2022 12:35 AM UTC
Owner: nobody
While GetDpiForWindow() could be used to eventually calculate the device scale factor oneself, pIDWriteFactory->CreateMonitorRenderingParams() also expects a monitor handle. Hence, determining the correct monitor is essential. Retrieving the physical coordinates and using them with MonitorFromRect() in a DPI-aware mode is a legitimate workaround.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 10, 2022 08:18 AM UTC
Owner: nobody
Committed [da25fe] for autocompletion images.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Sep 13, 2022 03:39 PM UTC
Owner: nobody
Merge request 34 is updated which extracts the logic into the new function Internal::MonitorFromWindow() and leaves the call site as easy to read as before:
const HWND hRootWnd = ::GetAncestor(MainHWND(), GA_ROOT);
const HMONITOR monitor = Internal::MonitorFromWindow(hRootWnd);
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 17, 2022 12:02 PM UTC
Owner: nobody
Using the application root window to determine the monitor and thus the scale factor and rendering parameters could cause problems when an application is deliberately moved to straddle the monitor boundary.
Say, for example, that the user has a 2 monitor set-up and opens SciTE with the edit pane on one monitor and the output pane on another monitor. One or the other monitors will be considered the monitor for the root window and the pane located on the other monitor will appear poorly with incorrect parameters.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Sep 23, 2022 09:22 AM UTC
Owner: nobody
The example you describe is exactly the case with my application.
But: When dragging the application window accross monitors, there's a single point in time when Windows discovers that most area of that window is covering a certain monitor, so Windows then goes for another DPI setting for the whole application (root) window.
You can see this actually very well when dragging a window from a 2x monitor to a 1x monitor: on the 1x monitor, the window appears too large while it still looks good on the 2x monitor. Then suddenly there's the switch, making the window in terms of size look good on the 1x monitor while it appears too small on the 2x monitor.
If your supposition was true, there would not have been the need to go for the root window in the first place.
BTW: as an example for the use case of SetThreadDpiAwarenessContext(), see the section Window Placement in Microsoft's blog post on High DPI Scaling Improvements ...: in order to restore logical window coordinates when starting-up Notepad, they switch to "unaware", call SetWindowPlacement() and then go back to the previous context.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Sep 24, 2022 11:47 PM UTC
Owner: nobody
DPI is not the only factor here. The original motivation for UpdateRenderingParams was the rendering parameters which differ between monitors and should be set for the monitor that best matches the Scintilla window, not the application window. For example, text should draw using the correct DWRITE_PIXEL_GEOMETRY (RGB/BGR/flat).
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Sep 25, 2022 09:36 AM UTC
Owner: nobody
Well, then the monitors have to be treated separatedly: we need one monitor handle for the Scintilla window (hCurrentMonitor) and Internal::GetDeviceScaleFactorWhenGdiScalingActive(hWnd) has to retrieve the root window handle to correctly determine the monitor for which Windows has decided to scale the whole window.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Sep 26, 2022 12:22 PM UTC
Owner: nobody
We forgot one important aspect: there's only one message that informs about the switch of monitors. So, when about half of the application is on the new monitor, UpdateRenderingParams() will be called. But it won't be called again when the application is dragged completely onto the new monitor.
Hence, to go for different monitor handles is wrong unless you'd get such a message for every child window that crosses the monitor boundaries.
In the end, I cannot think of a true real world use case where you wanna use an application window that stretches accross two monitors with different scaling.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Sep 26, 2022 12:43 PM UTC
Owner: nobody
there's only one message that informs about the switch of monitors. So, when about half of the application is on the new monitor, UpdateRenderingParams() will be called
WM_WINDOWPOSCHANGED is per-window so there is an UpdateRenderingParams for each SciTE pane as it moves and that runs through to changing the rendering parameters when its majority is shifted onto a monitor.
Therefore 'different monitor handles' is the right approach to handle the two separate aspects: rendering parameters and scaling factor.
In the end, I cannot think of a true real world use case where you wanna use an application window that stretches accross two monitors with different scaling.
An application that covers two monitors with the same scale but different rendering parameters is more likely.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Sep 26, 2022 12:58 PM UTC
Owner: nobody
WM_WINDOWPOSCHANGED is per-window so there is an UpdateRenderingParams for each SciTE pane as it moves
When moving an application window, only this top level window receives WM_WINDOWPOSCHANGED, not the hosted Scintilla windows.
and that runs through to changing the rendering parameters when its majority is shifted onto a monitor.
Yes: as the new scale factor impacts the size of the root window (in physical coordinates), this change in size makes the root window send the message to all its child windows. This is when UpdateRenderingParams() is eventually called.
When you continue dragging the root window so that it is completely on the new monitor, then (in your example) one SciTE pane still works with the monitor handle of the previous monitor.
Therefore 'different monitor handles' is the right approach to handle the two separate aspects: rendering parameters and scaling factor.
This is reflected by my updated merge request 28.
An application that covers two monitors with the same scale but different rendering parameters is more likely.
Yes, but in this case UpdateRenderingParams() is not triggered when crossing monitor boundaries as the physical window coordinates remain the same.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 29, 2022 01:52 AM UTC
Owner: nobody
When moving an application window, only this top level window receives WM_WINDOWPOSCHANGED, not the hosted Scintilla windows.
OK, that makes detecting monitor moves difficult. There isn't even always a WM_PAINT. Subclassing the root window seems too intrusive. Adding a check for global position change to the paint handler may be OK. The application could send WM_SETTINGCHANGE after its been moved if it wants to ensure updated rendering.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 29, 2022 03:48 PM UTC
Owner: nobody
I suggest you check in my merge request which in the meanwhile correctly fixes the issue described in my original post.
You might then want to go for a quick fix of UpdateRenderingParams() by retrieving the root window handle and passing it to my new function MonitorFromWindowFix():
const HWND hWnd = ::GetAncestor(MainHWND(), GA_ROOT);
const HMONITOR monitor = Internal::MonitorFromWindowFix(hWnd, MONITOR_DEFAULTTONEAREST);
if (!force && monitor == hCurrentMonitor && renderingParams->defaultRenderingParams) {
return false;
}
IMHO, in more than 99 % of the cases where an application window crosses monitors, the window will come to rest entirely on the new monitor. The code above will eventually make Scintilla work correctly in these situations.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Sep 29, 2022 11:14 PM UTC
Owner: nobody
So
// needs to use top level window to detect the acutely monitor
HWND topLevel = ::GetAncestor(MainHWND(), GA_ROOT);
HMONITOR monitor = ::MonitorFromWindow(topLevel, MONITOR_DEFAULTTONEAREST);
would be the quick fix (to make it works most of the time) for UpdateRenderingParams()?
The rectangle hack appears to fix the unexpected behavior, could be addressed later.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Sep 30, 2022 07:22 AM UTC
Owner: nobody
Yes.
The rectangle hack appears to fix the unexpected behavior, could be addressed later.
Well, it's not a hack, it's a workaround as my implementation should actually resemble what MonitorFromWindow() is supposed to be doing if Microsoft supported GDI scaling correctly.
Anyway: my function MonitorFromWindowEx() can be checked in separately from this bug report as this report is about DirectWrite rendering looking blurry with DPI unaware apps. So even if the GDI scaled app looks blurry with DirectWrite, still the correct monitor would be used.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Oct 14, 2022 02:15 PM UTC
Owner: nobody
my implementation should actually resemble what MonitorFromWindow() is supposed to be doing if Microsoft supported GDI scaling correctly.
Is there a Windows bug report where this can be tracked?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Oct 15, 2022 07:46 AM UTC
Owner: nobody
Is there a Windows bug report where this can be tracked?
I haven't filed a report and even if then one could not follow it publicly.
I don't intend to file a report because of at least three reasons:
We can fix the bug on our own: there's a simple workaround available.
It costs way too much time as Microsoft's first level support sucks.
Years ago, we filed a bug report for a mixed DPI feature that was introduced with Windows 10 1703 as we could not work around the bug on our own. We ended up teaching the first level support how the whole feature that Microsoft invented actually works before the first level support understood the bug.
Even if Microsoft fixes the bug, it most likely won't be back-ported. Chances are that a fix might be released with the next "feature update" of Windows 11 which would be one year from now. This doesn't give us much benefit.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Oct 16, 2022 09:42 PM UTC
Owner: nobody
Committed Internal::MonitorFromWindow as [1da193] .
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Oct 17, 2022 07:27 PM UTC
Owner: nobody
Merge request 28 and 34 conflict so, now that 34 is committed, 28 no longer applies cleanly. It also contains many changes that were reverted so its a mess to try to extract the changes manually.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Oct 29, 2022 10:52 AM UTC
Owner: nobody
I've updated merge request 28.
Besides integrating the changes of the default branch, there's new code in ScintillaWin::EnsureRenderTarget() to make DirectWriteDC support GDI scaling as well (lines 674-680). We don't think that this is the correct general approach as it also requires the correct counterpart in the application that uses Scintilla. We opted for this solution as we had to ship our application which uses DirectWriteDC for rendering our DSL code to a bitmap. I will come back on this issue once we found a way to retrieve the DPI from a DC. (Our current approach is to (ab-) use viewport transformation (scaling) in the application in order to make Scintilla render more content according to the scaling ratio.)
We also found a bug: ListBoxX::Draw() (line 3265) passes the window handle of the list box to Surface::Init(), which makes SurfaceD2D::Init() call Internal::GetDeviceScaleFactor(wid). Alas, the passed window ID is not connected to a ScintillaWin, but to the ListBoxX ... so the cast in GetDeviceScaleFactor(WindowID wid) (line 603) is wrong.
Our quick hack was to pass the window ID of the parent of the list box which is a ScintillaWin (line 3265 in PlatWin.cxx):
- surfaceItem->Init(pDCRT, pDrawItem->hwndItem);
+ surfaceItem->Init(pDCRT, parent->GetID());
Do you have an idea for a better solution?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Fri Nov 11, 2022 01:11 AM UTC
Owner: nobody
We don't think that this is the correct general approach as it also requires the correct counterpart in the application that uses Scintilla.
Yeah, requiring changes to existing apps isn't reasonable.
Our quick hack was to pass the window ID of the parent of the list box which ...
Do you have an idea for a better solution?
That is a bit smelly and could make some changes more difficult. I can't recall if we've been around this one before but it may be better to push the scale factor to the surface, perhaps by adding it to RenderingParams.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 14, 2022 03:19 PM UTC
Owner: nobody
Having SurfaceD2D know about the class controlling the window its working for reverses responsibilities. SurfaceD2D should be usable in different contexts: edit views, bitmaps, calltips, autocomplete, printing, and others as they are defined. It should be the class owning the surface that sets up the surface to behave correctly in its context.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Nov 16, 2022 09:38 PM UTC
Owner: nobody
Merge request 28 is updated.
The new approach I've taken is that SurfaceD2D determines the device scale factor from the passed in render target.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 21, 2022 10:20 PM UTC
Owner: nobody
Regarding the changes made for DirectWriteDC in ScintillaWin::EnsureRenderTarget(), we think they can remain:
SIZE windowExtent;
SIZE viewportExtent;
::GetWindowExtEx(hdc, &windowExtent);
::GetViewportExtEx(hdc, &viewportExtent);
drtp.dpiX = 96.f * abs(windowExtent.cx) / abs(viewportExtent.cx);
drtp.dpiY = 96.f * abs(windowExtent.cy) / abs(viewportExtent.cy);
If an application makes no changes to the DC, the code will still set 96 dpi as it did before. This means: with DirectWriteDC, DPI aware apps get crisp texts, GDI scaled apps get blurry text. Hence no (breaking) changes.
What we do in our DPI unaware, but GDI scaled app to get crisp text is to scale up the window rectangle by the device scale factor so that the window to draw in gets larger. Eventually, we scale down the device context by setting the view port to the original rectangle. But as GDI scaling itself scales up the DC by the device scale factor, these last two steps merge to a no-op which leaves the DC with more pixels:
const int factor = Sys::GetDeviceScaleFactorWhenGdiScalingActive(hWnd);
CRect scaledRect(origRect);
scaledRect.right = scaledRect.left + factor * scaledRect.Width();
scaledRect.bottom = scaledRect.top + factor * scaledRect.Height();
::MoveWindow(hWnd, 0, 0, scaledRect.Width(), scaledRect.Height(), false);
// Make the DC scale down the larger content
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(scaledRect.Width(), scaledRect.Height());
pDC->SetViewportExt(origRect.Width(), origRect.Height());
The only side affect of our code change in Scintilla would be for applications that want to scale the Scintilla client area by reaching out to view port transformation which is rather unlikely as Scintilla features its own implementation of zooming.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Nov 23, 2022 02:58 PM UTC
Owner: nobody
Applications sometimes do strange things with painting into their own DCs.
More of a worry is that this is a hidden option - how are application developers to know that this is a possibility? If it is just for one application then it may not be worth including.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Nov 23, 2022 03:35 PM UTC
Owner: nobody
That is a better technique. There should be more simplifications since both GetDeviceScaleFactor functions are no longer needed.
There are some warnings from Visual C++ Code Analysis.
win32\PlatWin.cxx(1435): warning C26447: The function is declared 'noexcept' but calls function 'SetDeviceScaleFactor()' which may throw exceptions (f.6).
win32\PlatWin.cxx(1543): warning C26461: The pointer argument 'pD2D1RenderTarget' for function 'Scintilla::Internal::SurfaceD2D::SetDeviceScaleFactor' can be marked as a pointer to const (con.3).
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Nov 23, 2022 09:44 PM UTC
Owner: nobody
Updated merge request 28 addresses your remarks.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Wed Nov 23, 2022 10:43 PM UTC
Owner: nobody
We tried to figure out a way to achieve this in a generic way, but alas we found no solution. As our solution works great for our purpose, we won't invest more time and energy to provide a solution that nobody else is waiting for.
The updated merge request 28 drops our app specific approach to make DirectWriteDC crisp with GDI scaling.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Nov 24, 2022 11:56 AM UTC
Owner: nobody
Committed as [543259] with minor changes (in patch) to make linters happy and to simplify.
Attachments:
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Thu Nov 24, 2022 12:00 PM UTC
Owner: nobody
This commit seem breaks no integral scaling, e.g. 125% for SciTE.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Nov 27, 2022 11:00 AM UTC
Owner: nobody
SciTE is marked as DPI-aware in its manifest so it shouldn't be triggering this code. Are you setting an override mode? How is it broken?
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Nov 27, 2022 12:00 PM UTC
Owner: nobody
Sorry, I can not reproduce the blur look after reboot.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 28, 2022 01:27 AM UTC
Owner: nobody
Thanks.
While making my merge request compatible with the default branch, I introduced a little bug in GetDeviceScaleFactorWhenGdiScalingActive() by calling the wrong version of MonitorFromWindow:
const HMONITOR hMonitor = MonitorFromWindow(hRootWnd, MONITOR_DEFAULTTONEAREST);
The fixed version only features one parameter, the official API features two. That's the reason why I originally called it MonitorFromWindowFix to explicitly mark the difference.
Would you please incorporate the fix? Renaming the function wouldn't be a bad idea either.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 28, 2022 11:07 AM UTC
Owner: nobody
[3828f7] changes function called and renames it to MonitorFromWindowHandleScaling. Its more descriptive but still not a great name.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 28, 2022 11:33 AM UTC
Owner: nobody
Just create a new namespace called Fix which is the home for fixed versions of platform APIs. Then you can call it the same way as the original function. There is no need to come up with a clumsy name because the semantic of the function has not changed: it is still supposed to do what it was actually meant for, but without the bugs. To find out what's actually fixed: comments in the fixed version will tell.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Mon Nov 28, 2022 11:24 PM UTC
Owner: nobody
It should be made really obvious at the call site that the function is doing something different to the platform function. Different code comes with new possibilities of failure as well as performance concerns.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: open
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Nov 29, 2022 04:56 PM UTC
Owner: nobody
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Nov 29, 2022 08:25 PM UTC
Owner: nobody
Hi, please look at this demo. Is this related? Latest NPP, text scaling is off (100%), Win7. The text is not just blurred.. It is getting more blur with time!
Attachments:
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Tue Dec 06, 2022 10:45 PM UTC
Owner: nobody
This issue is specifically about text scaling. If text isn't scaled then it isn't related.
The video appears to be drawing again over a previous draw without clearing and could be due to video driver capabilities. Try the alternative SC_TECHNOLOGY_DIRECTWRITERETAIN or SC_TECHNOLOGY_DIRECTWRITEDC options when calling SCI_SETTECHNOLOGY.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Jan 20, 2024 09:58 PM UTC
Owner: nobody
Yes, DIRECTWRITE DC helped. Why was it introduced initially? Specially for cases like this? Does it slow down rendering or may be set by default safely? Or be used as a fallback on some known buggy hardware or legacy environments like win7/wine?
Or may it be investigated and fixed? Cause I've never seen anything like this in any other software. Although it may be related to the video driver - yes - but it only appears in Scintilla (both Npp and Sc1). Other software somehow avoid that... Although, frankly, not many programs support direct write.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sat Jan 20, 2024 10:35 PM UTC
Owner: nobody
Ha, found the bad guy! It's Intel CMAA setting. They call it, "optimal application mode". The videodriver is trying to be smart and disable CMMA for text applications. The problem is, the list is hardcoded and includes word, excel and a few others. Npp is not in it.
So, to play games CMAA should be enabled. But to play Scintilla, CMAA should be switched off - see the screenshot - with one of the highlighted checkboxes.
Is there a way to detect CMAA present and fallback to DC canvas? Could be quite a solution.
Attachments:
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Jan 21, 2024 12:50 AM UTC
Owner: nobody
Yes, DIRECTWRITE DC helped. Why was it introduced initially? Specially for cases like this?
Early on, DirectWrite had some problems due to bugs in drivers and its own initial implementation. So some extra work-around modes were added. I can't recall specifically what led to this mode but you could search this tracker, the source code history, and the mailing list if you want to discover why it was done.
Does it slow down rendering or may be set by default safely?
SC_TECHNOLOGY_DIRECTWRITEDC may increase memory use or decrease performance and should not be used when SC_TECHNOLOGY_DIRECTWRITE works.
Is there a way to detect CMAA present and fallback to DC canvas? Could be quite a solution
There may be but it would not be a reasonable inclusion in Scintilla. There'd be a possibility of failing to understand the circumstances (like active GPU depending on power supply as occurs on some systems) and switching technology the wrong way. Windows 7 is old and unsupported by Microsoft. Intel don't seem much interested in fixing their drivers. This issue is going to affect very few people as most are now running newer systems. Its simpler to leave an option that can be chosen in unusual circumstances.
[bugs:#2344] DirectWrite rendering looks blurry with DPI unaware apps
Status: closed
Group: Bug
Created: Tue Aug 16, 2022 11:33 AM UTC by Markus Nißl
Last Updated: Sun Jan 21, 2024 01:27 AM UTC
Owner: nobody