2016-03-02 87 views
2

概述
我正在使用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); 
    } 
    } 
} 
+0

什么没有按没有与你尝试过的东西一起工作? – Jacobr365

+0

我试过的方法之一就是这样,但图像质量下降,应用程序仍然有点慢。如果我重新调整大小,唯一的方法就是不要打破它。那么这是我想出来的解决方案,(源,800,1100),当我这样做,它工作正常。如果我不这样做,应用程序停止并给我一个错误OutOfMemoryException未处理我的进程内存是864。@ Jacobr365 – taji01

回答

1

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

为了做到这一点,我们引入一个新成员

private Size pageSize; 

和最后一行不按Bitmap Constructor (Image, Size)文件实际大小调整如下

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

使用它:

从t中初始化位图类的新实例他指定现有的图像,缩放到指定的大小。

的,我们需要引入附加参数:

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)); 
+0

在private void Open(string path)'{var data = PageBuffer.Open(path,maxSize:1100,cashesize:5);'方法。我不正确地写它。 maxSize有一个错误。 – taji01

+1

应该是'maxSize:new Size(850,1100)' –

+0

嘿,感谢几天帮助我出去一对夫妇。我非常感谢所有的帮助。这是一段极好的代码,当您重新调整图像大小时,它会占用更少的内存,但会稍微扭曲它。只是为了让你知道,在我问你之前,我尝试自己编码/测试/解决它,但无论如何,你可能已经知道何时重新缩放或缩放图像以适应常规(8.5 x 11)的打印纸图像文件会稍微变形。有没有缩小图像的方法或策略,以便使用更少的内存,但打印高质量的tif图像? @Ivan Stoev – taji01

0
 Bitmap bmp = new Bitmap(newWidth, newHeight); 
     Graphics g = Graphics.FromImage(bmp); 
    for (int idx = 0; idx < count; idx++) 
     { 
      bmp.SelectActiveFrame(FrameDimension.Page, idx); 
     g.DrawImage(source, new Rectangle(0,0,source.Width,source.Height), 
     new Rectangle(0,0,bmp.Width,bmp.Height)); 
      ImageCodecInfo myImageCodecInfo; 
      System.Drawing.Imaging.Encoder myEncoder; 
      EncoderParameter myEncoderParameter; 
      EncoderParameters myEncoderParameters; 
      myImageCodecInfo = GetEncoderInfo("image/tiff"); 
      myEncoder = System.Drawing.Imaging.Encoder.Compression; 
      myEncoderParameters = new EncoderParameters(1); 
      myEncoderParameter = new EncoderParameter(
       myEncoder, 
       (long)EncoderValue.CompressionLZW); 
      myEncoderParameters.Param[0] = myEncoderParameter;  
      bmp.SaveAdd(byteStream, ImageFormat.Tiff); 
     } 


private static ImageCodecInfo GetEncoderInfo(String mimeType) 
     { 
      int j; 
      ImageCodecInfo[] encoders; 
      encoders = ImageCodecInfo.GetImageEncoders(); 
      for (j = 0; j < encoders.Length; ++j) 
      { 
       if (encoders[j].MimeType == mimeType) 
        return encoders[j]; 
      } 
      return null; 
     } 
+0

如何将其实施到我的代码中。当我创建一个新的位图时,只有'tif'图像的第一页在图片框中填充。 – taji01

+0

我编辑了答案 –

+0

嗨,感谢您的回复,但是我在哪里将它放在我的代码中? – taji01

相关问题