Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Display 100's of Images

Status
Not open for further replies.

Error7

Programmer
Jul 5, 2002
656
GB
Can anybody suggest a more efficient way of doing this.

My main programme can load multiple images into a picture box sequentially, sizing the Picture box according to the limitations of the graphics card memory. So with 256 MB of memory I can achieve a picture box 4 x 4 of the screen size.

I now want to give the users the ability to rearrange their photo's before adding them to the picture box and to do this I have a form which equals the screen size and I add each of the users selected photo's into an image box. Loading and resizing the image boxes as I go.

This is my code that loads the Image Boxes:

Code:
Dim i As Integer
Dim a As Integer
Dim Count As Integer
Dim X1 As Long, Y1 As Long
Me.MousePointer = vbHourglass
On Error GoTo ErrHandler

For i = 0 To frmMain.lstFiles.ListCount - 1    
    DoEvents    
    Fname = frmMain.lstFiles.List(i)
    
    With Image1(a)
        .Visible = True
        .Picture = LoadPicture(Fname)
        .Width = PicWidth
        .Height = PicHeight
        .Left = X1
        .Top = Y1
    End With
    
    X1 = X1 + PicWidth
    Count = Count + 1
    
    If Count >= NumWide Then
        Count = 0
        X1 = 0
        Y1 = Y1 + (PicHeight)
    End If
    
    a = a + 1
    Load Image1(a)
    End If
Here:
Next i

Me.MousePointer = vbDefault

My problem is that if the user selects a couple of hundred photos then the whole thing starts to bog down and gets slower and slower until it eventually bombs out on an Error 7.

Can this be done more efficiently or do I need to have a re-think about what I am trying to achieve.

Thanks in advance.

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Is there a better way to do this?

[gray]Experience is something you don't get until just after you need it.[/gray]
 
>sizing the Picture box according to the limitations of the graphics card memory. So with 256 MB of memory I can achieve a picture box 4 x 4 of the screen size

Not quite sure I know where you got this from.

>the whole thing starts to bog down and gets slower and slower

Well, it would; you are loading and retaining all your pictures in memory

>do I need to have a re-think

I'd say so. And the rethink should take you in the direction of NOT having 100s of pictures loaded at the same time.



 
Thanks Mike for showing an interest. I was getting worried.

Covering the first bit about screen size v graphics memory. If I create a Picture box 4 times the screen width but only twice the screen height and fill it with an image or number of images, if the graphics card has less than 256 MB then it can display this. However if I increase the Picture Box height to 4 times the screen height then when I scroll the Picture Box the bottom half streaks the image(s).

If I do the same with 256 MB graphics memory or more, then the Picture Box displays perfectly ok.

The main issue I have come across with 256 MB or more is that if I create the Picture Box bigger than 4 x 4 the screen size, the Picture Box displays fine but when I save it to a file the file size is as pretty big as expected (80 MB plus) but if I view the image in any picture editing application it is just a cream coloured picture.

I need to load 100's of images into individual Image Boxes so that the user can arrange the layout of their finished wall of pictures before I create the BIG Picture Box.

This is an example of my existing version that doesn't provide the ability to arrange the layout.


Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Thanks Joe

Since posting my earlier reply, I experimented with considerably reducing the size of each image before loading into the multiple Image Boxes and once all the resizing has taken place, I was able to load 600 image boxes onto the Form in 10 seconds so this could be the way to go.

So my next question is, is there a way to create a thumbnail from an image without going round the houses?


[gray]Experience is something you don't get until just after you need it.[/gray]
 
>I need to load 100's of images into individual Image Boxes

Not necessarily. You only need to load those that are visible (or partially visible) on the screen
 
Thanks Mike

I understand what you are saying but the user may want to drag an image from the bottom right corner to the top left. If I don't have them all visible within the dimensions of the screen there will be lots of scrolling necessary.

I have now looked on the Internet and found various code examples but they don't seem any quicker than the code that I experimented with to do a batch re-size.

I will play about with that a bit more.

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Alan:

>So my next question is, is there a way to create a thumbnail from an image without going round the houses?

Here is a simple sub-routine to create a thumbnail (maintains aspect ratio) from an image that you may be able to adapt to your program.

To test... place a picture box, image box and command button on a new form. Size the picture box to the size of the thumbnail you wish to display. Picture1.AutoSize should be False. Load any size picture in Picture 1. The Command button calls the following sub routine

-------------------------------------------
Code:
Private Sub ImageThumbNail()

'This is to create thumbnail picture
    
    Image1.Visible = False
    Image1.Stretch = False
    Image1.Picture = Picture1.Picture
    
    
    If Image1.Picture Then
      Image1.Height = Image1.Picture.Height
      Image1.Width = Image1.Picture.Width
      Image1.Stretch = True

      'keep aspect ratio - do height and width
       If Image1.Picture.Height >= Image1.Picture.Width Then
         Image1.Height = Picture1.Height
         Image1.Width = Image1.Width / (Image1.Picture.Height / Image1.Height)
         
           If Image1.Width > Picture1.Width Then
             Image1.Width = Picture1.Width
             Image1.Height = Image1.Picture.Height / (Image1.Picture.Width / Image1.Width)
           End If
           
       End If


       If Image1.Picture.Width > Image1.Picture.Height Then
         Image1.Width = Picture1.Width
         Image1.Height = Image1.Height / (Image1.Picture.Width / Image1.Width)
             
           If Image1.Height > Picture1.Height Then
             Image1.Height = Picture1.Height
             Image1.Width = Image1.Picture.Width / (Image1.Picture.Height / Image1.Height)
           End If
           
       End If
        

    Image1.Visible = True
        
   End If

End Sub
---------------------------------------
Hope this leads you in the right direction.

Tom
 
Thanks Tom

Unfortunately I'm off on my hols for a week today so won't have chance to try your code until I return.

Last night I tested my method and was able to create over 1,000 thumbnails from the same number of photo's, the average size of which was around the 1.5 MB mark. Within the same loop I loaded, sized and positioned sequentially an array of Image Controls on a single screen.

It worked!

(But it was rather slow)

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
I'm afraid that by itself I don't think Tom's code will help much, as you'll now have the full size image loaded into the Image control (even though it potentially displays as a thumbnail), and it is that that we are mostly trying to avoid.

>don't seem any quicker than the code that I experimented with to do a batch re-size.

Which is why I'm interested in seeing the code you are using, as I'd like to try and see where your bottleneck is (generally you should be able to do very fast thumbnailing, but loading each file can be time constraining. For example, I have some (unoptimised) code that can resize and display 1024 1.4Mb images on a form in less than 1/4 of a second - as long as the full-size images are preloaded (yes, I know we run in to a memory problem if we try and actually load 1024 full size images, so the example resizes and displays the same image 1024 times). But it takes about 15 seconds or so if I have to load an image each time.
 
Quite right, Tom's code didn't help much.

This is a stripped down version of my code that loads selected images from a folder into Image2 (Stretch = True) then paints that into Picture2 before copying to the array of image boxes which are positioned on the Form sequentially.

Code:
PicWidth = Me.ScaleWidth / NumWide 'Number of images across 
PicHeight = Me.ScaleHeight / NumHigh 'Number of images down

Picture2.Width = PicWidth
Picture2.Height = PicHeight
    
For i = 0 To MaxImages - 1
    
    fname = frmLayout.lstReorder.List(i)    
    
    Image2.Picture = LoadPicture(fname)
    
    Picture2.PaintPicture Image2.Picture, 0, 0, PicWidth, PicHeight
       
    With Image1(a)
        .Visible = True
        .Picture = Picture2.Image
        .Width = PicWidth
        .Height = PicHeight
        .Left = X1
        .Top = Y1
        .Enabled = False
    End With

    X1 = X1 + PicWidth
    Count = Count + 1
    If Count >= NumWide Then
        Count = 0
        X1 = 0
        Y1 = Y1 + (PicHeight)
    End If
   
    a = a + 1
    Load Image1(a)
    
Next i


[gray]Experience is something you don't get until just after you need it.[/gray]
 
I should have mentioned that I'm not concerned about maintaining the aspect ratio at this stage. That is done later.

[gray]Experience is something you don't get until just after you need it.[/gray]
 
That's fairly similar to what I've got - except you have the overhead of loading into an Image control, then painting to a picture control, and then capturing that to another image control ... in other words you want to eliminate the VB controls wherever possible (my version loads the initial image into a StdPicture and then StretchBlits it (well, the StdPicture equivalent, Render) directly into the Form's DC at the relevant coordinates
 
Thanks Mike

Any chance of a quick peek at your code?

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Sure, although it's a bit of a mess. You'll just need a form with autoredraw set to true. TYhen paste in the following (er, replace my filename with your filename obviously):
Code:
[blue]Option Explicit
Private Const PicsX = 32
Private Const PicsY = 32

Private Declare Function GetTickCount Lib "kernel32" () As Long


Private Sub Form_Resize()
    Dim lp1 As Long
    Dim lp2 As Long
    
    Dim lp As Long
    
    Dim myPic As StdPicture
    Static InResize As Boolean
    
    If Not InResize Then
        InResize = True
        Form1.Cls
       
        Set myPic = LoadPicture("c:\sunset.bmp")
        
        Dim starttime As Long
'        starttime = GetTickCount
        For lp1 = 0 To PicsX
            For lp2 = 0 To PicsY
                lp = lp2 * PicsX + lp1
                myPic.Render Form1.hDC, lp1 * Form1.ScaleWidth / PicsX, lp2 * Form1.ScaleHeight / PicsY, Form1.ScaleWidth / PicsX, Form1.ScaleHeight / PicsY, 0&, myPic.Height, myPic.Width, -myPic.Height, 0&
            Next
        Next
'        Label1.Caption = GetTickCount - starttime
        InResize = False
    End If
End Sub[/blue]
 
Thanks Mike.

Unfortunately I need to display the images in individual Image Controls so that the user can re-position them on the form using Drag Drop.

I experimented with my code to determine where the bottleneck is by moving the lines...

fname = frmLayout.lstReorder.List(i)
Image2.Picture = LoadPicture(fname)
Picture2.PaintPicture Image2.Picture, 0, 0, PicWidth, PicHeight


...out of the For Next loop. (temporarily)

I was able to create 1089 Image Controls and load each with the same picture in only 4 seconds.

So it is apparent that painting the picture into Picture2 is the real problem but if I don't do this, if I load each picture directly into the array of Image1 controls, then I am back to square one where I initially said "My problem is that if the user selects a couple of hundred photos then the whole thing starts to bog down and gets slower and slower until it eventually bombs out on an Error 7."

I also tried replacing Image2.Picture = LoadPicture(fname) with Set pic = LoadPicture(fname) but that made no significant difference.

I don't know If it's possible to improve on this. [sad]

[gray]Experience is something you don't get until just after you need it.[/gray]
 
>using Drag Drop

Sure - but you can do your own drag and drop ... you don't neccessarily have to rely on the built-in controls.

I don't have time to put an example together right now, but a search in the forum for Regions may locate some code of mine that hints at a possible solution.

Otherwise I'll see what I can cobble together tomorrow. Should'nt be too many extra lines of code. I hope ...

 
Ok, you just need a blank form with an Image control on it for this. Again, replace the filename I use with your own:
Code:
[blue]Option Explicit
Private Const PicsX = 32
Private Const PicsY = 32

Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Private Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT, ByVal bErase As Long) As Long
Private Declare Function ValidateRect Lib "user32" (ByVal hwnd As Long, lpRect As Any) As Long 'RECT) As Long
Private Declare Function UpdateWindow Lib "user32" (ByVal hwnd As Long) As Long

Private DragPicture As StdPicture


Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single)
    Form1.Cls ' erase anything we left on top of drawing surface ...
    Form1.AutoRedraw = True
End Sub

Private Sub Form_DragOver(Source As Control, X As Single, Y As Single, State As Integer)
    Dim GoodRect  As RECT
    Dim BadRect As RECT

    Static LastX
    Static LastY

    DragPicture.Render Form1.hdc, X, Y, Source.Width, Source.Height, 0&, DragPicture.Height, DragPicture.Width, -DragPicture.Height, 0&
    
    BadRect.Left = LastX
    BadRect.Top = LastY
    BadRect.Right = LastX + Source.Width
    BadRect.Bottom = LastY + Source.Height
    GoodRect.Left = X
    GoodRect.Top = Y
    GoodRect.Right = X + Source.Width
    GoodRect.Bottom = Y + Source.Height
    
    ' these three lines make sure we cheaply delete remains of custom icon drawn
    ' in previous position
    InvalidateRect Form1.hwnd, BadRect, False
    ValidateRect Form1.hwnd, GoodRect
    UpdateWindow Form1.hwnd
    
    ' Update previous position
    LastX = X
    LastY = Y
End Sub

Private Sub Form_Load()
    Form1.AutoRedraw = True
    Form1.ScaleMode = vbPixels
End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    ' We change Image1's size becasue we are going to be leveraging OLEDragDrop's ability to display the frame of a control
    ' that is being dragged, so we need to ensure that it is the right size
    ' I'm happening to use an Image control, but it doesn't have to be
    Image1.Left = X
    Image1.Top = Y
    Image1.Width = Form1.ScaleWidth / PicsX
    Image1.Height = Form1.ScaleHeight / PicsY

    Set DragPicture = LoadPicture("c:\test2.bmp") ' In reality at this point you'd load whatever picture is under the cursor
    
    Form1.AutoRedraw = False
    Image1.Drag vbBeginDrag
End Sub

Private Sub Form_Resize()
    Dim lp1 As Long
    Dim lp2 As Long
    
    Dim lp As Long
    
    Dim myPic As StdPicture
    Static InResize As Boolean
    
    If Not InResize Then
        InResize = True
        Form1.Cls
       
        Set myPic = LoadPicture("c:\test2.bmp")
        
        For lp1 = 0 To PicsX
            For lp2 = 0 To PicsY
                lp = lp2 * PicsX + lp1
                myPic.Render Form1.hdc, lp1 * Form1.ScaleWidth / PicsX, lp2 * Form1.ScaleHeight / PicsY, Form1.ScaleWidth / PicsX, Form1.ScaleHeight / PicsY, 0&, myPic.Height, myPic.Width, -myPic.Height, 0&
            Next
        Next
        InResize = False
    End If
End Sub

Private Sub Image1_OLEStartDrag(Data As DataObject, AllowedEffects As Long)
    Data.SetData LoadPicture("c:\test2.bmp"), vbCFBitmap
    AllowedEffects = vbDropEffectMove ' Cons OLE into giving us a nice frame
End Sub[/blue]
 
Mike I haven't studied your code yet, merely pasted it into a form to test it.

So far all I can say is WOW!!!

Hopefully I will get chance to play about with it more when I get home. If her indoors permits. [wink]

Thanks

Alan

[gray]Experience is something you don't get until just after you need it.[/gray]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top