0

I’m using WinForms. In my Form I have a picture-box that displays image documents. These image documents have many pages. I have an open, next, and previous button on my form. The next button goes forward one page, and the previous button goes back one page in the document that’s opened in the picture-box. I also have labels on my form to indicate how much pages there are, and what page the user is currently viewing.

The problem with my code is when i open large image files into my picture-box, for example documents that has 1200 pages, and click next. The page loads up slow. I want to improve the performance of the code.

How can i make viewing the image documents faster or my code better?

I provided a tif document to test on: http://www.filedropper.com/sampletifdocument5pages

    private int int_Current_Page = 0;

    private void btnNextImage_Click_1(object sender, EventArgs e)
    {
        Image image2;
        try
        {
            if (int_Current_Page == Convert.ToInt32(lblNumPages.Text)) // if you have reached the last page it ends here
                                                                  // the "-1" should be there for normalizing the number of pages
            { int_Current_Page = Convert.ToInt32(lblNumPages.Text); }
            else
            {                   
                int_Current_Page++; //page increment

                using (FileStream stream = new FileStream(@"C:\my_Image_document", FileMode.Open, FileAccess.Read))
                {
                    image2 = Image.FromStream(stream);
                    Refresh_Image();
                } 
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void btnPrevImage_Click_1(object sender, EventArgs e)
    {
        if (int_Current_Page == 0) // it stops here if you reached the bottom, the first page of the tiff
        { int_Current_Page = 0; }
        else
        {
            int_Current_Page--; // if its not the first page, then go to the previous page
            Refresh_Image(); // refresh the image on the selected page
        }
    }

    private void openButton_Click_1(object sender, EventArgs e)
    {
        Refresh_Image(); //Opens the large image document
    }

    private void Refresh_Image()
    {
        Image myImg; // setting the selected tiff
        Image myBmp; // a new occurance of Image for viewing

        using (FileStream stream = new FileStream(@"C:\my_Image_document", FileMode.Open, FileAccess.Read))
        {
            myImg = Image.FromStream(stream);

            int intPages = myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page); // getting the number of pages of this tiff
            intPages--; // the first page is 0 so we must correct the number of pages to -1
            lblNumPages.Text = Convert.ToString(intPages); // showing the number of pages
            lblCurrPage.Text = Convert.ToString(int_Current_Page); // showing the number of page on which we're on

            myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, int_Current_Page); // going to the selected page

            myBmp = new Bitmap(myImg, pictureBox1.Width, pictureBox1.Height);

            pictureBox1.Image = myBmp; // showing the page in the pictureBox1  
        }
    }

enter image description here

Jonas
  • 121,568
  • 97
  • 310
  • 388
taji01
  • 2,527
  • 8
  • 36
  • 80
  • 2
    You are re-loading the image every time. Try just changing the active frame when navigating pages. – LarsTech Nov 25 '15 at 18:39
  • 3
    You need to devise a caching strategy that works for your app. For example, after you load page 1, on a separate Thread (pre-.NET) or Task (.NET 4 or better) you can load pages 2 - 10 and keep them readily available when the page is changed. This is not a quick fix, you need to also think about how often you refresh your cache, etc. – Glenn Ferrie Nov 25 '15 at 18:40
  • 1
    Also, you can make sure you are not validating the image data when you call FromStream, which will improve your performace. See this: http://blogs.msdn.com/b/omars/archive/2004/03/29/100941.aspx – sovemp Nov 25 '15 at 18:44
  • I tried looking at active frame, but i couldn't wrap my head around it. I'm new to programming so some of the concepts are foggy. @LarsTech – taji01 Nov 25 '15 at 19:03

1 Answers1

1

This is probably slowing down in one of two places, either loading the enormous file (hopefully) or Selecting the active frame. If it's the first issue, it's probably easy to fix just by lazy-loading the image one time:

private int int_Current_Page = 0;

private void btnNextImage_Click_1(object sender, EventArgs e)
{
    Image image2;
    try
    {
        if (int_Current_Page == Convert.ToInt32(lblNumPages.Text)) // if you have reached the last page it ends here
                                                              // the "-1" should be there for normalizing the number of pages
        { int_Current_Page = Convert.ToInt32(lblNumPages.Text); }
        else
        {                   
            int_Current_Page++; //page increment
            Refresh_Image();
        }
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

private void btnPrevImage_Click_1(object sender, EventArgs e)
{
    if (int_Current_Page == 0) // it stops here if you reached the bottom, the first page of the tiff
    { int_Current_Page = 0; }
    else
    {
        int_Current_Page--; // if its not the first page, then go to the previous page
        Refresh_Image(); // refresh the image on the selected page
    }
}

private void openButton_Click_1(object sender, EventArgs e)
{
    Refresh_Image(); //Opens the large image document
}
FileStream _stream; 
Image _myImg; // setting the selected tiff
private void Refresh_Image()
{
    // Image myImg; // setting the selected tiff - Now a member variable
    Image myBmp; // a new occurance of Image for viewing

    if (_myImg == null)
    {
        _stream = new FileStream(@"C:\my_Image_document", FileMode.Open, FileAccess.Read)
        _myImg = Image.FromStream(_Stream);
    }
    int intPages = _myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page); // getting the number of pages of this tiff
    intPages--; // the first page is 0 so we must correct the number of pages to -1
    lblNumPages.Text = Convert.ToString(intPages); // showing the number of pages
    lblCurrPage.Text = Convert.ToString(int_Current_Page); // showing the number of page on which we're on

    _myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, int_Current_Page); // going to the selected page

    myBmp = new Bitmap(_myImg, pictureBox1.Width, pictureBox1.Height);

    pictureBox1.Image = myBmp; // showing the page in the pictureBox1  
}

protected override void Dispose(bool disposing)
{
    if (_stream != null) _stream.Dispose();
    base.Dispose(disposing);
}
  • When i tested this i received a GDI+ error. The number of pages incremented on my form but the image did not refresh. Meaning that when i clicked next to go to page 2. The image in page 2 did not show. – taji01 Nov 25 '15 at 19:16
  • It threw an exception? Did you copy/paste the code exactly? Or is this just an example similar to what you're working on? And what was the exception that got thrown? Also, I didn't notice this when I was refactoring your code, but what is the purpose of "image2" in btnNextImage_Click_1? That can be removed (I'll edit) – MutantNinjaCodeMonkey Nov 25 '15 at 19:23
  • yeah i copied and pasted it again, and it did the same thing. I think the error is here: _myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, int_Current_Page);... The image2 is nothing i forgot to take that off. I was just trying different ways to make this work. – taji01 Nov 25 '15 at 19:30
  • What is the value of int_Current_Page at the time of the error? – MutantNinjaCodeMonkey Nov 25 '15 at 19:31
  • The value of int_Current_Page increases every time i hit the next button. – taji01 Nov 25 '15 at 19:33
  • The only thing that wont show is the next image in the Tif document – taji01 Nov 25 '15 at 19:34
  • What was the error message that was thrown when this occurred? – MutantNinjaCodeMonkey Nov 25 '15 at 19:35
  • 1
    Try it with "FromFile" (see my edit). It's possible that "FromStream" requires that Stream to remain open for the lifetime of that image's manipulation (that using block will close and dispose that stream). – MutantNinjaCodeMonkey Nov 25 '15 at 19:47
  • The performance increased and the GDI+ error was removed, but i needed a way to dispose the file that's why i used the using statement. – taji01 Nov 25 '15 at 19:56
  • 1
    So it's working now? :) What do you mean by "dispose" the file? Dispose means cleaning up unmanaged resources when the object is no longer in use. FromFile is handling all of that for you. If you mean Dispose as in delete the file, that's a different API call (`File.Delete()`). – MutantNinjaCodeMonkey Nov 25 '15 at 20:02
  • With the using statement i was able to delete the file in the directory. Every time i use FromFile(); The method locks the file. I can't go in the directory and delete the file. – taji01 Nov 25 '15 at 20:12
  • I see. Well that's an annoying bug. I corrected it using information found here: http://stackoverflow.com/questions/6576341/open-image-from-file-then-release-lock See if that works. :) – MutantNinjaCodeMonkey Nov 25 '15 at 20:25
  • I just tried implementing the "using" and the image displayed, but now my lblNumPage = 0 and lblCurrPage = 0. Meaning: The application thinks there is only 1 image. So when i click next non of the other images display. – taji01 Nov 25 '15 at 20:34
  • 1
    Well that's weird. The other thing you could do is revert back to the way you were before, but store that stream as a member variable as well. You wouldn't be able to use the "using" statement, and you'd have to close/Dispose it when the form is disposed (probably have to override the form's Dispose method to include that). – MutantNinjaCodeMonkey Nov 25 '15 at 20:40
  • How can i implement that? – taji01 Nov 25 '15 at 20:46
  • I tried implementing this in the protected override void Dispose i get an error: Form1 already defines a member called 'Dispose' with the same parameter types. – taji01 Nov 25 '15 at 21:06
  • 1
    That means that form already has a dispose override somewhere. Locate the method and add the `if (_stream != null) _stream.Dispose();` line to that. It might be in the .designer.cs file. – MutantNinjaCodeMonkey Nov 25 '15 at 21:10
  • I just caught that thanks, I tested it out again. I went to the directory while the application was open and while i was viewing the file. I tried deleting the file i was viewing it was still locked. When i try deleting it. Windows error message says File In Use: The action can't be completed because the file is open in vshost32.exe (visual studio) – taji01 Nov 25 '15 at 21:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/96189/discussion-between-mutantninjacodemonkey-and-taji01). – MutantNinjaCodeMonkey Nov 25 '15 at 21:24