Прежде чем ничего, я отмечу, что принимаю решение на C# или VB.Net.Замените цвет изображения, используя Lockbits
У меня есть этот старый код, который я пытаюсь реорганизовать, чтобы избежать вредных привычек и производительности неэффективности использования GetPixel
/SetPixel
методы:
<Extension>
Public Function ChangeColor(ByVal sender As Image,
ByVal oldColor As Color,
ByVal newColor As Color) As Image
Dim bmp As New Bitmap(sender.Width, sender.Height, sender.PixelFormat)
Dim x As Integer = 0
Dim y As Integer = 0
While (x < bmp.Width)
y = 0
While y < bmp.Height
If DirectCast(sender, Bitmap).GetPixel(x, y) = oldColor Then
bmp.SetPixel(x, y, newColor)
End If
Math.Max(Threading.Interlocked.Increment(y), y - 1)
End While
Math.Max(Threading.Interlocked.Increment(x), x - 1)
End While
Return bmp
End Function
Итак, после прочтения наибольшего количества голосов решения here с использованием LockBits
подход, я пытаюсь адаптировать код к моим потребностям, использовать параметр Color
в качестве параметра, а не последовательность байтов (потому что по существу то же самое):
<Extension>
Public Function ChangeColor(ByVal sender As Image,
ByVal oldColor As Color,
ByVal newColor As Color) As Image
Dim bmp As Bitmap = DirectCast(sender.Clone, Bitmap)
' Lock the bitmap's bits.
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As BitmapData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat)
' Get the address of the first line.
Dim ptr As IntPtr = bmpData.Scan0
' Declare an array to hold the bytes of the bitmap.
Dim numBytes As Integer = (bmpData.Stride * bmp.Height)
Dim rgbValues As Byte() = New Byte(numBytes - 1) {}
' Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, numBytes)
' Manipulate the bitmap.
For i As Integer = 0 To rgbValues.Length - 1 Step 3
If (Color.FromArgb(rgbValues(i), rgbValues(i + 1), rgbValues(i + 2)) = oldColor) Then
rgbValues(i) = newColor.R
rgbValues(i + 1) = newColor.G
rgbValues(i + 2) = newColor.B
End If
Next i
' Copy the RGB values back to the bitmap.
Marshal.Copy(rgbValues, 0, ptr, numBytes)
' Unlock the bits.
bmp.UnlockBits(bmpData)
Return bmp
End Function
У меня есть две проблемы с методом расширения, первое, что если pixelformat не является Format24bppRgb
в качестве исходного примера, тогда все идет не так, исключение IndexOutOfRange
выбрано в цикле, я полагаю, это связано с тем, что я читаю 3 байта (RGB) вместо 4 (ARGB), но я не уверен, как его адаптировать для любого исходного пиксельного формата, который я могу передать функции.
Второе, что если я использую Format24bppRgb
в качестве исходного примера C#, цвет меняется на черный.
Обратите внимание, что я не уверен, что исходное решение, заданное в вопросе C#, которое я связал, неверно, потому что, по мнению их комментариев, в некотором роде это неправильно.
Это способ, которым я пытаюсь использовать:
' This function creates a bitmap of a solid color.
Dim srcImg As Bitmap = ImageUtil.CreateSolidcolorBitmap(New Size(256, 256), Color.Red)
Dim modImg As Image = srcImg.ChangeColor(Color.Red, Color.Blue)
PictureBox1.BackgroundImage = srcImg
PictureBox2.BackgroundImage = modImg
Итак, я добавил примеры для форматов пикселей 1, 4, 8, 24 и 32 бит. Конечно, вы можете рассчитать цвета для 15/16/64 и других форматов. Я использовал здесь небезопасные методы, но вы можете использовать 'Marshal.Copy' для копирования данных там и обратно с помощью управляемого массива. – taffer