c# - 在图片框中打开之前如何调整图像大小

标签 c# .net image winforms resize

概览
我正在使用 WinForms。在我的代码中我有这个方法。我正在尝试使用此代码按比例调整 tif 图像的大小。我的目标是从流(源)中获取图像并重新调整大小。我要调整大小的图像是具有多个页面的 tif 文档。如何按比例调整图像大小?

代码功能和计划
下面的代码在另一个线程中缓存 tif 图像。问题是我使用非常大的 tif 图像。当我打开这些非常大的包含多个页面的 tif 图像时,我的应用程序崩溃并滞后。我的目标或想法是在图像加载之前我应该​​重新调整它的大小,这样应用程序就不会崩溃并且性能变得更快。

示例 Tif 文档:

我创建了一个大的 tif 文件用于测试,您可以从这个链接下载它:http://www.filedropper.com/tiftestingdoc

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);
    }
  }
}

最佳答案

一种尝试是限制缓存位图的大小。

为了做到这一点,让我们介绍一个新成员

private Size pageSize;

并按如下方式使用

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

最后一行根据 Bitmap Constructor (Image, Size) 实际调整大小文档:

Initializes a new instance of the Bitmap class from the specified existing image, scaled to the specified size.

我们需要引入额外的参数:

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

private PageBuffer(Stream stream, Size maxSize, int cacheSize)
{
    // ...
} 

这样您就可以传递所需的最大 大小。在该限制下的实际页面大小,保持原始图像大小比例是这样计算的:

var sourceSize = source.Size;
float scale = Math.Min((float)maxSize.Width / sourceSize.Width, (float)maxSize.Height / sourceSize.Height);
var targetSize = new Size((int)(sourceSize.Width * scale), (int)(sourceSize.Height * scale));

完整代码如下:

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

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

    private PageBuffer(Stream stream, Size maxSize, 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))];
        pageSize = source.Size;
        if (!maxSize.IsEmpty)
        {
            float scale = Math.Min((float)maxSize.Width / pageSize.Width, (float)maxSize.Height / pageSize.Height);
            pageSize = new Size((int)(pageSize.Width * scale), (int)(pageSize.Height * scale));
        }
        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, pageSize);
    }

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

    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);
        }
    }
}

示例用法:

var data = PageBuffer.Open(path, new Size(850, 1100));

关于c# - 在图片框中打开之前如何调整图像大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35756082/

相关文章:

.net - 使用 .Net 和 SQL Server 2005 保存/加载图片的最佳方式?

c# - 使用 JSON.net 将内部数组反序列化为对象

c# - Twitter Streaming API .NET 库

c# - C# 中的 IRC 库

.net - 基础设施 UltraGrid : how to remove default selection of first row

html - 文本对齐不适用于 img 标签

c# - 使用 WinSCP Session.GetFile 从 SFTP 进行流式传输失败 – ((WinSCP.PipeStream)stream).Length' 引发类型 'System.NotSupportedException' 的异常

c# - 如何在 c# 中创建自定义操作并将其绑定(bind)到 wix 设置项目

asp.net - 调用 PostAsync 后测试 HttpRequestMessage.Content 的值

image - 使用UL在垂直方向上居中图像并内嵌显示