Many people may have noticed in the past how intimidating using Bitmap.Lockbits has been due to a lack of clarification/documentation on its usage. This article attempts to do an in depth review of its usage. It does not focus on alternate methods to lockbits.
I remember sometime back, the very first thing ever that turned me onto Bitmap.LockBits was when I went to draw a single pixel using the graphics object, that was when I saw that using SetPixel (or even drawing 1x1px rectangles with the graphics object) was extremely costly in terms of CPU usage, and performance was poor. I remember sometime back reading Bob Powell's article on LockBits, which seemed to me the only in-depth coverage of this I could find anywhere. I however was still left with a little confusion, and later I was able to iron out the details of that confusion. Many thanks to Bob for that original article. The examples in this article will start simple, and grow in complexity as an understanding is established, it is recommended you start from the beginning. This article can be applicable to any .Net language, although this sample will be shown in Visual Basic.Net.
This example will only change a single pixel of a bitmap.
1.
Public
Shared
Function
SetPixel(
ByRef
OriginalBitmap
As
Bitmap, _
2.
PixelLocation
Point, _
3.
PixelColor
Color)
Bitmap
4.
5.
End
01.
02.
03.
04.
Const
px
System.Drawing.GraphicsUnit = GraphicsUnit.Pixel
05.
fmtArgb
Imaging.PixelFormat = Imaging.PixelFormat.Format32bppArgb
06.
Dim
boundsF
RectangleF = OriginalBitmap.GetBounds(px)
07.
bounds
New
Rectangle
08.
bounds.Location =
Point(
CInt
(boundsF.X),
(boundsF.Y))
09.
bounds.Size =
Size(
(boundsF.Width),
(boundsF.Height))
10.
bmClone
Bitmap = OriginalBitmap.Clone(bounds, fmtArgb)
11.
bmData
System.Drawing.Imaging.BitmapData = bmClone.LockBits(bounds, Imaging.ImageLockMode.ReadWrite, bmClone.PixelFormat)
12.
offsetToFirstPixel
IntPtr = bmData.Scan0
13.
byteCount
Integer
= Math.Abs(bmData.Stride) * bmClone.Height
14.
bitmapBytes(byteCount - 1)
Byte
15.
System.Runtime.InteropServices.Marshal.Copy(offsetToFirstPixel, bitmapBytes, 0, byteCount)
16.
alpha
= PixelColor.A
17.
red
= PixelColor.R
18.
green
= PixelColor.G
19.
blue
= PixelColor.B
20.
baseOffset
= bmData.Stride * PixelLocation.Y
21.
addOffset
= PixelLocation.X * 4
22.
offset
= baseOffset + addOffset
23.
bitmapBytes(offset) = blue
24.
bitmapBytes(offset + 1) = green
25.
bitmapBytes(offset + 2) = red
26.
bitmapBytes(offset + 3) = alpha
27.
System.Runtime.InteropServices.Marshal.Copy(bitmapBytes, 0, offsetToFirstPixel, byteCount)
28.
bmClone.UnlockBits(bmData)
29.
Return
30.
Private
Sub
Button1_Click(
ByVal
sender
System.
Object
,
e
System.EventArgs)
Handles
Button1.Click
sourceBitmap
Bitmap(100, 100)
graphics
Graphics = graphics.FromImage(SourceBitmap)
graphics.FillRectangle(Brushes.DarkGray,
Rectangle(
Point(0, 0),
Size(100, 100)))
graphics.Dispose()
PictureBox1.BackgroundImageLayout = ImageLayout.None
'Look in the bottom right corner, its tiny...
PictureBox1.BackgroundImage = SetPixel(sourceBitmap,
Point(99, 99), Color.Red)
sourceBitmap.Dispose()
FillRectangle Function Code
FillRectangle(
Rectangle, _
FillColor
StartOffset
= (Rectangle.Top * bmData.Stride)
EndOffset
= StartOffset + (Rectangle.Height * bmData.Stride)
RectLeftOffset
= (Rectangle.Left * 4), RectRightOffset
= ((Rectangle.Left + Rectangle.Width) * 4)
X
= Rectangle.Left, Y
= Rectangle.Top - 1
For
FirstOffsetInEachLine
= StartOffset
To
Step
bmData.Stride
Y += 1
X = Rectangle.Left - 1
PixelOffset
= RectLeftOffset
RectRightOffset
4
X += 1
Point(X, Y)
bitmapBytes(FirstOffsetInEachLine + PixelOffset) = FillColor.B
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 1) = FillColor.G
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 2) = FillColor.R
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 3) = FillColor.A
Next
FillRect
Point(10, 10),
Size(80, 80))
PictureBox1.BackgroundImage = FillRectangle(sourceBitmap, FillRect, Color.Red)
GrayScaleFillRectangle(
Bitmap,
Rectangle,
GrayScaleMode
GrayScaleMode)
Image
C
Color = Color.FromArgb(bitmapBytes(FirstOffsetInEachLine + PixelOffset + 3), _
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 2), _
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 1), _
bitmapBytes(FirstOffsetInEachLine + PixelOffset))
RGB
= 0
Color
Select
Case
Form1.GrayScaleMode.GrayScaleLuminosity
RGB =
(C.R * 0.21 + C.G * 0.71 + C.B * 0.07)
FillColor = Color.FromArgb(RGB)
Form1.GrayScaleMode.GrayScaleAverage
RGB = (
(C.R) +
(C.G) +
(C.B)) \ 3
Else
bmClone.Dispose()
bitmapBytes(FirstOffsetInEachLine + PixelOffset) = FillColor.A
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 1) = FillColor.R
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 2) = FillColor.G
bitmapBytes(FirstOffsetInEachLine + PixelOffset + 3) = FillColor.B
Enum
GrayScaleLuminosity = 1
GrayScaleAverage = 2
Graphics = graphics.FromImage(sourceBitmap)
graphics.DrawLine(Pens.Red,
Point(99, 99))
PictureBox1.BackgroundImage = GrayScaleFillRectangle(sourceBitmap, FillRect, GrayScaleMode.GrayScaleLuminosity)
You can use iteration to set more than one pixel at a time, but the main focus of this article is on how to use LockBits.... There are many more things you can do with LockBits, and I will update this later, but for now, I hope you enjoy the 3 examples. References
I hope you find this helpful! Stats Total