Skip to content

Commit aaf715a

Browse files
committed
Implemented an optimization of the ToBitmap method based upon instead from (#1777).
1 parent 0980f53 commit aaf715a

File tree

1 file changed

+73
-21
lines changed

1 file changed

+73
-21
lines changed

src/Magick.NET.SystemDrawing/IMagickImageExtentions.cs

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,50 @@ public static Bitmap ToBitmapWithDensity<TQuantumType>(this IMagickImage<TQuantu
8181
where TQuantumType : struct, IConvertible
8282
=> ToBitmap(self, imageFormat, withDensity: true);
8383

84+
private static void CopyPixels<TQuantumType>(IUnsafePixelCollection<TQuantumType> pixels, BitmapData data, IMagickImage<TQuantumType> image, string mapping)
85+
where TQuantumType : struct, IConvertible
86+
{
87+
var destination = data.Scan0;
88+
for (var y = 0; y < image.Height; y++)
89+
{
90+
var bytes = pixels.ToByteArray(0, y, image.Width, 1, mapping);
91+
if (bytes is not null)
92+
Marshal.Copy(bytes, 0, destination, bytes.Length);
93+
94+
destination += data.Stride;
95+
}
96+
}
97+
98+
private static unsafe void CopyPixelsOptimized<TQuantumType>(IUnsafePixelCollection<TQuantumType> pixels, BitmapData data, IMagickImage<TQuantumType> image, uint blueIndex, uint greenIndex, uint redIndex, int alphaIndex)
99+
where TQuantumType : struct, IConvertible
100+
{
101+
#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
102+
var source = (TQuantumType*)pixels.GetAreaPointer(0, 0, image.Width, image.Height);
103+
#pragma warning restore CS8500
104+
var destination = (byte*)data.Scan0;
105+
var increment = alphaIndex == -1 ? 3 : 4;
106+
107+
var quantum = QuantumScaler.Create<TQuantumType>();
108+
for (var y = 0; y < image.Height; y++)
109+
{
110+
var startOfRow = destination;
111+
112+
for (var x = 0; x < image.Width; x++)
113+
{
114+
*destination = quantum.ScaleToByte(*(source + blueIndex));
115+
*(destination + 1) = quantum.ScaleToByte(*(source + greenIndex));
116+
*(destination + 2) = quantum.ScaleToByte(*(source + redIndex));
117+
if (alphaIndex != -1)
118+
*(destination + 3) = quantum.ScaleToByte(*(source + alphaIndex));
119+
120+
source += pixels.Channels;
121+
destination += increment;
122+
}
123+
124+
destination = startOfRow + data.Stride;
125+
}
126+
}
127+
84128
private static Bitmap ToBitmap<TQuantumType>(this IMagickImage<TQuantumType> self, bool withDensity)
85129
where TQuantumType : struct, IConvertible
86130
{
@@ -89,6 +133,12 @@ private static Bitmap ToBitmap<TQuantumType>(this IMagickImage<TQuantumType> sel
89133
var image = self;
90134

91135
var format = PixelFormat.Format24bppRgb;
136+
var mapping = "BGR";
137+
if (image.HasAlpha)
138+
{
139+
format = PixelFormat.Format32bppArgb;
140+
mapping = "BGRA";
141+
}
92142

93143
try
94144
{
@@ -98,23 +148,33 @@ private static Bitmap ToBitmap<TQuantumType>(this IMagickImage<TQuantumType> sel
98148
image.ColorSpace = ColorSpace.sRGB;
99149
}
100150

101-
if (image.HasAlpha)
102-
format = PixelFormat.Format32bppArgb;
103-
104151
using var pixels = image.GetPixelsUnsafe();
105-
var mapping = GetMapping(format);
152+
var blueIndex = pixels.GetChannelIndex(PixelChannel.Blue);
153+
var greenIndex = pixels.GetChannelIndex(PixelChannel.Green);
154+
var redIndex = pixels.GetChannelIndex(PixelChannel.Red);
155+
var alphaIndex = pixels.GetChannelIndex(PixelChannel.Alpha);
106156

107157
var bitmap = new Bitmap((int)image.Width, (int)image.Height, format);
108-
for (var y = 0; y < image.Height; y++)
158+
var data = bitmap.LockBits(new Rectangle(0, 0, (int)image.Width, (int)image.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
159+
try
160+
{
161+
if (format == PixelFormat.Format32bppArgb)
162+
{
163+
if (blueIndex is not null && greenIndex is not null && redIndex is not null && alphaIndex is not null)
164+
CopyPixelsOptimized(pixels, data, image, blueIndex.Value, greenIndex.Value, redIndex.Value, (int)alphaIndex.Value);
165+
else
166+
CopyPixels(pixels, data, image, mapping);
167+
}
168+
else
169+
{
170+
if (blueIndex is not null && greenIndex is not null && redIndex is not null)
171+
CopyPixelsOptimized(pixels, data, image, blueIndex.Value, greenIndex.Value, redIndex.Value, -1);
172+
else
173+
CopyPixels(pixels, data, image, mapping);
174+
}
175+
}
176+
finally
109177
{
110-
var row = new Rectangle(0, y, (int)image.Width, 1);
111-
var data = bitmap.LockBits(row, ImageLockMode.WriteOnly, format);
112-
var destination = data.Scan0;
113-
114-
var bytes = pixels.ToByteArray(0, y, image.Width, 1, mapping);
115-
if (bytes is not null)
116-
Marshal.Copy(bytes, 0, destination, bytes.Length);
117-
118178
bitmap.UnlockBits(data);
119179
}
120180

@@ -165,14 +225,6 @@ private static Density GetDefaultDensity(IMagickImage image)
165225
return image.Density.ChangeUnits(DensityUnit.PixelsPerInch);
166226
}
167227

168-
private static string GetMapping(PixelFormat format)
169-
=> format switch
170-
{
171-
PixelFormat.Format24bppRgb => "BGR",
172-
PixelFormat.Format32bppArgb => "BGRA",
173-
_ => throw new NotImplementedException(format.ToString()),
174-
};
175-
176228
private static bool IsSupportedImageFormat(ImageFormat format)
177229
=> format.Guid.Equals(ImageFormat.Bmp.Guid) ||
178230
format.Guid.Equals(ImageFormat.Gif.Guid) ||

0 commit comments

Comments
 (0)