Skip to content

Commit

Permalink
Clipboard workaround for Google Chrome bugs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tannerhelland committed Dec 19, 2020
1 parent a56af48 commit 84f84be
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 12 deletions.
55 changes: 44 additions & 11 deletions Classes/pdClipboardMain.cls
Expand Up @@ -344,7 +344,16 @@ Friend Sub ClipboardCopy(ByVal copyMerged As Boolean, Optional ByVal updateUI As
'
'As always, PNG is the preferred interchange format for images. If you use a program that
' doesn't copy/paste PNG-format data, get them to fix their software!
'm_Clipboard.SetClipboardData_DelayedRendering CF_DIB

'UPDATE December 2020: due to clipboard issues in Google Chrome, I've reenabled explicit DIB
' availability on the clipboard. This allows pasting into Chrome, and my hope is that as of
' this year, any programs that support alpha channels will be smart enough to grab PNG or
' DIBv5 data instead of bare DIB data (which is pre-composited against a white background to
' ensure correct behavior in alpha-unaware software like MS Paint). This does break 32-bpp
' pasting into some legacy software. As a workaround, PD's Edit > Special Copy/Cut menu allows
' you to explicitly paste DIBv5 data which is an expensive workaround, but hey - at least it's
' there if you need it.
m_Clipboard.SetClipboardData_DelayedRendering CF_DIB

ElseIf (cFormat = pdcf_Bitmap) Then
m_Clipboard.SetClipboardData_DelayedRendering CF_BITMAP
Expand Down Expand Up @@ -1770,14 +1779,21 @@ Friend Sub RenderAllClipboardFormatsManually()
RenderClipboard_PNG
RenderClipboard_BITMAP

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

'NOTE: regular DIB rendering has been reinstated to workaround issues with Chrome,
' which apparently won't grab BITMAP, DIBv5, or PNG formats if available (instead always
' defaulting to DIB regardless of its success or failure when retrieving)
RenderClipboard_DIB False

'Close the clipboard
m_Clipboard.ClipboardClose

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

'DIBs should be unpremultiplied prior to copying; note that some esoteric software (*cough* XNView *cough*) wants
' premultiplied alpha, but the general consensus seems to be "use unpremultiplied", so that's what we do too.
If m_ClipboardDIB.GetAlphaPremultiplication Then m_ClipboardDIB.SetAlphaPremultiplication False
'DIBs should be unpremultiplied prior to copying; note that some esoteric software
' (*cough* XNView *cough*) wants premultiplied alpha, but the general consensus seems to be
' "use unpremultiplied", so that's what we do too.
'
'Note that alpha state is only relevant for DIBv5 DIBs; for compatibility reasons, PD always
' composites plain DIBs against a white backdrop, since alpha channel compatibility is so
' variable between apps.
Dim tmpDIB As pdDIB
Set tmpDIB = New pdDIB

If useV5Header Then
If m_ClipboardDIB.GetAlphaPremultiplication Then m_ClipboardDIB.SetAlphaPremultiplication False
Else
tmpDIB.CreateFromExistingDIB m_ClipboardDIB
tmpDIB.CompositeBackgroundColor 255, 255, 255
End If

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

Dim dibPointer As Long, dibSize As Long
m_ClipboardDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
If useV5Header Then
m_ClipboardDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
Else
tmpDIB.RetrieveDIBPointerAndSize dibPointer, dibSize
End If

Dim memSize As Long
memSize = headerSize + dibSize
Expand Down
3 changes: 3 additions & 0 deletions Classes/pdDIB.cls
Expand Up @@ -1829,6 +1829,9 @@ Friend Sub CompositeBackgroundColor(Optional ByVal newR As Byte = 255, Optional
'With our alpha channel complete, point dibPixels() away from the DIB
Me.UnwrapArrayFromDIB dibPixels

'A composited image is always premultiplied
Me.SetInitialAlphaPremultiplicationState True

End Sub

'Blend byte1 w/ byte2 based on mixRatio. mixRatio is expected to be a value between 0 and 1.
Expand Down
2 changes: 1 addition & 1 deletion PhotoDemon.vbp
Expand Up @@ -462,7 +462,7 @@ Description="PhotoDemon Photo Editor"
CompatibleMode="0"
MajorVer=8
MinorVer=9
RevisionVer=297
RevisionVer=301
AutoIncrementVer=1
ServerSupportFiles=0
VersionComments="Copyright 2000-2020 Tanner Helland - photodemon.org"
Expand Down

0 comments on commit 84f84be

Please sign in to comment.