Skip to content

Commit 84f84be

Browse files
committed
Clipboard workaround for Google Chrome bugs
See #343 for details. Thank you to @EsterLoken for catching and reporting, and @wqweto for additional follow-up. tl;dr: this fixes Copy+Paste into Google Chrome. By manually marking CF_DIB as available at Cut/Copy-time, PD can reliably receive notifications for copy+paste when old-style CF_DIB format is requested. This is an ancient Windows bug going back to at least Windows 7 (possibly earlier? idk), but the bug was useful because you could "force" apps to choose between bitmaps/DDBs (which every app supports all the way back to Win 3.1) or DIBv5 (which any Win 98+ app should support, as it fixes DIB issues by e.g. making alpha-handling and color-management explicit). I have never before encountered an app that didn't support either DDBs or DIBv5, but at some point, Chrome developers apparently decided this was the way to go. I'm shocked that Google developers are this incompetent (/s obviously, Google's app behavior on Windows is perpetually incompetent) Longer explanation: from a code standpoint this change is trivial, but it has many potential implications for interop with other software, including breaking 32-bpp copy+paste ops in old software. Given Chrome's ubiquity, I think changing this is probably the least of several evils, but I'm extremely annoyed that Chrome is incapable of using modern clipboard formats, and that its clipboard interop breaks with regularity. (I've lost track of how many times I've tweaked PD's clipboard code to make Chrome happy. At least this time it involves broken pasting into Chrome instead of broken copying out of Chrome... so points for at least breaking in a novel way? uuuuuugh) Even longer explanation: Windows DIB format, like nearly everything in GDI, is inconsistent in its handling of alpha bytes. As an easy example, PrintScreen keypresses often place a 32-bpp DIB on the clipboard with random alpha bytes. Because you don't ever know if a clipboard DIB has useful or broken alpha, you must assume broken alpha and treat the data as 24-bpp only. (Heuristics can be used to try and "guess" alpha state, but like any heuristics, edge-cases are complicated and impossible to handle perfectly.) Old-school DDBs (CF_BITMAP) don't have this issue because alpha is explicitly *not* supported, and "modern" DIBv5 fixes the issue with not just explicit alpha support, but extra support for nice things like color-management. There is no reason to forcibly use CF_DIB except for software which predates Windows 98. (Or if you're a Google dev, apparently.) By reenabling old-style DIB support to make Chrome work, I must manually composite 32-bpp images against a backcolor when using DIB format. This is required to making pasting into e.g. MS Paint still viable. (MS Paint preferentially grabs DIB data over DDB data, so when placing 32-bpp DIB data on the clipboard you *must* composite first or you'll get a broken image when pasting into MS Paint. Paint will ignore a DDB copy completely if a DIB is present, alas.) This change also breaks some software that would preferentially choose DIBv5 over DIB when available (e.g. Chasys Draw, which can no longer paste 32-bpp data from PD. This is technically a problem on their end but users will only know "it worked before and now it doesn't", alas). I'm especially frustrated with Chrome because this change doesn't even allow you to paste 32-bpp data into Chrome - that still isn't possible, because they won't take DIBv5 or PNG-format data if available, no matter what you do. All it does is break interop with a bunch of legacy software, just so you can paste into Chrome *at all*. Anyway, I don't maintain an exhaustive list of clipboard format support in other apps, but I'm hoping that PNG as an interop format is more ubiquitious in 2020, which may negate some of the worst issues of this change. (For example, the last time I changed PD's clipboard mechanism, this fix didn't work because pasting into Paint.NET was downgraded to 24-bpp data only. Two years later, however, I can revert this because Paint.NET added PNG clipboard support in 2019, and it will preferentially use PNG data over the DIB data PD is now forced to place on the clipboard.) Anyway, I've been interrupted by my toddler roughly 1000x while trying to explain this issue, so I've probably repeated myself many times over. Sorry! I'm just frustrated with Google for what feels like the billionth time, and being forced to enable a Win-95-era clipboard format just to appease Chrome makes me very unhappy.
1 parent a56af48 commit 84f84be

File tree

3 files changed

+48
-12
lines changed

3 files changed

+48
-12
lines changed

Classes/pdClipboardMain.cls

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,16 @@ Friend Sub ClipboardCopy(ByVal copyMerged As Boolean, Optional ByVal updateUI As
344344
'
345345
'As always, PNG is the preferred interchange format for images. If you use a program that
346346
' doesn't copy/paste PNG-format data, get them to fix their software!
347-
'm_Clipboard.SetClipboardData_DelayedRendering CF_DIB
347+
348+
'UPDATE December 2020: due to clipboard issues in Google Chrome, I've reenabled explicit DIB
349+
' availability on the clipboard. This allows pasting into Chrome, and my hope is that as of
350+
' this year, any programs that support alpha channels will be smart enough to grab PNG or
351+
' DIBv5 data instead of bare DIB data (which is pre-composited against a white background to
352+
' ensure correct behavior in alpha-unaware software like MS Paint). This does break 32-bpp
353+
' pasting into some legacy software. As a workaround, PD's Edit > Special Copy/Cut menu allows
354+
' you to explicitly paste DIBv5 data which is an expensive workaround, but hey - at least it's
355+
' there if you need it.
356+
m_Clipboard.SetClipboardData_DelayedRendering CF_DIB
348357

349358
ElseIf (cFormat = pdcf_Bitmap) Then
350359
m_Clipboard.SetClipboardData_DelayedRendering CF_BITMAP
@@ -1770,14 +1779,21 @@ Friend Sub RenderAllClipboardFormatsManually()
17701779
RenderClipboard_PNG
17711780
RenderClipboard_BITMAP
17721781

1773-
'DIBs have some special considerations. For details, check out the comments in the ClipboardCopy function, but in a nutshell,
1774-
' PD doesn't render CF_DIB images because various programs handle alpha in varying ways, making it impossible to please everyone.
1775-
' Instead, as far as standard formats go, PD renders CF_BITMAP and CF_DIBv5 ONLY, with the assumption that "advanced" image
1776-
' editors can pick up the DIBv5 without trouble, while "basic" editors (like MS Paint) will take the CF_BITMAP copy.
1777-
' (This approach also spares the user's system a good chunk of resources, as we're not copying a crapload of mega-sized images
1778-
' in varying DIB formats.)
1782+
'DIBs have some special considerations. For details, check out the comments in the
1783+
' ClipboardCopy() function, but in a nutshell, PD doesn't render CF_DIB images because
1784+
' various programs handle alpha in varying ways, making it impossible to please everyone.
1785+
' Instead, as far as standard formats go, PD renders CF_BITMAP and CF_DIBv5 ONLY, with the
1786+
' assumption that "advanced" image editors can pick up the DIBv5 without trouble, while "basic"
1787+
' editors (like MS Paint) will take the CF_BITMAP copy. (This approach also spares the user's
1788+
' system a good chunk of resources, as we're not copying a crapload of mega-sized images in
1789+
' varying DIB formats.)
17791790
RenderClipboard_DIB True
17801791

1792+
'NOTE: regular DIB rendering has been reinstated to workaround issues with Chrome,
1793+
' which apparently won't grab BITMAP, DIBv5, or PNG formats if available (instead always
1794+
' defaulting to DIB regardless of its success or failure when retrieving)
1795+
RenderClipboard_DIB False
1796+
17811797
'Close the clipboard
17821798
m_Clipboard.ClipboardClose
17831799

@@ -2002,9 +2018,22 @@ Private Sub RenderClipboard_DIB(Optional ByVal useV5Header As Boolean = False)
20022018
PDDebug.LogAction "Clipboard copy update: allocating global memory for DIB object..."
20032019
End If
20042020

2005-
'DIBs should be unpremultiplied prior to copying; note that some esoteric software (*cough* XNView *cough*) wants
2006-
' premultiplied alpha, but the general consensus seems to be "use unpremultiplied", so that's what we do too.
2007-
If m_ClipboardDIB.GetAlphaPremultiplication Then m_ClipboardDIB.SetAlphaPremultiplication False
2021+
'DIBs should be unpremultiplied prior to copying; note that some esoteric software
2022+
' (*cough* XNView *cough*) wants premultiplied alpha, but the general consensus seems to be
2023+
' "use unpremultiplied", so that's what we do too.
2024+
'
2025+
'Note that alpha state is only relevant for DIBv5 DIBs; for compatibility reasons, PD always
2026+
' composites plain DIBs against a white backdrop, since alpha channel compatibility is so
2027+
' variable between apps.
2028+
Dim tmpDIB As pdDIB
2029+
Set tmpDIB = New pdDIB
2030+
2031+
If useV5Header Then
2032+
If m_ClipboardDIB.GetAlphaPremultiplication Then m_ClipboardDIB.SetAlphaPremultiplication False
2033+
Else
2034+
tmpDIB.CreateFromExistingDIB m_ClipboardDIB
2035+
tmpDIB.CompositeBackgroundColor 255, 255, 255
2036+
End If
20082037

20092038
'Figure out how much size is required for the global allocation. This is just (size_of_header + size_of_pixels).
20102039
Dim headerSize As Long
@@ -2016,7 +2045,11 @@ Private Sub RenderClipboard_DIB(Optional ByVal useV5Header As Boolean = False)
20162045
End If
20172046

20182047
Dim dibPointer As Long, dibSize As Long
2019-
m_ClipboardDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
2048+
If useV5Header Then
2049+
m_ClipboardDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
2050+
Else
2051+
tmpDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
2052+
End If
20202053

20212054
Dim memSize As Long
20222055
memSize = headerSize + dibSize

Classes/pdDIB.cls

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,9 @@ Friend Sub CompositeBackgroundColor(Optional ByVal newR As Byte = 255, Optional
18291829
'With our alpha channel complete, point dibPixels() away from the DIB
18301830
Me.UnwrapArrayFromDIB dibPixels
18311831

1832+
'A composited image is always premultiplied
1833+
Me.SetInitialAlphaPremultiplicationState True
1834+
18321835
End Sub
18331836

18341837
'Blend byte1 w/ byte2 based on mixRatio. mixRatio is expected to be a value between 0 and 1.

PhotoDemon.vbp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ Description="PhotoDemon Photo Editor"
462462
CompatibleMode="0"
463463
MajorVer=8
464464
MinorVer=9
465-
RevisionVer=297
465+
RevisionVer=301
466466
AutoIncrementVer=1
467467
ServerSupportFiles=0
468468
VersionComments="Copyright 2000-2020 Tanner Helland - photodemon.org"

0 commit comments

Comments
 (0)