I've rewritten this question a half dozen times and each time it makes less sense. In a nutshell, I want to populate a listview with large icons representing image files but I can't get them to come out unstretched. They are always stretched along the x or y axis because the aspect ratios vary. Does anyone know how to create a thumbnail to go into an imageList so that the thumbnail is padded in either direction to prevent stretching?

Recommended Answers

All 10 Replies

I don't know ANYTHING about .NET and I don't even know what listview is. But, if this is a web application that is outputting HTML, you only need to specify the dimensions for height or width and the other will be calculated to the correct aspect ratio. Probably of no use to you, but I figured I'd give it a shot.

Maybe have a look at the Stretch property of your images. Click Here

Got it working. I'll post my solution after I clean it up.

The code to load the ListView is

Private Sub btnThumbs_Click(sender As System.Object, e As System.EventArgs) Handles btnThumbs.Click

    imgFiles.Images.Clear()
    lvwFiles.Items.Clear()
    lvwFiles.View = View.LargeIcon

    Dim i As Integer = 0

    For Each file As String In My.Computer.FileSystem.GetFiles(path, FileIO.SearchOption.SearchTopLevelOnly, filter)
        imgFiles.Images.Add(MakeThumb(file))
        Dim item As New ListViewItem(System.IO.Path.GetFileName(file))
        item.ImageIndex = i : i += 1
        lvwFiles.Items.Add(item)
    Next

End Sub

and to generate the thumbnails (no stretching) is

Private Function MakeThumb(file As String) As Bitmap
    Return ResizeImage(New Bitmap(Image.FromFile(file)), imgFiles.ImageSize.Width, imgFiles.ImageSize.Height)
End Function

Private Function ResizeImage(srce As Drawing.Bitmap, destW As Int32, destH As Int32) As Drawing.Bitmap

    Dim dest As New Drawing.Bitmap(destW, destH, Drawing.Imaging.PixelFormat.Format32bppArgb)

    Dim srceAspect = srce.Width / srce.Height
    Dim destAspect = dest.Width / dest.Height

    Dim NewX As Integer = 0                 'X offset of image in new bitmap
    Dim NewY As Integer = 0                 'Y offset of image in new bitmap
    Dim NewW As Integer = dest.Width        'width of image in new bitmap   
    Dim NewH As Integer = dest.Height       'height of image in new bitmap  

    'calculate sizes and offsets for centering

    If destAspect > srceAspect Then
        NewW = Convert.ToInt32(Math.Floor(srceAspect * NewH))
        NewX = Convert.ToInt32(Math.Floor((dest.Width - NewW) / 2))
    Else
        NewH = Convert.ToInt32(Math.Floor((1 / srceAspect) * NewW))
        NewY = Convert.ToInt32(Math.Floor((dest.Height - NewH) / 2))
    End If

    'draw the srce image into the dest bitmap

    Using grDest = Drawing.Graphics.FromImage(dest)
        grDest.CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
        grDest.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
        grDest.PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
        grDest.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
        grDest.CompositingMode = Drawing.Drawing2D.CompositingMode.SourceOver
        grDest.DrawImage(srce, NewX, NewY, NewW, NewH)
    End Using

    Return dest

End Function

Premature celebration. When I run this on a folder with 60 files it works well, but in a folder with 200 it craps out with

A first chance exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll

I changed MakeThumb to

Private Function MakeThumb(file As String) As Bitmap

    Dim srce As New Bitmap(Image.FromFile(file))
    Dim dest As Bitmap = ResizeImage(srce, imgFiles.ImageSize.Width, imgFiles.ImageSize.Height)
    Return dest

End Function

and it dies on

Dim srce As New Bitmap(Image.FromFile(file))

Any suggestions on what might be causing the memory problem?

Perhaps you need to manually free/dispose your bitmaps after use, I think they are unmanaged resources.

I tried

Private Function MakeThumb(file As String) As Bitmap

    Dim srce As New Bitmap(Image.FromFile(file))
    Dim dest As Bitmap = ResizeImage(srce, imgFiles.ImageSize.Width, imgFiles.ImageSize.Height)
    srce.Dispose
    Return dest

End Function

and watching the memory in task manager I can see it cycle up and down. It gets to 279 files now instead of 146 but it still dies.

The thumbnails still take up contiguous memory. Usually when you get an out of memory exception in image processing, it's due to fragmentation. Have you considered using a window of the thumbnails rather than trying to display all of them? That way you have a manageable subset of bitmaps in memory at any given time.

I don't understand. Wouldn't I still need all of the thumbnails in the imageList? Watching the numbers in the task manager I see the memory cycling. It ramps up to 800 meg then drops back down (I'm guessing when the garbage collector runs). It does this several times before getting the error. I'm thinking there might be some way to force the garbage collector to run more frequently.

That seems to have done it. If I change the code to

Private Function MakeThumb(file As String) As Bitmap

    Dim srce As New Bitmap(Image.FromFile(file))
    Dim dest As Bitmap = ResizeImage(srce, imgFiles.ImageSize.Width, imgFiles.ImageSize.Height)
    srce.Dispose
    GC.Collect()
    Return dest

End Function

Then the memory stays within reason. I can probably fine tune it to run GC.Collect() on every nth thumbnail gen rather than on every one.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.