Is a ColorMatrix the fastest way to edit bitmaps? (LockBits)

I've made a function that makes bitmaps transparent via a ColorMatrix but now I'm wondering if that is truly the fastest way, because I can see that it takes a while (not that much, but enough to notice) to update the picture when I use a slider to adjust the transparency.

Is there perhaps an imageformat that takes an alpha-value for the whole picture instead of per pixel, so that I don't have to use a colormatrix to go through every pixel to update the transparency?

Edit1: for example, setting the opacity of a form updates instantly, so how can I change a bitmap's transparency that fast in managed code - or is that just asking too much of VB?

Edit2: LockBits seems to promise better speeds - examples are welcome :)

Edit3: okay, I tried both ColorMatrix and LockBits methods, and they seem to give the same speed so back to square one: is this the fastest possible?

[889 byte] By [nogChoco] at [2007-12-30]
# 1

Hi,

I'd be intersted in seeing your code.

By the way i've done a small project that adjusts the forecolor of a control and the 4 sliders adjust the A ,R, G, B values respectively to create a colorMixer.

See >> http://www.programmersheaven.com/download/46787/download.aspx

I guess you are doing this with a bitmap picture in the same way.

I'm wondering if doing the Alpha channel adjust on a bitmap in memory like in a buffer would be any faster on the Mouse Down event in a slider.

Then on the Mouse Up event, post the updated contents to the pictureBox?

I'd still like to see your code if you's care to share it here?

Regards,

S_DS

Spidermans_DarkSide at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 2
Actually, it was the trackbar that was causing most of the delays, so I replaced it with a label to track mousedown/move and I'm relatively happy with the speed I'm getting now, although suggestions to speed it up are still welcome :)

My code is for a ColorPicker as well, but one with a full

hue-colorwheel, not just one color. I'm blending a fully saturated

picture (of the colorwheel) with the grayscale version of itself, so

that I can simulate a saturation level.

The LockBits method (memorybuffer method) is pretty much the same speed as a ColorMatrix, but for some reason, it corrupts the resolution on one of my testpics even though I'm only working on the Alpha value of the BitmapData. It seems to give a more linear blending-result, though, so I'll probably end up using this method.

Here's a picture of the ColorPicker:
http://img157.imageshack.us/my.php?image=colorpicker2ry6.png

And here's the code to compare the ColorMatrix and LockBits methods:



Public Class Form1

Private zValue As Single

Private WithEvents zLabel As New Label
Private WithEvents zPB1 As New PictureBox
Private WithEvents zPB2 As New PictureBox
Private WithEvents zTimer As New Windows.Forms.Timer

Private zBitmap As New Bitmap("c:\ColorPicker2.png")
Private zGrayScaleBitmap As Bitmap = GrayScaleBitmap(zBitmap)

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
With Me
.StartPosition = FormStartPosition.CenterScreen
.Size =
New Size(700, 600)
.DoubleBuffered =
True
.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
.SetStyle(ControlStyles.AllPaintingInWmPaint,
True)
.SetStyle(ControlStyles.UserPaint,
True)
.Text =
"ColorMatrix at top, LockBits at Bottom"
End With

zPB1.Dock = DockStyle.Top
zPB1.Height = 260
zPB1.BackColor = Color.LightBlue
Me.Controls.Add(zPB1)
zPB2.Dock = DockStyle.Top
zPB2.Height = 260
zPB2.BackColor = Color.LightGreen
Me.Controls.Add(zPB2)
zPB2.BringToFront()
zLabel.Dock = DockStyle.Fill
Me.Controls.Add(zLabel)
zLabel.BringToFront()
zLabel.BorderStyle = BorderStyle.FixedSingle

With zTimer
.Interval = 20
.Enabled =
True
.Start()
End With
End Sub

Private Sub zTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles zTimer.Tick
zPB1.Image = BlendBitmaps(zGrayScaleBitmap, TransparentBitmap_ColorMatrix(zBitmap, zValue))
zPB2.Image = BlendBitmaps(zGrayScaleBitmap, TransparentBitmap_LockBits(zBitmap,
CByte(zValue * 255)))
zLabel.Text = (zValue * 100).ToString(
"0") & " % [Click and slide across this area to change the blend]"
End Sub

Private Sub zLabel_Mouse(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles zLabel.MouseDown, zLabel.MouseMove
If e.Button = Windows.Forms.MouseButtons.Left Then zValue = Math.Max(0, Math.Min(1, e.X / zLabel.Width))
End Sub

Private Function BlendBitmaps(ByVal zBackBitmap As Bitmap, ByVal zFrontBitmap As Bitmap) As Bitmap
Dim zSize As New Size(Math.Max(zBackBitmap.Width, zFrontBitmap.Width), Math.Max(zBackBitmap.Height, zFrontBitmap.Height))
Dim zResult As New Bitmap(zSize.Width, zSize.Height)
Dim zGraphics As Graphics = Graphics.FromImage(zResult)
With zGraphics
.CompositingMode = Drawing2D.CompositingMode.SourceOver
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
End With
zGraphics.DrawImage(zBackBitmap, 0, 0, zSize.Width, zSize.Height)
zGraphics.DrawImage(zFrontBitmap, 0, 0, zSize.Width, zSize.Height)
zGraphics.Dispose()
Return zResult
End Function

Private Function TransparentBitmap_LockBits(ByVal zGivenBitmap As Bitmap, ByVal zGivenTransparency As Byte) As Bitmap
' NOTE :: Bitmap must have AlphaChannel (4Bytes/pixel // RGBA format // pixelformat: 32bppARGB)
Dim zBitmapData As Imaging.BitmapData = zGivenBitmap.LockBits(New Rectangle(0, 0, zGivenBitmap.Width, zGivenBitmap.Height), Imaging.ImageLockMode.ReadWrite, zGivenBitmap.PixelFormat)
Dim zBufferSize As Long = zBitmapData.Stride * zBitmapData.Height
Dim zBytesPerPixel As Integer = zBitmapData.Stride / zBitmapData.Width
Dim zGivenBitmapDataBytes(zBufferSize) As Byte
Dim zBitmapDataBytes(zBufferSize) As Byte

System.Runtime.InteropServices.Marshal.Copy(zBitmapData.Scan0, zBitmapDataBytes, 0, zBufferSize)
For zRow As Integer = 0 To zBitmapData.Height - 1
For zByte As Integer = 3 To zBitmapData.Stride - 1 Step 4
zBitmapDataBytes.SetValue(zGivenTransparency, (zRow * zBitmapData.Stride) + zByte)
Next
Next
System.Runtime.InteropServices.Marshal.Copy(zBitmapDataBytes, 0, zBitmapData.Scan0, zBufferSize)
zGivenBitmap.UnlockBits(zBitmapData)

zBitmapDataBytes = Nothing
zBitmapData = Nothing
Return zGivenBitmap
End Function

Private Function TransparentBitmap_ColorMatrix(ByVal zGivenBitmap As Bitmap, ByVal zGivenTransparency As Single) As Bitmap
zGivenTransparency = Math.Max(0, Math.Min(1, zGivenTransparency))

Dim zColorMatrixItems()() As Single = _
New Single() 1, 0, 0, 0, 0, _
New Single() 0, 1, 0, 0, 0, _
New Single() 0, 0, 1, 0, 0, _
New Single() 0, 0, 0, zGivenTransparency, 0, _
New Single() 0, 0, 0, 0, 1

Dim zColorMatrix As New Imaging.ColorMatrix(zColorMatrixItems)
Dim zImageAttributes As New Imaging.ImageAttributes
zImageAttributes.SetColorMatrix(zColorMatrix, Imaging.ColorMatrixFlag.Default, Imaging.ColorAdjustType.Bitmap)
Dim zResult As New Bitmap(zGivenBitmap.Width, zGivenBitmap.Height)
Dim zGraphics As Graphics = Graphics.FromImage(zResult)
With zGraphics
.CompositingMode = Drawing2D.CompositingMode.SourceOver
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
End With
zGraphics.DrawImage(zGivenBitmap, New Rectangle(0, 0, zGivenBitmap.Width, zGivenBitmap.Height), 0, 0, zGivenBitmap.Width, zGivenBitmap.Height, GraphicsUnit.Pixel, zImageAttributes)
zGraphics.Dispose()
Return zResult
End Function

Private Function GrayScaleBitmap(ByVal zGivenBitmap As Bitmap) As Bitmap
Dim zGS As Single = 1 / 3
Dim zColorMatrixItems()() As Single = _
New Single() zGS, zGS, zGS, 0, 0, _
New Single() zGS, zGS, zGS, 0, 0, _
New Single() zGS, zGS, zGS, 0, 0, _
New Single() 0, 0, 0, 1, 0, _
New Single() 0, 0, 0, 0, 1

Dim zColorMatrix As New Imaging.ColorMatrix(zColorMatrixItems)
Dim zImageAttributes As New Imaging.ImageAttributes
zImageAttributes.SetColorMatrix(zColorMatrix, Imaging.ColorMatrixFlag.Default, Imaging.ColorAdjustType.Bitmap)
Dim zResult As New Bitmap(zGivenBitmap.Width, zGivenBitmap.Height)
Dim zGraphics As Graphics = Graphics.FromImage(zResult)
With zGraphics
.CompositingMode = Drawing2D.CompositingMode.SourceOver
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
End With
zGraphics.DrawImage(zGivenBitmap, New Rectangle(0, 0, zGivenBitmap.Width, zGivenBitmap.Height), 0, 0, zGivenBitmap.Width, zGivenBitmap.Height, GraphicsUnit.Pixel, zImageAttributes)
zGraphics.Dispose()
Return zResult
End Function

End Class

nogChoco at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 3

Hi

Using a ColorMatrix is the way to go.

GDI+ is notoriously slow with anything remotely complex. If performance is a real consideration for you, then I'd recommend you branch into the unmanaged world of GDI.

Richard

PS - For a more accurate grey scaling uses a matrix of 0.3, 0.59, 0.11

DickDonny at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 4
Thanks for the info, Richard :)

Do you know 'how unmanaged' the LockBits method is? I mean, is it on a level that will practically guarantee problems or is it relatively safe to use, considering the fact that it'll be part of a program that I would like to make available for download.

Those NTSC matrixvalues work perfectly on a photo but skew the gradient pattern on my ColorWheel, so I went with the default 0.333 values instead.

nogChoco at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 5

Hi

Lockbits is used to lock the memory location of the bitmap so that you can carry out 'unmanaged' bitmap mangling (GC may defrag memory any time it sees fit so if you venture into working with memory direct you must pin it).

For me, stick with the colormatrix if you can. Interop based techniques are always likely to be more fragile than managed code as .net versions move forward.

Richard

DickDonny at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 6
Okay, I'll take the red pill and keep messing with the Matrix Thanks, Richard!
nogChoco at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 7
Good luck ... by the way, the screenshot looks good.
DickDonny at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 8
Thanks Very interesting blog, btw


I found out why ColorMatrix was giving me non-linear transparency results - if I add this line:

Dim zImageAttributes As New Imaging.Imag...
zImageAttributes.SetNoOp(Imaging.ColorAdjustType.Bitmap)
zImageAttributes.SetColorMatrix(zColorMatrix, Imaging.ColorMa...

then the result is exactly the same as the LockBits method, so it must have been doing some color-adjustment by default.

nogChoco at 2007-9-5 > top of Msdn Tech,Visual Basic,Visual Basic Language...