您能看出这个生成缩略图的方法有什么问题吗?


昨天又使用了某个多年以前写的,或者说是“收集”而来的方法。这个方法的作用是根据一幅图片(一般是幅大图)生成它的缩略图。这个方法用了许多年了,一直没有去怀疑过它的正确性,但是昨天忽然发现它一直以来都存在一个问题,虽然可能不是那么明显,而且也不会造成太大问题(否则早就发现了)——但是,这的确是个不妥的地方。这个问题在我看来也有一定借鉴意义,因此我打算把它展示出来。那么,您能否看出它究竟是错在什么地方了呢?

  生成缩略图的规则很简单,概括地说有三点:

  包含图片完整内容,以及长宽比不变。

  尺寸尽可能大,但如果图片本身很小,也不做拉伸。

  不超过指定的width * height的范围内。

  这个规则其实就是最传统的缩略图生成方式,使用如Windows照片浏览器等软件打开图片后,一般来说默认都会如此调整图片尺寸。而我们如果需要写一段代码来实现这一点也并不困难,以下便是我用了许多年的方法:

/// <summary> 
/// Creates a thumbnail from an existing image. Sets the biggest dimension of the 
/// thumbnail to either desiredWidth or Height and scales the other dimension down 
/// to preserve the aspect ratio 
/// </summary> 
/// <param name="imageStream">stream to create thumbnail for</param> 
/// <param name="desiredWidth">maximum desired width of thumbnail</param> 
/// <param name="desiredHeight">maximum desired height of thumbnail</param> 
/// <returns>Bitmap thumbnail</returns> 
public Bitmap CreateThumbnail(Bitmap originalBmp, int desiredWidth, int desiredHeight) 
{ 
  // If the image is smaller than a thumbnail just return it 
  if (originalBmp.Width <= desiredWidth && originalBmp.Height <= desiredHeight) 
  { 
    return originalBmp; 
  } 
 
  int newWidth, newHeight; 
 
  // scale down the smaller dimension 
  if ((decimal)desiredWidth / originalBmp.Width < (decimal)desiredHeight / originalBmp.Height) 
  { 
    decimal desiredRatio = (decimal)desiredWidth / originalBmp.Width; 
    newWidth = desiredWidth; 
    newHeight = (int)(originalBmp.Height * desiredRatio); 
  } 
  else 
  { 
    decimal desiredRatio = (decimal)desiredHeight / originalBmp.Height; 
    newHeight = desiredHeight; 
    newWidth = (int)(originalBmp.Width * desiredRatio); 
  } 
 
  // This code creates cleaner (though bigger) thumbnails and properly 
  // and handles GIF files better by generating a white background for 
  // transparent images (as opposed to black) 
  // This is preferred to calling Bitmap.GetThumbnailImage() 
  Bitmap bmpOut = new Bitmap(newWidth, newHeight); 
 
  using (Graphics graphics = Graphics.FromImage(bmpOut)) 
  { 
    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 
    graphics.FillRectangle(Brushes.White, 0, 0, newWidth, newHeight); 
    graphics.DrawImage(originalBmp, 0, 0, newWidth, newHeight); 
  } 
 
  return bmpOut; 
}

它的具体来源我已经记不得了,不过从英文注释上来看这应该是个老外写的代码,那么我们现在就来解释一番。首先,这个方法会先判断源图片的大小是否已经可以放入目标区域(desiredWidth * desiredHeight)中了,如果是,则直接返回源图片。如果不满足第一个判断,则说明宽和高之中至少有一个超出了目标尺寸,而我们要对源图片进行等比例缩放。

  那么缩放的“比例”又是多少呢?自然是“宽”或“高”中缩放“程度大”的那个。因为如果按照缩放程度小的那条边的比例来改变图片尺寸,那么另一条边势必会超出范围。因此,我们接下来便是比较desiredWidth与originalBmp.Width之比,以及desiredHeight与 originalBmp.Height之比孰大孰小。哪个小,则意味着我们要把它作为缩放依据,因为它对图片尺寸的限制要比另一条边来的严格。于是乎,再第二个条件判断的任意一个分支中,我们都可以计算出缩放的比例(desiredRatio),然后把作为“依据”的那条边设为 desiredWidth/Height,将另一条边根据缩放比例进行调整。在计算和比较过程中我们都使用了decimal数据类型,因为它是.NET中精度最高的浮点数类型,我们以此减少计算过程中所带来的误差。

  至于得到了newWidth和newHeight之后,我们便只要根据这个尺寸生成目标图片即可,它便是源图片的缩略图,符合我们之前提出的三个要求。

  听起来很简单,看上去也没有什么问题,不是吗?不过,其实这个实现中有一个不那么明显的问题,您发现了吗


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3