diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs
index 02a7f03f1..e87f3d1d6 100644
--- a/src/Greenshot.Base/Core/ClipboardHelper.cs
+++ b/src/Greenshot.Base/Core/ClipboardHelper.cs
@@ -175,7 +175,7 @@ EndSelection:<<<<<<<4
try
{
- // For BUG-1935 this was changed from looping ourselfs, or letting MS retry...
+ // For BUG-1935 this was changed from looping ourselves, or letting MS retry...
Clipboard.SetDataObject(ido, copy, 15, 200);
}
catch (Exception clipboardSetException)
@@ -866,19 +866,13 @@ EndSelection:<<<<<<<4
{
if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIB))
{
- using (MemoryStream tmpBmpStream = new MemoryStream())
- {
- // Save image as BMP
- SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
- ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
-
- dibStream = new MemoryStream();
- // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
- dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int) tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH);
- }
+ // Create the stream for the clipboard
+ dibStream = new MemoryStream();
+ var dibBytes = DibHelper.ConvertToDib(imageToSave);
+ dibStream.Write(dibBytes,0, dibBytes.Length);
// Set the DIB to the clipboard DataObject
- dataObject.SetData(DataFormats.Dib, true, dibStream);
+ dataObject.SetData(DataFormats.Dib, false, dibStream);
}
}
catch (Exception dibEx)
diff --git a/src/Greenshot.Base/Core/CoreConfiguration.cs b/src/Greenshot.Base/Core/CoreConfiguration.cs
index 4776a3985..ead32a464 100644
--- a/src/Greenshot.Base/Core/CoreConfiguration.cs
+++ b/src/Greenshot.Base/Core/CoreConfiguration.cs
@@ -658,16 +658,6 @@ namespace Greenshot.Base.Core
{
WebRequestReadWriteTimeout = 100;
}
-
- // Workaround for the Windows 11 clipboard issue found here: https://github.com/greenshot/greenshot/issues/348
- if (WindowsVersion.IsWindows11OrLater)
- {
- // If the format DIB is used, remove it and replace it with BITMAP.
- if (ClipboardFormats.Contains(ClipboardFormat.DIB))
- {
- ClipboardFormats = ClipboardFormats.Where(cf => cf != ClipboardFormat.DIB).Append(ClipboardFormat.BITMAP).ToList();
- }
- }
}
///
diff --git a/src/Greenshot.Base/Core/DibHelper.cs b/src/Greenshot.Base/Core/DibHelper.cs
new file mode 100644
index 000000000..70708a0b5
--- /dev/null
+++ b/src/Greenshot.Base/Core/DibHelper.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using Greenshot.Base.UnmanagedHelpers;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Though Greenshot implements the specs for the DIB image format,
+ /// it seems to cause a lot of issues when using the clipboard.
+ /// There is some research done about the DIB on the clipboard, this code was taking from:
+ /// here
+ ///
+ internal static class DibHelper
+ {
+ ///
+ /// Converts the image to Device Independent Bitmap format of type BITFIELDS.
+ /// This is (wrongly) accepted by many applications as containing transparency,
+ /// so I'm abusing it for that.
+ ///
+ /// Image to convert to DIB
+ /// The image converted to DIB, in bytes.
+ public static byte[] ConvertToDib(Image image)
+ {
+ byte[] bm32bData;
+ int width = image.Width;
+ int height = image.Height;
+ // Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
+ using (var bm32b = ImageHelper.CreateEmptyLike(image, Color.Transparent, PixelFormat.Format32bppArgb))
+ {
+ using (var graphics = Graphics.FromImage(bm32b))
+ {
+ graphics.DrawImage(image, new Rectangle(0, 0, bm32b.Width, bm32b.Height));
+ }
+ // Bitmap format has its lines reversed.
+ bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
+ bm32bData = GetImageData(bm32b, out var stride);
+ }
+ // BITMAPINFOHEADER struct for DIB.
+ uint hdrSize = 0x28;
+ var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
+ var bitmapInfoHeader = MemoryMarshal.Cast(fullImage.AsSpan());
+
+ bitmapInfoHeader[0].biSize = hdrSize;
+ bitmapInfoHeader[0].biWidth = width;
+ bitmapInfoHeader[0].biHeight = height;
+ bitmapInfoHeader[0].biPlanes = 1;
+ bitmapInfoHeader[0].biBitCount = 32;
+ bitmapInfoHeader[0].biCompression = BI_COMPRESSION.BI_BITFIELDS;
+ bitmapInfoHeader[0].biSizeImage = (uint)bm32bData.Length;
+ bitmapInfoHeader[0].biXPelsPerMeter = (int)(image.HorizontalResolution * 39.3701);
+ bitmapInfoHeader[0].biYPelsPerMeter = (int)(image.VerticalResolution * 39.3701);
+
+
+ // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
+ bitmapInfoHeader[0].bV5RedMask = 0x00FF0000;
+ bitmapInfoHeader[0].bV5GreenMask = 0x0000FF00;
+ bitmapInfoHeader[0].bV5BlueMask = 0x000000FF;
+
+ // These are all 0. Since .net clears new arrays, don't bother writing them.
+ //Int32 biClrUsed = 0;
+ //Int32 biClrImportant = 0;
+
+ Array.Copy(bm32bData, 0, fullImage, hdrSize + 12, bm32bData.Length);
+ return fullImage;
+ }
+
+ ///
+ /// Gets the raw bytes from an image.
+ ///
+ /// The image to get the bytes from.
+ /// Stride of the retrieved image data.
+ /// The raw bytes of the image
+ public static byte[] GetImageData(Bitmap sourceImage, out int stride)
+ {
+ BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, sourceImage.PixelFormat);
+ stride = sourceData.Stride;
+ byte[] data = new byte[stride * sourceImage.Height];
+ Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
+ sourceImage.UnlockBits(sourceData);
+ return data;
+ }
+ }
+}
diff --git a/src/Greenshot.Base/Core/ImageHelper.cs b/src/Greenshot.Base/Core/ImageHelper.cs
index 60d846eac..7565694e3 100644
--- a/src/Greenshot.Base/Core/ImageHelper.cs
+++ b/src/Greenshot.Base/Core/ImageHelper.cs
@@ -1497,16 +1497,17 @@ namespace Greenshot.Base.Core
///
/// the source bitmap as the specifications for the new bitmap
/// The color to fill with, or Color.Empty to take the default depending on the pixel format
- ///
- public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor)
+ /// PixelFormat
+ /// Bitmap
+ public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor, PixelFormat? pixelFormat = null)
{
- PixelFormat pixelFormat = sourceImage.PixelFormat;
+ pixelFormat ??= sourceImage.PixelFormat;
if (backgroundColor.A < 255)
{
pixelFormat = PixelFormat.Format32bppArgb;
}
- return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat.Value, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
}
///