@@ -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