Tuesday, January 06, 2009
Home
.Net
Return of the Repeater
Complete image control & app
Part 2 - Starting the control
Part 3 - Loading images
Part 4 - Custom exceptions
Part 5 - Fax/Multipage tifs
Part 6 - Custom events
Part 7 - Selecting fax pages
Part 8 - Rotating displays
Part 9 - The power of GDI+
Part 10 - Zooming
Part 11 - .Net's fatal exception
Part 12 - Fix the squishies
Part 13 - Zoom to fit
Part 14 - You beat the pros
Part 15 - Cropping
Part 16 - Bonus: StickyMouse
Part 17 - Finishing touches?
Part 18 - Make the application
Part 19 - Adding viewports
Part 20 - A better toolbar
Part 21 - Connect the toolbar
Part 22 - Adding ImageEditors
Part 23 - Toolbar ZoomCombo
Part 24 - VB3 style ease
Part 25 - Saving images to files
Part 26 - Integer-only textBox
Part 27 - Passing save settings
Part 28 - The last exception
Part 29 - Menus offer more
Part 30 - Book, app & source
Couple of CS Snippets
XML processing quickies
File extension extensions
McAfee.Not
cs IntBox
Floating Holidays
Code snippets
Flexible sorts
Converted deserializations
Autodeploy not found
Autodeploy just stops
VB must be killed
Media file attributes
Fastest file searches
Webservicing custom objects
Aspect correct resize
Funky thumbnails
Wide interval timing
VB2005 or bust?
Recurring events
Single instances
Proper proper casing
Simple address formating
Combo filling reminder
Easy gradient forms
Grrrr on interops?
Winform memory usage
Windows service your ISP
Pretty up VS code printing
Remote configs with no BS
GDI+ printing cd cases
Your own flat combo
Where's the splitter?
Full autoemail
ReessppoonncceeWwrriittee
Kill the back cache
Color to hex
Source to web
Recode without recompile
Prop snippet for VS2008
Database tricks

Your own image control

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


Print  

pagecomment
  Add Comment



Submit Comment
  View Ratings
50.00%0
40.00%0
30.00%0
20.00%0
10.00%0

Number of Comments 0 , Average of Ratings
  View Comments
No comment.


Privacy Statement  |  Terms Of Use
Copyright 2008 by Robert C. Smith