如何修复使用BitmapData将全黑PNG转换为全白PNG时无效的问题
I built a small test example with the goal of changing all pixels in a PNG image to white. I chose to use BitmapData because I heard it has better performance. If this example works correctly, I plan to adjust the target pixel range and add different pixel color modification conditions later.
However, I'm having an issue with this simple test: after inputting a 50×50 all-black PNG image, the output is still an all-black PNG instead of the expected all-white PNG. Here's my C# code:
public static void TestConvertAllBlackBitmapToAllWhite() { string allBlackPNGFullFilePath = @"C:\Users\{Username}\Desktop\50x50AllBlack.png"; Bitmap allBlackBitmap = new Bitmap(allBlackPNGFullFilePath); Bitmap newBitmap = (Bitmap)allBlackBitmap.Clone(); Size size = newBitmap.Size; PixelFormat pixelFormat = newBitmap.PixelFormat; byte bitDepth = (byte)(pixelFormat == PixelFormat.Format32bppArgb ? 4 : 3); Rectangle rectangle = new Rectangle(Point.Empty, size); BitmapData bitmapData = newBitmap.LockBits(rectangle, ImageLockMode.ReadOnly, pixelFormat); int dataSize = bitmapData.Stride * bitmapData.Height; byte[] data = new byte[dataSize]; Marshal.Copy(bitmapData.Scan0, data, 0, dataSize); Color white = Color.White; for (int y = 0; y < size.Height; y++) { for (int x = 0; x < size.Width; x++) { // Get Index int index = y * bitmapData.Stride + x * bitDepth; // Set Pixel Color data[index] = white.B; data[index + 1] = white.G; data[index + 2] = white.R; } } Marshal.Copy(data, 0, bitmapData.Scan0, data.Length); newBitmap.UnlockBits(bitmapData); // Save New Converted Bitmap string originalFileName = Path.GetFileNameWithoutExtension(allBlackPNGFullFilePath); string directory = Path.GetDirectoryName(allBlackPNGFullFilePath); string newBitmapFileName = originalFileName + "_Converted"; string newBitmapFullFileName = directory + Path.DirectorySeparatorChar.ToString() + newBitmapFileName + ".png"; newBitmap.Save(newBitmapFullFileName, ImageFormat.Png); }
Could someone help me figure out how to modify this code to get an all-white PNG output?
The main issue in your code is the ImageLockMode.ReadOnly flag you’re using when locking the bitmap bits. When you lock the bitmap in read-only mode, any changes you make to the underlying data won’t be written back to the bitmap—that’s why your output stays black.
Here are the key fixes and improvements:
- Switch to
ImageLockMode.ReadWrite: This mode lets you both read existing pixel data and write your modifications back to the bitmap. - Handle the Alpha channel (for 32bppArgb format): If your image uses 32-bit ARGB, set the alpha value to 255 (fully opaque) to ensure the white pixels are visible.
- Simplify file path construction: Use
Path.Combineinstead of string concatenation to avoid cross-platform path separator issues. - Dispose bitmap objects: Prevent memory leaks by disposing of
Bitmapinstances when you’re done with them.
Here’s the modified code:
using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.IO; public static void TestConvertAllBlackBitmapToAllWhite() { string allBlackPNGFullFilePath = @"C:\Users\{Username}\Desktop\50x50AllBlack.png"; Bitmap allBlackBitmap = new Bitmap(allBlackPNGFullFilePath); Bitmap newBitmap = (Bitmap)allBlackBitmap.Clone(); Size size = newBitmap.Size; PixelFormat pixelFormat = newBitmap.PixelFormat; byte bitDepth = (byte)(pixelFormat == PixelFormat.Format32bppArgb ? 4 : 3); Rectangle rectangle = new Rectangle(Point.Empty, size); // Fix 1: Use ReadWrite mode to allow modifying the bitmap BitmapData bitmapData = newBitmap.LockBits(rectangle, ImageLockMode.ReadWrite, pixelFormat); int dataSize = bitmapData.Stride * bitmapData.Height; byte[] data = new byte[dataSize]; Marshal.Copy(bitmapData.Scan0, data, 0, dataSize); Color white = Color.White; for (int y = 0; y < size.Height; y++) { for (int x = 0; x < size.Width; x++) { int index = y * bitmapData.Stride + x * bitDepth; // Set RGB values data[index] = white.B; data[index + 1] = white.G; data[index + 2] = white.R; // Fix 2: Set alpha channel for 32-bit images to ensure opacity if (bitDepth == 4) { data[index + 3] = white.A; // 255 for fully opaque white } } } Marshal.Copy(data, 0, bitmapData.Scan0, data.Length); newBitmap.UnlockBits(bitmapData); // Fix 3: Use Path.Combine for safe, cross-platform path building string originalFileName = Path.GetFileNameWithoutExtension(allBlackPNGFullFilePath); string directory = Path.GetDirectoryName(allBlackPNGFullFilePath); string newBitmapFullFileName = Path.Combine(directory, $"{originalFileName}_Converted.png"); newBitmap.Save(newBitmapFullFileName, ImageFormat.Png); // Fix 4: Dispose bitmaps to free up system resources allBlackBitmap.Dispose(); newBitmap.Dispose(); }
Quick Notes:
- For larger images, you could optimize performance by processing the byte array in a single flat loop instead of nested x/y loops, but your current approach works perfectly for small 50x50 images.
- Always double-check the pixel format of your input image—some PNGs might use indexed color modes, which would require additional handling, but your code covers the most common 24/32-bit formats.
内容的提问来源于stack exchange,提问作者Shiasu-sama

