mirror of
https://github.com/greenshot/greenshot
synced 2025-08-20 21:43:24 -07:00
Better documentation for the DibHelper [skip ci]
This commit is contained in:
parent
9ac6d22c96
commit
6b90f2c3ff
1 changed files with 29 additions and 15 deletions
|
@ -35,15 +35,20 @@ namespace Greenshot.Base.Core
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class DibHelper
|
internal static class DibHelper
|
||||||
{
|
{
|
||||||
|
private const double DpiToPelsPerMeter = 39.3701;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS.
|
/// Converts the Bitmap to a Device Independent Bitmap format of type BITFIELDS.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sourceBitmap">Bitmap to convert to DIB</param>
|
/// <param name="sourceBitmap">Bitmap to convert to DIB</param>
|
||||||
/// <returns>The image converted to DIB, in bytes.</returns>
|
/// <returns>byte{} with the image converted to DIB</returns>
|
||||||
public static byte[] ConvertToDib(this Bitmap sourceBitmap)
|
public static byte[] ConvertToDib(this Bitmap sourceBitmap)
|
||||||
{
|
{
|
||||||
if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap));
|
if (sourceBitmap == null) throw new ArgumentNullException(nameof(sourceBitmap));
|
||||||
|
|
||||||
|
var area = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
|
||||||
|
|
||||||
|
// If the supplied format doesn't match 32bpp, we need to convert it first, and dispose the new bitmap afterwards
|
||||||
bool needsDisposal = false;
|
bool needsDisposal = false;
|
||||||
if (sourceBitmap.PixelFormat != PixelFormat.Format32bppArgb)
|
if (sourceBitmap.PixelFormat != PixelFormat.Format32bppArgb)
|
||||||
{
|
{
|
||||||
|
@ -51,22 +56,27 @@ namespace Greenshot.Base.Core
|
||||||
var clonedImage = ImageHelper.CreateEmptyLike(sourceBitmap, Color.Transparent, PixelFormat.Format32bppArgb);
|
var clonedImage = ImageHelper.CreateEmptyLike(sourceBitmap, Color.Transparent, PixelFormat.Format32bppArgb);
|
||||||
using (var graphics = Graphics.FromImage(clonedImage))
|
using (var graphics = Graphics.FromImage(clonedImage))
|
||||||
{
|
{
|
||||||
graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, clonedImage.Width, clonedImage.Height));
|
graphics.DrawImage(sourceBitmap, area);
|
||||||
}
|
}
|
||||||
sourceBitmap = clonedImage;
|
sourceBitmap = clonedImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All the pixels take this many bytes:
|
||||||
var bitmapSize = 4 * sourceBitmap.Width * sourceBitmap.Height;
|
var bitmapSize = 4 * sourceBitmap.Width * sourceBitmap.Height;
|
||||||
|
// The bitmap info hear takes this many bytes:
|
||||||
var bitmapInfoHeaderSize = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
|
var bitmapInfoHeaderSize = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
|
||||||
|
// The bitmap info size is the header + 3 RGBQUADs
|
||||||
var bitmapInfoSize = bitmapInfoHeaderSize + 3 * Marshal.SizeOf(typeof(RGBQUAD));
|
var bitmapInfoSize = bitmapInfoHeaderSize + 3 * Marshal.SizeOf(typeof(RGBQUAD));
|
||||||
|
|
||||||
// Create a byte [] to contain the DIB
|
// Create a byte [] to contain the complete DIB (with .NET 5 and upwards, we could write the pixels directly to a stream)
|
||||||
var fullBmpBytes = new byte[bitmapInfoSize + bitmapSize];
|
var fullBmpBytes = new byte[bitmapInfoSize + bitmapSize];
|
||||||
|
// Get a span for this, this simplifies the code a bit
|
||||||
var fullBmpSpan = fullBmpBytes.AsSpan();
|
var fullBmpSpan = fullBmpBytes.AsSpan();
|
||||||
// Cast the span to be of type BITMAPINFOHEADER so we can assign values
|
// Cast the span to be of type BITMAPINFOHEADER so we can assign values
|
||||||
// TODO: in .NET 6 we could do a AsRef
|
// TODO: in .NET 6 we could do a AsRef, and even write to a stream directly
|
||||||
var bitmapInfoHeader = MemoryMarshal.Cast<byte, BITMAPINFOHEADER>(fullBmpSpan);
|
var bitmapInfoHeader = MemoryMarshal.Cast<byte, BITMAPINFOHEADER>(fullBmpSpan);
|
||||||
|
|
||||||
|
// Fill up the bitmap info header
|
||||||
bitmapInfoHeader[0].biSize = (uint)bitmapInfoHeaderSize;
|
bitmapInfoHeader[0].biSize = (uint)bitmapInfoHeaderSize;
|
||||||
bitmapInfoHeader[0].biWidth = sourceBitmap.Width;
|
bitmapInfoHeader[0].biWidth = sourceBitmap.Width;
|
||||||
bitmapInfoHeader[0].biHeight = sourceBitmap.Height;
|
bitmapInfoHeader[0].biHeight = sourceBitmap.Height;
|
||||||
|
@ -74,34 +84,37 @@ namespace Greenshot.Base.Core
|
||||||
bitmapInfoHeader[0].biBitCount = 32;
|
bitmapInfoHeader[0].biBitCount = 32;
|
||||||
bitmapInfoHeader[0].biCompression = BI_COMPRESSION.BI_BITFIELDS;
|
bitmapInfoHeader[0].biCompression = BI_COMPRESSION.BI_BITFIELDS;
|
||||||
bitmapInfoHeader[0].biSizeImage = (uint)bitmapSize;
|
bitmapInfoHeader[0].biSizeImage = (uint)bitmapSize;
|
||||||
bitmapInfoHeader[0].biXPelsPerMeter = (int)(sourceBitmap.HorizontalResolution * 39.3701);
|
bitmapInfoHeader[0].biXPelsPerMeter = (int)(sourceBitmap.HorizontalResolution * DpiToPelsPerMeter);
|
||||||
bitmapInfoHeader[0].biYPelsPerMeter = (int)(sourceBitmap.VerticalResolution * 39.3701);
|
bitmapInfoHeader[0].biYPelsPerMeter = (int)(sourceBitmap.VerticalResolution * DpiToPelsPerMeter);
|
||||||
|
|
||||||
// The aforementioned "BITFIELDS": color masks applied to the Int32 pixel value to get the R, G and B values.
|
// Specify the color masks applied to the Int32 pixel value to get the R, G and B values.
|
||||||
var rgbQuads = MemoryMarshal.Cast<byte, RGBQUAD>(fullBmpSpan.Slice(Marshal.SizeOf(typeof(BITMAPINFOHEADER))));
|
var rgbQuads = MemoryMarshal.Cast<byte, RGBQUAD>(fullBmpSpan.Slice(Marshal.SizeOf(typeof(BITMAPINFOHEADER))));
|
||||||
rgbQuads[0].rgbRed = 255;
|
rgbQuads[0].rgbRed = 255;
|
||||||
rgbQuads[1].rgbGreen = 255;
|
rgbQuads[1].rgbGreen = 255;
|
||||||
rgbQuads[2].rgbBlue = 255;
|
rgbQuads[2].rgbBlue = 255;
|
||||||
|
|
||||||
// Now copy the lines, in reverse to the byte array
|
// Now copy the lines, in reverse (bmp is upside down) to the byte array
|
||||||
var sourceBitmapData = sourceBitmap.LockBits(new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, sourceBitmap.PixelFormat);
|
var sourceBitmapData = sourceBitmap.LockBits(area, ImageLockMode.ReadOnly, sourceBitmap.PixelFormat);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get a span for the real bitmap bytes, which starts after the header
|
// Get a span for the real bitmap bytes, which starts after the bitmapinfo (header + 3xRGBQuad)
|
||||||
var bitmapSpan = fullBmpSpan.Slice(bitmapInfoSize);
|
var bitmapSpan = fullBmpSpan.Slice(bitmapInfoSize);
|
||||||
// Make sure we also have a span to copy from
|
// Make sure we also have a span to copy from, by taking the pointer from the locked bitmap
|
||||||
Span<byte> bitmapSourceSpan;
|
Span<byte> bitmapSourceSpan;
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
bitmapSourceSpan = new Span<byte>(sourceBitmapData.Scan0.ToPointer(), sourceBitmapData.Stride * sourceBitmapData.Height);
|
bitmapSourceSpan = new Span<byte>(sourceBitmapData.Scan0.ToPointer(), sourceBitmapData.Stride * sourceBitmapData.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop over all the lines and copy the top line to the bottom (flipping the image)
|
// Loop over all the bitmap lines
|
||||||
for (int y = 0; y < sourceBitmap.Height; y++)
|
for (int destinationY = 0; destinationY < sourceBitmap.Height; destinationY++)
|
||||||
{
|
{
|
||||||
var sourceY = (sourceBitmap.Height - 1) - y;
|
// Calculate the y coordinate for the bottom up. (flipping the image)
|
||||||
|
var sourceY = (sourceBitmap.Height - 1) - destinationY;
|
||||||
|
// Make a Span for the source bitmap pixels
|
||||||
var sourceLine = bitmapSourceSpan.Slice(sourceBitmapData.Stride * sourceY, 4 * sourceBitmap.Width);
|
var sourceLine = bitmapSourceSpan.Slice(sourceBitmapData.Stride * sourceY, 4 * sourceBitmap.Width);
|
||||||
var destinationLine = bitmapSpan.Slice(y * 4 * sourceBitmap.Width);
|
// Make a Span for the destination dib pixels
|
||||||
|
var destinationLine = bitmapSpan.Slice(destinationY * 4 * sourceBitmap.Width);
|
||||||
sourceLine.CopyTo(destinationLine);
|
sourceLine.CopyTo(destinationLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +123,7 @@ namespace Greenshot.Base.Core
|
||||||
sourceBitmap.UnlockBits(sourceBitmapData);
|
sourceBitmap.UnlockBits(sourceBitmapData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we created a new bitmap, we need to dispose this
|
||||||
if (needsDisposal)
|
if (needsDisposal)
|
||||||
{
|
{
|
||||||
sourceBitmap.Dispose();
|
sourceBitmap.Dispose();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue