smithvoice.com
 Y'herd thisun? 

“If someone asks me the question “Why are you interested in space?” the only answer I can give is “Why aren’t you?””
-Jeff Greason, President of XCOR


from Jeff Greason SpaceNews Profile

Your own image control and App part 9

TaggedCoding, VB, Imaging

Originally published December 2002 on DoItIn.net using VB7.0/2002. Updated for VB7.1 February 2005

Links for compiled demo versions, all required resources and source code are included at the end of this article.

Plus, get the complete eBook in Adobe Acrobat 7 format ... all here.


 

9) The most useful tool in GDI+: DrawImage

There are a number of ways to make a pixel for pixel copy of a raster graphic. As we've seen, applying RotateFlip is one way but it doesn't help anyone deal with multipage tifs. Some of the other techniques include:

  • Create a new bitmap object and loop through each individual pixel in the source graphic using GetPixel to read the pixel data and SetPixel to copy it to the same X/Y location of the destination bitmap. Even on a fast computer this can be very slow process and is best used when you have to perform some manipulation to each pixel such as altering the RGB value for a color adjustment.
  • P/Invoke ("Platform Invoke", a fancy term for working directly with the Win32 API) using GDI32.dll's GetPixel/SetPixel or the slightly faster GetPixelV/SetPixelV. This is faster because it cuts right through the .Net Runtime overhead but it forces you to do a lot of Marshaling in and out of managed memory so you have to be very careful and it's still doing a loop that can take noticeable time on even average sized images. (Though most .Net code will forever be run on Windows based systems, if there ever comes a time when your project will be expected to run on Linux via MONO P/Invoke is out for obvious reasons)
  • "BitBlast" the graphic's data to a new bitmap all at once using the GDI32.dll's bitblit function. Again, this is P/Invoke so it has both the upside of speed and the downsides of not being very portable and putting you in unsafe territory, marshalling the bits in and out of the CLR's process with non-OOP code that can quickly turn ugly. BitBlasting from a source image currently displayed unobstructed on the screen is pretty simple hence it still being the defacto standard for screen capturing, but once you get into loading images from files into memory bitmaps you have to deal with creating DeviceContexts which gets complicated quickly, and trying to get at the individual page/frames of a multipage tif with the API just starts turning your code into confusing and unstable spaghetti. Google for "bitblt" if you want to get into it but keep in mind that there's a reason even VB5/6 graphics gurus took unsupported chances with automating the Wang/Kodak image applet interfaces or did their fax work with expensive 3rd party components.
  • Use GDI+'s DrawImage/DrawImageUnscaled. These give you pretty darned fast bitblast style results and the upsides are that they are relatively simple ("less code = less chance of bugs"), have a number of overloaded versions to fit most all needs, and your code stays in the managed realm letting the runtime take care of *most* of the memory concerns. This is our winner.

Now that you're ready for your code review with the reasons why, right click on the UC's project node and "Add" a "New Module", name it "ImageUtilities.vb". Add an "Imports System.Drawing.Drawing2D" and a DrawImage function wrapper named CopyImage:

 

Imports System.Drawing.Drawing2D
  
Module ImageUtilities
Friend Function CopyImage(ByVal SourceImage As Image) As Image
  
Dim bmpDest As Bitmap
Dim gfx As Graphics
  
Try
 'make bitmap for result
 bmpDest = New Bitmap(SourceImage.Width, SourceImage.Height)
  
 'create Graphics object for final bitmap
 gfx = Graphics.FromImage(bmpDest)
  
 'changing Interpolation Modes from Default affects quality and speed
 'with higher quality taking the most time.  Start with the highest
 'setting - HighQualityBicubic - and if it drags down the systems then
 'start dropping down to other levels.  You could even make the mode
 'an argument of the function and expose it as a control property
 'so the dev-users could deal with it as they see fit
 gfx.InterpolationMode = InterpolationMode.HighQualityBicubic
  
 'blast the source image into the dest
 gfx.DrawImage(SourceImage, 0, 0, bmpDest.Width, bmpDest.Height)
  
 Return DirectCast(bmpDest.Clone, Image)
  
Catch ex As OutOfMemoryException
 'this is GDI+'s generic error
 Throw ex
Catch ex As Exception
 Throw ex
 'Todo: remove after complete testing
  
Finally
  
 If Not (SourceImage Is Nothing) Then
 SourceImage.Dispose() ' get rid of the old one if it exists.
 End If
  
 If Not (bmpDest Is Nothing) Then
 bmpDest.Dispose() ' get rid of the old one if it exists.
 End If
  
 If Not gfx Is Nothing Then
 gfx.Dispose()
 End If
  
End Try
  
End Function
End Module

 

Update the LoadImage function to slice out the individual images from the original file using CopyImage. Because DrawImage is processor-intensive (GDI+ 1x doesn't take advantage of hardware acceleration), we'll only do it for multipage images:

 

...
 
If pageCount > 1 Then
For i As Integer = 0 To UBound(m_ImagePages)
tmpFileImage.SelectActiveFrame(FrameDimension.Page, i)
tmpFrameImage = DirectCast(tmpFileImage.Clone, Image)
tmpFrameImage = CopyImage(tmpFrameImage)
m_ImagePages(i).OriginalImage = tmpFrameImage
m_ImagePages(i).WorkingImage = DirectCast(tmpFrameImage.Clone, Image)
m_ImagePages(i).CurrentZoom = 100
Next
Else
m_ImagePages(0).OriginalImage = tmpFileImage
m_ImagePages(0).WorkingImage = DirectCast(tmpFileImage.Clone, Image)
m_ImagePages(0).CurrentZoom = 100
End If
 
...

Rebuild, Save and F5. That's better :)

Next: Zooming

Robert Smith
Kirkland, WA

added to smithvoice march 2005


jump to:

  • 1) The spec
  • 2) Setting up the workspace
  • 3) Feature 1: Loading an image
  • 4) Custom Exceptions
  • 5) "Fax images" and Multipage TIFFs
  • 6) Custom events
  • 7) Selecting specific fax pages
  • 8) Feature 2: Rotating image displays
  • 9) The most useful tool in GDI+: DrawImage
  • 10) Feature 3: Zooming
  • 11) Handling the unhandleable exception
  • 12) Fixing the squish
  • 13) Zooming to fit the control
  • 14) You're already beating the Pros
  • 15) Feature 4: Cropping
  • 16) Bonus Feature: StickyMouse
  • 17a) Final Cleanup
  • 17b) Passing the current display image
  • 18) Making the application
  • 19) Source and result viewports
  • 20) A better toolbar
  • 21) Hooking the toolbar to the project
  • 22) Adding ImageEditors
  • 23) The toolbar ZoomCombo
  • 24) The final solution
  • 25) Saving to image files
  • 26) An integer-only textbox
  • 27) Passing save options between forms
  • 28) Dealing with that last exception
  • 29) Offer more options with menus
  • 30 The downloads and ebook


  • home     who is smith    contact smith     rss feed π
    Since 1997 a place for my stuff, and it if helps you too then all the better smithvoice.com