使用.tif图像进入下一页时提升性能 [英] Boost the Performance when Advancing to the Next Page Using .tif Images

查看:153
本文介绍了使用.tif图像进入下一页时提升性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用WinForms。在我的表格中,我有一个打开和下一个按钮。我的应用程序将 .tif 图像打开到图片框中。我使用的所有 .tif 图像都有多个页面。下一个按钮用于转到 tif 图像中的下一页。我使用的这些 .tif 图像非常大。



示例:尺寸: 2600 x 3300( .tif images)



问题:如何优化我的申请表现如何?我已阅读/研究过我可能需要直接从计算机内存和其他一些方法加载图像。我该怎么做呢?还是有更好的编码方式?



这是我到目前为止的代码,但是当我去的时候我的应用程序有点滞后下一页。你可以帮帮我吗?



下面是一个包含多个测试页面的大型tif图像链接。



链接



解决方案

事实证明,缓慢的部分是 Image.SelectActiveFrame 调用。



像往常一样,解决方案是缓存。但是,为了不增加初始加载时间,应该在后台懒惰地执行。



这个想法很简单。启动一个工作线程并将所有图像帧作为单独的位图加载到一个数组中。然后使用数组中的缓存图像而不是 SelectActiveFrame



由于所有这些都需要一些线程同步,我将它封装在一个帮助器类中:

  class PageBuffer:IDisposable 
{
public static PageBuffer Open(string path)
{
return new PageBuffer(File.OpenRead(path));
}

private PageBuffer(Stream stream)
{
this.stream = stream;
Source = Image.FromStream(stream);
PageCount = Source.GetFrameCount(FrameDimension.Page);
if(PageCount< 2)返回;
pages = new Image [PageCount];
var worker = new Thread(LoadPages){IsBackground = true};
worker.Start();
}

private void LoadPages()
{
for(int index = 0 ;; index ++)
{
lock(syncLock)
{
如果(处置)返回;
if(index> = pages.Length)
{
//如果您不需要源图像,
//取消注释以下行以释放一些资源
// DisposeSource();
返回;
}
if(pages [index] == null)
pages [index] = LoadPage(index);
}
}
}

private Image LoadPage(int index)
{
Source.SelectActiveFrame(FrameDimension.Page,index);
返回新的位图(来源);
}

私有流流;
private Image []页面;
私有对象syncLock = new object();
私人布尔处置;

public Image Source {get;私人集; }
public int PageCount {get;私人集; }
public Image GetPage(int index)
{
if(dispos)throw new ObjectDisposedException(GetType()。Name);
if(PageCount< 2)返回Source;
var image = pages [index];
if(image == null)
{
lock(syncLock)
{
image = pages [index];
if(image == null)
image = pages [index] = LoadPage(index);
}
}
返回图片;
}

public void Dispose()
{
if(dispos)return;
lock(syncLock)
{
dispos = true;
if(pages!= null)
{
foreach(页面中的var项目)
if(item!= null)item.Dispose();
pages = null;
}
DisposeSource();
}
}

private void DisposeSource()
{
if(Source!= null)
{
Source。处置();
Source = null;
}
if(stream!= null)
{
stream.Dispose();
stream = null;
}
}
}

完整的工作演示:

 使用System;使用System.Drawing 
;
使用System.Drawing.Imaging;
使用System.IO;
使用System.Threading;
使用System.Windows.Forms;

名称空间演示
{
class TestForm:Form
{
public TestForm()
{
var panel = new Panel {Dock = DockStyle.Top,BorderStyle = BorderStyle.FixedSingle};
openButton = new Button {Text =Open,Top = 8,Left = 16};
prevButton = new Button {Text =Prev,Top = 8,Left = 16 + openButton.Right};
nextButton = new Button {Text =Next,Top = 8,Left = 16 + prevButton.Right};
panel.Height = 16 + openButton.Height;
panel.Controls.AddRange(new Control [] {openButton,prevButton,nextButton});
pageViewer = new PictureBox {Dock = DockStyle.Fill,SizeMode = PictureBoxSizeMode.Zoom};
ClientSize = new Size(850,1100 + panel.Height);
Controls.AddRange(new Control [] {panel,pageViewer});
openButton.Click + = OnOpenButtonClick;
prevButton.Click + = OnPrevButtonClick;
nextButton.Click + = OnNextButtonClick;
Disposed + = OnFormDisposed;
UpdatePageInfo();
}

private Button openButton;
private Button prevButton;
private Button nextButton;
private PictureBox pageViewer;
private PageBuffer pageData;
private int currentPage;

private void OnOpenButtonClick(object sender,EventArgs e)
{
using(var dialog = new OpenFileDialog())
{
if(dialog。 ShowDialog(this)== DialogResult.OK)
Open(dialog.FileName);
}
}

private void OnPrevButtonClick(object sender,EventArgs e)
{
SelectPage(currentPage - 1);
}

private void OnNextButtonClick(object sender,EventArgs e)
{
SelectPage(currentPage + 1);
}

private void OnFormDisposed(object sender,EventArgs e)
{
if(pageData!= null)
pageData.Dispose();
}

private void打开(字符串路径)
{
var data = PageBuffer.Open(path);
pageViewer.Image = null;
if(pageData!= null)
pageData.Dispose();
pageData = data;
SelectPage(0);
}

private void SelectPage(int index)
{
pageViewer.Image = pageData.GetPage(index);
currentPage = index;
UpdatePageInfo();
}

private void UpdatePageInfo()
{
prevButton.Enabled = pageData!= null&& currentPage> 0;
nextButton.Enabled = pageData!= null&& currentPage< pageData.PageCount - 1;
}
}

静态类程序
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}

class PageBuffer:IDisposable
{
public static PageBuffer Open(string path)
{
return new PageBuffer(File.OpenRead(路径));
}

private PageBuffer(Stream stream)
{
this.stream = stream;
Source = Image.FromStream(stream);
PageCount = Source.GetFrameCount(FrameDimension.Page);
if(PageCount< 2)返回;
pages = new Image [PageCount];
var worker = new Thread(LoadPages){IsBackground = true};
worker.Start();
}

private void LoadPages()
{
for(int index = 0 ;; index ++)
{
lock(syncLock)
{
如果(处置)返回;
if(index> = pages.Length)
{
//如果您不需要源图像,
//取消注释以下行以释放一些资源
// DisposeSource();
返回;
}
if(pages [index] == null)
pages [index] = LoadPage(index);
}
}
}

private Image LoadPage(int index)
{
Source.SelectActiveFrame(FrameDimension.Page,index);
返回新的位图(来源);
}

私有流流;
private Image []页面;
私有对象syncLock = new object();
私人布尔处置;

public Image Source {get;私人集; }
public int PageCount {get;私人集; }
public Image GetPage(int index)
{
if(dispos)throw new ObjectDisposedException(GetType()。Name);
if(PageCount< 2)返回Source;
var image = pages [index];
if(image == null)
{
lock(syncLock)
{
image = pages [index];
if(image == null)
image = pages [index] = LoadPage(index);
}
}
返回图片;
}

public void Dispose()
{
if(dispos)return;
lock(syncLock)
{
dispos = true;
if(pages!= null)
{
foreach(页面中的var项目)
if(item!= null)item.Dispose();
pages = null;
}
DisposeSource();
}
}

private void DisposeSource()
{
if(Source!= null)
{
Source。处置();
Source = null;
}
if(stream!= null)
{
stream.Dispose();
stream = null;
}
}
}
}

UPDATE:正如评论中所提到的,上面的实现是使用非常简单的贪心缓存策略,它使用大量内存而不适用于大文件。



但好处是,一旦逻辑被封装在类中,我们就可以在不触及应用程序代码的情况下更改策略。例如,我们可以删除缓存(返回初始状态),或者通过维护一小组缓存图像窗口来优化上一个/下一个导航

  class PageBuffer:IDisposable 
{
public const int DefaultCacheSize = 5;

public static PageBuffer Open(string path,int cacheSize = DefaultCacheSize)
{
return new PageBuffer(File.OpenRead(path),cacheSize);
}

private PageBuffer(Stream stream,int cacheSize)
{
this.stream = stream;
source = Image.FromStream(stream);
pageCount = source.GetFrameCount(FrameDimension.Page);
if(pageCount< 2)return;
pageCache = new Image [Math.Min(pageCount,Math.Max(cacheSize,3))];
var worker = new Thread(LoadPages){IsBackground = true};
worker.Start();
}

private void LoadPages()
{
while(true)
{
lock(syncLock)
{
如果(处置)返回;
int index = Array.FindIndex(pageCache,0,pageCacheSize,p => p == null);
if(index< 0)
Monitor.Wait(syncLock);
else
pageCache [index] = LoadPage(pageCacheStart + index);
}
}
}

private Image LoadPage(int index)
{
source.SelectActiveFrame(FrameDimension.Page,index);
返回新的Bitmap(源码);
}

私有流流;
private图片来源;
private int pageCount;
private Image [] pageCache;
private int pageCacheStart,pageCacheSize;
私有对象syncLock = new object();
私人布尔处置;

public Image Source {get {return source;
public int PageCount {get {return pageCount;
public Image GetPage(int index)
{
if(dispos)throw new ObjectDisposedException(GetType()。Name);
if(PageCount< 2)返回Source;
lock(syncLock)
{
AdjustPageCache(index);
int cacheIndex = index - pageCacheStart;
var image = pageCache [cacheIndex];
if(image == null)
image = pageCache [cacheIndex] = LoadPage(index);
返回图片;
}
}

private void AdjustPageCache(int pageIndex)
{
int start,end;
if((start = pageIndex - pageCache.Length / 2)< = 0)
end =(start = 0)+ pageCache.Length;
else if((end = start + pageCache.Length)> = PageCount)
start =(end = PageCount) - pageCache.Length;
if(start< pageCacheStart)
{
int shift = pageCacheStart - start;
if(shift> = pageCacheSize)
ClearPageCache(0,pageCacheSize);
else
{
ClearPageCache(pageCacheSize - shift,pageCacheSize);
for(int j = pageCacheSize - 1,i = j - shift; i> = 0; j--,i--)
Exchange(ref pageCache [i],ref pageCache [j] );
}
}
else if(start> pageCacheStart)
{
int shift = start - pageCacheStart;
if(shift> = pageCacheSize)
ClearPageCache(0,pageCacheSize);
else
{
ClearPageCache(0,shift);
for(int j = 0,i = shift; i< pageCacheSize; j ++,i ++)
Exchange(ref pageCache [i],ref pageCache [j]);
}
}
if(pageCacheStart!= start || pageCacheStart + pageCacheSize!= end)
{
pageCacheStart = start;
pageCacheSize = end - start;
Monitor.Pulse(syncLock);
}
}

void ClearPageCache(int start,int end)
{
for(int i = start; i< end; i ++)
Dispose(ref pageCache [i]);
}

static void Dispose< T>(ref T target)其中T:class,IDisposable
{
var value = target;
if(value!= null)value.Dispose();
target = null;
}

static void Exchange< T>(ref T a,ref T b){var c = a; a = b; b = c; }

public void Dispose()
{
if(dispos)return;
lock(syncLock)
{
dispos = true;
if(pageCache!= null)
{
ClearPageCache(0,pageCacheSize);
pageCache = null;
}
Dispose(ref source);
Dispose(ref stream);
if(pageCount> 2)
Monitor.Pulse(syncLock);
}
}
}

或实施其他智能缓存策略。我们甚至可以通过实施策略模式来选择策略。



这将是另一个故事。对于OP用例,第二个 PageBuffer 实现应该足够了。


I'm using WinForms. In my forms I have an open and a next button. My application opens .tif images into a picturebox. All the .tif images I work with have multiple pages. The next button is for going to the next page in the tif image. These .tif images I work with are very large.

Example: Dimensions: 2600 x 3300 (.tif images)

Question: How do I optimize the performance of my application? I've read/researched that I might have to load the images directly from the computers memory and some other methods. How would i go about this or is there a better way of coding this?

That is the code i have so far, but my application lags a little when i go to the next page. Can you please help me out?

Below is a link of a large tif image with multiple pages for testing.

Link

http://www.filedropper.com/tiftestingdoc

    FileStream _stream;
    Image _myImg; // setting the selected tiff
    string _fileName;


    private Image _Source = null;
    private int _TotalPages = 0;

    private int intCurrPage = 0;

    private void Clone_File()
    { // Reads file, then copys the file and loads it in the picture box as a temporary image doc. That way files are not locked in users directory when in use by this application.
        try
        {

            if (_myImg == null)
            {

                try
                {
                    _fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
                    File.Copy(@"C:\Picture_Doc\The_Image.tif", _fileName);
                    _stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
                    this._Source = Image.FromStream(_stream);
                }
                catch (Exception ex)
                {
                }
            }
            _TotalPages = _Source.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);

            intCurrPage = 1;

            Display_Page(intCurrPage);

        }catch(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

    }

    private void Show_Processing_Image_Label()
    {
        Application.DoEvents();
    }

    private void Display_Page(int PageNumber, RotateFlipType Change)
    {
        if (pictureBox1.Image != null && pictureBox1.Image != _Source)
        {
            //Release memory for old rotated image
            pictureBox1.Image.Dispose();
        }

        // set the variable to null for easy Garbage Collection cleanup
        pictureBox1.Image = null;

        _Source.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, PageNumber - 1);

        pictureBox1.Image = new Bitmap(_Source);

        pictureBox1.Image.RotateFlip(Change);

        pictureBox1.Refresh();
        //Refresh() Calls Invalidate and then Update to refresh synchronously.
    }

    private void Display_Page(int PageNumber)
    {
        Show_Processing_Image_Label();

        //You could adjust the PictureBox size here for each frame OR adjust the image to fit the picturebox nicely

        if (pictureBox1.Image != _Source)
        {
            if (pictureBox1.Image != null)
            {
                //Release memory for old copy and set the variable to null for easy GC cleanup
                pictureBox1.Image.Dispose();
                pictureBox1.Image = null;
            }

            pictureBox1.Image = _Source;
        }

        pictureBox1.Image.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, PageNumber - 1);

        pictureBox1.Refresh();

    }

    private void Next_btn_Click(object sender, EventArgs e)
    {
        intCurrPage++;
        Display_Page(intCurrPage);
    }

    private void Open_btn_Click(object sender, EventArgs e)
    {

            if (_stream != null)
            {
                _myImg = null; //dispose the copy image
            }

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                Clone_File();
            }

            pictureBox1.Size = new Size(850, 1100);

     } 

解决方案

It turns out that the slow part is the Image.SelectActiveFrame call.

As usual, the solution is caching. However, in order to not increase the initial load time, it should be performed lazily on background.

The idea is simple. Start a worker thread and load all the image frames as separate Bitmaps in an array. Then use the cached image from the array instead of the SelectActiveFrame.

Since all that require some thread synchronization, I've encapsulated it in a helper class:

class PageBuffer : IDisposable
{
    public static PageBuffer Open(string path)
    {
        return new PageBuffer(File.OpenRead(path));
    }

    private PageBuffer(Stream stream)
    {
        this.stream = stream;
        Source = Image.FromStream(stream);
        PageCount = Source.GetFrameCount(FrameDimension.Page);
        if (PageCount < 2) return;
        pages = new Image[PageCount];
        var worker = new Thread(LoadPages) { IsBackground = true };
        worker.Start();
    }

    private void LoadPages()
    {
        for (int index = 0; ; index++)
        {
            lock (syncLock)
            {
                if (disposed) return;
                if (index >= pages.Length)
                {
                    // If you don't need the source image, 
                    // uncomment the following line to free some resources
                    //DisposeSource();
                    return;
                }
                if (pages[index] == null)
                    pages[index] = LoadPage(index);
            }
        }
    }

    private Image LoadPage(int index)
    {
        Source.SelectActiveFrame(FrameDimension.Page, index);
        return new Bitmap(Source);
    }

    private Stream stream;
    private Image[] pages;
    private object syncLock = new object();
    private bool disposed;

    public Image Source { get; private set; }
    public int PageCount { get; private set; }
    public Image GetPage(int index)
    {
        if (disposed) throw new ObjectDisposedException(GetType().Name);
        if (PageCount < 2) return Source;
        var image = pages[index];
        if (image == null)
        {
            lock (syncLock)
            {
                image = pages[index];
                if (image == null)
                    image = pages[index] = LoadPage(index);
            }
        }
        return image;
    }

    public void Dispose()
    {
        if (disposed) return;
        lock (syncLock)
        {
            disposed = true;
            if (pages != null)
            {
                foreach (var item in pages)
                    if (item != null) item.Dispose();
                pages = null;
            }
            DisposeSource();
        }
    }

    private void DisposeSource()
    {
        if (Source != null)
        {
            Source.Dispose();
            Source = null;
        }
        if (stream != null)
        {
            stream.Dispose();
            stream = null;
        }
    }
}

A full working demo:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace Demo
{
    class TestForm : Form
    {
        public TestForm()
        {
            var panel = new Panel { Dock = DockStyle.Top, BorderStyle = BorderStyle.FixedSingle };
            openButton = new Button { Text = "Open", Top = 8, Left = 16 };
            prevButton = new Button { Text = "Prev", Top = 8, Left = 16 + openButton.Right };
            nextButton = new Button { Text = "Next", Top = 8, Left = 16 + prevButton.Right };
            panel.Height = 16 + openButton.Height;
            panel.Controls.AddRange(new Control[] { openButton, prevButton, nextButton });
            pageViewer = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom };
            ClientSize = new Size(850, 1100 + panel.Height);
            Controls.AddRange(new Control[] { panel, pageViewer });
            openButton.Click += OnOpenButtonClick;
            prevButton.Click += OnPrevButtonClick;
            nextButton.Click += OnNextButtonClick;
            Disposed += OnFormDisposed;
            UpdatePageInfo();
        }

        private Button openButton;
        private Button prevButton;
        private Button nextButton;
        private PictureBox pageViewer;
        private PageBuffer pageData;
        private int currentPage;

        private void OnOpenButtonClick(object sender, EventArgs e)
        {
            using (var dialog = new OpenFileDialog())
            {
                if (dialog.ShowDialog(this) == DialogResult.OK)
                    Open(dialog.FileName);
            }
        }

        private void OnPrevButtonClick(object sender, EventArgs e)
        {
            SelectPage(currentPage - 1);
        }

        private void OnNextButtonClick(object sender, EventArgs e)
        {
            SelectPage(currentPage + 1);
        }

        private void OnFormDisposed(object sender, EventArgs e)
        {
            if (pageData != null)
                pageData.Dispose();
        }

        private void Open(string path)
        {
            var data = PageBuffer.Open(path);
            pageViewer.Image = null;
            if (pageData != null)
                pageData.Dispose();
            pageData = data;
            SelectPage(0);
        }

        private void SelectPage(int index)
        {
            pageViewer.Image = pageData.GetPage(index);
            currentPage = index;
            UpdatePageInfo();
        }

        private void UpdatePageInfo()
        {
            prevButton.Enabled = pageData != null && currentPage > 0;
            nextButton.Enabled = pageData != null && currentPage < pageData.PageCount - 1;
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new TestForm());
        }
    }

    class PageBuffer : IDisposable
    {
        public static PageBuffer Open(string path)
        {
            return new PageBuffer(File.OpenRead(path));
        }

        private PageBuffer(Stream stream)
        {
            this.stream = stream;
            Source = Image.FromStream(stream);
            PageCount = Source.GetFrameCount(FrameDimension.Page);
            if (PageCount < 2) return;
            pages = new Image[PageCount];
            var worker = new Thread(LoadPages) { IsBackground = true };
            worker.Start();
        }

        private void LoadPages()
        {
            for (int index = 0; ; index++)
            {
                lock (syncLock)
                {
                    if (disposed) return;
                    if (index >= pages.Length)
                    {
                        // If you don't need the source image, 
                        // uncomment the following line to free some resources
                        //DisposeSource();
                        return;
                    }
                    if (pages[index] == null)
                        pages[index] = LoadPage(index);
                }
            }
        }

        private Image LoadPage(int index)
        {
            Source.SelectActiveFrame(FrameDimension.Page, index);
            return new Bitmap(Source);
        }

        private Stream stream;
        private Image[] pages;
        private object syncLock = new object();
        private bool disposed;

        public Image Source { get; private set; }
        public int PageCount { get; private set; }
        public Image GetPage(int index)
        {
            if (disposed) throw new ObjectDisposedException(GetType().Name);
            if (PageCount < 2) return Source;
            var image = pages[index];
            if (image == null)
            {
                lock (syncLock)
                {
                    image = pages[index];
                    if (image == null)
                        image = pages[index] = LoadPage(index);
                }
            }
            return image;
        }

        public void Dispose()
        {
            if (disposed) return;
            lock (syncLock)
            {
                disposed = true;
                if (pages != null)
                {
                    foreach (var item in pages)
                        if (item != null) item.Dispose();
                    pages = null;
                }
                DisposeSource();
            }
        }

        private void DisposeSource()
        {
            if (Source != null)
            {
                Source.Dispose();
                Source = null;
            }
            if (stream != null)
            {
                stream.Dispose();
                stream = null;
            }
        }
    }
}

UPDATE: As mentioned in the comments, the above implementation is using quite simple greedy caching strategy, which uses a lot of memory and does not work for big files.

The good thing though is that once the logic is encapsulated inside the class, we can change the strategy without touching our app code. For instance, we can remove the caching at all (return to the initial state), or optimize for "prev/next" navigation by maintaining a small set of cached image "window" like this

class PageBuffer : IDisposable
{
    public const int DefaultCacheSize = 5;

    public static PageBuffer Open(string path, int cacheSize = DefaultCacheSize)
    {
        return new PageBuffer(File.OpenRead(path), cacheSize);
    }

    private PageBuffer(Stream stream, int cacheSize)
    {
        this.stream = stream;
        source = Image.FromStream(stream);
        pageCount = source.GetFrameCount(FrameDimension.Page);
        if (pageCount < 2) return;
        pageCache = new Image[Math.Min(pageCount, Math.Max(cacheSize, 3))];
        var worker = new Thread(LoadPages) { IsBackground = true };
        worker.Start();
    }

    private void LoadPages()
    {
        while (true)
        {
            lock (syncLock)
            {
                if (disposed) return;
                int index = Array.FindIndex(pageCache, 0, pageCacheSize, p => p == null);
                if (index < 0)
                    Monitor.Wait(syncLock);
                else
                    pageCache[index] = LoadPage(pageCacheStart + index);
            }
        }
    }

    private Image LoadPage(int index)
    {
        source.SelectActiveFrame(FrameDimension.Page, index);
        return new Bitmap(source);
    }

    private Stream stream;
    private Image source;
    private int pageCount;
    private Image[] pageCache;
    private int pageCacheStart, pageCacheSize;
    private object syncLock = new object();
    private bool disposed;

    public Image Source { get { return source; } }
    public int PageCount { get { return pageCount; } }
    public Image GetPage(int index)
    {
        if (disposed) throw new ObjectDisposedException(GetType().Name);
        if (PageCount < 2) return Source;
        lock (syncLock)
        {
            AdjustPageCache(index);
            int cacheIndex = index - pageCacheStart;
            var image = pageCache[cacheIndex];
            if (image == null)
                image = pageCache[cacheIndex] = LoadPage(index);
            return image;
        }
    }

    private void AdjustPageCache(int pageIndex)
    {
        int start, end;
        if ((start = pageIndex - pageCache.Length / 2) <= 0)
            end = (start = 0) + pageCache.Length;
        else if ((end = start + pageCache.Length) >= PageCount)
            start = (end = PageCount) - pageCache.Length;
        if (start < pageCacheStart)
        {
            int shift = pageCacheStart - start;
            if (shift >= pageCacheSize)
                ClearPageCache(0, pageCacheSize);
            else
            {
                ClearPageCache(pageCacheSize - shift, pageCacheSize);
                for (int j = pageCacheSize - 1, i = j - shift; i >= 0; j--, i--)
                    Exchange(ref pageCache[i], ref pageCache[j]);
            }
        }
        else if (start > pageCacheStart)
        {
            int shift = start - pageCacheStart;
            if (shift >= pageCacheSize)
                ClearPageCache(0, pageCacheSize);
            else
            {
                ClearPageCache(0, shift);
                for (int j = 0, i = shift; i < pageCacheSize; j++, i++)
                    Exchange(ref pageCache[i], ref pageCache[j]);
            }
        }
        if (pageCacheStart != start || pageCacheStart + pageCacheSize != end)
        {
            pageCacheStart = start;
            pageCacheSize = end - start;
            Monitor.Pulse(syncLock);
        }
    }

    void ClearPageCache(int start, int end)
    {
        for (int i = start; i < end; i++)
            Dispose(ref pageCache[i]);
    }

    static void Dispose<T>(ref T target) where T : class, IDisposable
    {
        var value = target;
        if (value != null) value.Dispose();
        target = null;
    }

    static void Exchange<T>(ref T a, ref T b) { var c = a; a = b; b = c; }

    public void Dispose()
    {
        if (disposed) return;
        lock (syncLock)
        {
            disposed = true;
            if (pageCache != null)
            {
                ClearPageCache(0, pageCacheSize);
                pageCache = null;
            }
            Dispose(ref source);
            Dispose(ref stream);
            if (pageCount > 2)
                Monitor.Pulse(syncLock);
        }
    }
}

or implement other "smart" caching strategy. We can even make the strategy selectable by implementing the Strategy pattern.

Bu that will be another story. The second PageBuffer implementation should be sufficient for the OP use case.

这篇关于使用.tif图像进入下一页时提升性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆