Note
For information about the possible usable PixelFormats on different platforms see the Remarks section of the ConvertPixelFormat method.
public static IReadWriteBitmapData GetReadWriteBitmapData(
this Bitmap bitmap,
WorkingColorSpace workingColorSpace,
Color backColor = default,
byte alphaThreshold = 128
)
<ExtensionAttribute>
Public Shared Function GetReadWriteBitmapData (
bitmap As Bitmap,
workingColorSpace As WorkingColorSpace,
Optional backColor As Color = Nothing,
Optional alphaThreshold As Byte = 128
) As IReadWriteBitmapData
public:
[ExtensionAttribute]
static IReadWriteBitmapData^ GetReadWriteBitmapData(
Bitmap^ bitmap,
WorkingColorSpace workingColorSpace,
Color backColor = Color(),
unsigned char alphaThreshold = 128
)
[<ExtensionAttribute>]
static member GetReadWriteBitmapData :
bitmap : Bitmap *
workingColorSpace : WorkingColorSpace *
?backColor : Color *
?alphaThreshold : byte
(* Defaults:
let _backColor = defaultArg backColor new Color()
let _alphaThreshold = defaultArg alphaThreshold 128
*)
-> IReadWriteBitmapData
All possible PixelFormats are supported, of which a Bitmap can be created in the current operating system.
If alphaThreshold is zero, then setting a fully transparent pixel in a bitmap with indexed or single-bit-alpha pixel format will blend the pixel to set with backColor even if the bitmap can handle transparent pixels.
If alphaThreshold is 1, then the result color of setting a pixel of a bitmap with indexed or single-bit-alpha pixel format will be transparent only if the color to set is completely transparent (has zero alpha).
If alphaThreshold is 255, then the result color of setting a pixel of a bitmap with indexed or single-bit-alpha pixel format will be opaque only if the color to set is completely opaque (its alpha value is 255).
If a pixel of a bitmap without alpha gradient support is set by the IWritableBitmapData.SetPixel/IWritableBitmapDataRow.SetColor methods or by the IReadWriteBitmapDataRow indexer, and the pixel has an alpha value that is greater than alphaThreshold, then the pixel to set will be blended with backColor.
The workingColorSpace parameter indicates the preferred color space mode when working with the result bitmap data.
Blending operations performed by this library (eg. by IWritableBitmapData.SetPixel when blending in necessary as described above,
or by the DrawInto extension methods) respect the value of this parameter.
Blending in the linear color space produces natural results but the operation is a bit slower if the actual pixel format is not in the linear color space
and the result is different from the results of most applications including popular image processors and web browsers.
See the Remarks section of the WorkingColorSpace enumeration for more details.
The following example demonstrates how easily you can copy the content of a 32-bit ARGB image into an 8-bit indexed one by using the GetReadableBitmapData and GetWritableBitmapData methods:
var targetFormat = PixelFormat.Format8bppIndexed; // feel free to try other formats as well
using (Bitmap bmpSrc = Icons.Shield.ExtractBitmap(new Size(256, 256)))
using (Bitmap bmpDst = new Bitmap(256, 256, targetFormat))
{
using (IReadableBitmapData dataSrc = bmpSrc.GetReadableBitmapData())
using (IWritableBitmapData dataDst = bmpDst.GetWritableBitmapData())
{
for (int y = 0; y < dataSrc.Height; y++)
{
for (int x = 0; x < dataSrc.Width; x++)
{
// Please note that bmpDst.SetPixel would not work for indexed formats
// and even when it can be used it would be much slower.
dataDst.SetPixel(x, y, dataSrc.GetPixel(x, y));
}
}
}
bmpSrc.SaveAsPng(@"c:\temp\bmpSrc.png");
bmpDst.SaveAsPng(@"c:\temp\bmpDst.png"); // or saveAsGif/SaveAsTiff to preserve the indexed format
}
The example above produces the following results:
bmpSrc.png | ![]() |
bmpDst.png | ![]() |
If the pixels are not accessed randomly, then the sequential enumeration of rows can be a bit faster:
// Replace the body of the inner using block of the previous example with the following code:
IReadableBitmapDataRowMovable rowSrc = dataSrc.FirstRow;
IWritableBitmapDataRowMovable rowDst = dataDst.FirstRow;
do
{
for (int x = 0; x < dataSrc.Width; x++)
rowDst[x] = rowSrc[x];
} while (rowSrc.MoveNextRow() && rowDst.MoveNextRow());
For parallel processing you can retrieve multiple rows by the indexer and process them concurrently. When targeting .NET Framework 4.0 or newer, the example above can be easily re-written to use parallel processing:
// The parallel version of same body as in the previous example:
Parallel.For(0, dataSrc.Height, y =>
{
IReadableBitmapDataRow rowSrc = dataSrc[y];
IWritableBitmapDataRow rowDst = dataDst[y];
for (int x = 0; x < dataSrc.Width; x++)
rowDst[x] = rowSrc[x];
});
The following example demonstrates how to use the read-write IReadWriteBitmapData returned by the GetReadWriteBitmapData method to manipulate a Bitmap in-place:
// This example produces the same result as the MakeGrayscale extension method without a ditherer:
using (Bitmap bmp = Icons.Shield.ExtractBitmap(new Size(256, 256)))
{
bmp.SaveAsPng(@"c:\temp\before.png");
using (IReadWriteBitmapData bmpData = bmp.GetReadWriteBitmapData())
{
IReadWriteBitmapDataRowMovable row = bmpData.FirstRow;
do
{
for (int x = 0; x < bmpData.Width; x++)
row[x] = row[x].ToGray();
} while (row.MoveNextRow());
}
bmp.SaveAsPng(@"c:\temp\after.png");
}
The example above produces the following results:
before.png | ![]() |
after.png | ![]() |