调整图片大小 GDI+ 图形 .NET

4
我有一个缩小网站图像的方法,具体如下:

:我正在处理的一个网站

static byte[] createSmallerImage(
   BlogPhoto blogPhoto, 
   int newMaxWidth, 
   int newMaxHeight)
{
  Image img;
  using (MemoryStream originalImage = 
           new MemoryStream(blogPhoto.BlogPhotoImage))
  {
    img = Image.FromStream(originalImage);
  }

  int newWidth;
  int newHeight;
  byte[] arr;

  if (img.Width > img.Height)
  {
    if (img.Width <= newMaxWidth)
    {

      using (MemoryStream thumbStr = new MemoryStream())
      {
        img.Save(thumbStr, ImageFormat.Jpeg);
        img.Dispose();
        arr = thumbStr.ToArray();
      }
      return arr;
    }

    newWidth = newMaxWidth;
    newHeight = 
       (int)(((float)newWidth / (float)img.Width) * (float)img.Height);
  }
  else
  {
    if (img.Height <= newMaxHeight)
    {

      using (MemoryStream thumbStr = new MemoryStream())
      {
        img.Save(thumbStr, ImageFormat.Jpeg);
        img.Dispose();
        arr = thumbStr.ToArray();
      }
      return arr;
    }

    newHeight = newMaxHeight;
    newWidth = 
      (int)(((float)newHeight / (float)img.Height) * (float)img.Width);
  }

  Image thumb = new Bitmap(newWidth, newHeight);

  Graphics g = Graphics.FromImage(thumb);
  g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  g.SmoothingMode = SmoothingMode.HighQuality;
  g.PixelOffsetMode = PixelOffsetMode.HighQuality;
  g.CompositingQuality = CompositingQuality.HighQuality;

  g.DrawImage(img, 0f, 0f, (float)newWidth, (float)newHeight);


  using (MemoryStream thumbStr = new MemoryStream())
  {
    thumb.Save(thumbStr, ImageFormat.Jpeg);
    arr = thumbStr.ToArray();
  }

  g.Dispose();
  img.Dispose();

  return arr;
}

大部分时间它工作得很好,但有时会出现这个异常:GDI+ 中发生一般性错误。错误代码为 -2147467259。源:“System.Drawing”。这发生在 Image.Save(...) 上。我试图尽可能地让这段代码具有防御性,但仍然不知道原因。如果有人知道答案那就太好了,或者欢迎提出批评意见。


你尝试保存到了加载的同一个文件吗? - Danny Varod
blogPhoto.BlogPhotoImage是从linq-to-sql检索到的byte[]。 - maxfridbe
4个回答

6
我个人使用此代码(没有流,虽然我不关心性能)来调整图片大小:
public Image resizeImage(int newWidth, int newHeight, string stPhotoPath)
 {
     Image imgPhoto = Image.FromFile(stPhotoPath); 

     int sourceWidth = imgPhoto.Width;
     int sourceHeight = imgPhoto.Height;

     //Consider vertical pics
    if (sourceWidth < sourceHeight)
    {
        int buff = newWidth;

        newWidth = newHeight;
        newHeight = buff;
    }

    int sourceX = 0, sourceY = 0, destX = 0, destY = 0;
    float nPercent = 0, nPercentW = 0, nPercentH = 0;

    nPercentW = ((float)newWidth / (float)sourceWidth);
    nPercentH = ((float)newHeight / (float)sourceHeight);
    if (nPercentH < nPercentW)
    {
        nPercent = nPercentH;
        destX = System.Convert.ToInt16((newWidth -
              (sourceWidth * nPercent)) / 2);
    }
    else
    {
        nPercent = nPercentW;
        destY = System.Convert.ToInt16((newHeight -
              (sourceHeight * nPercent)) / 2);
    }

    int destWidth = (int)(sourceWidth * nPercent);
    int destHeight = (int)(sourceHeight * nPercent);


    Bitmap bmPhoto = new Bitmap(newWidth, newHeight,
              PixelFormat.Format24bppRgb);

    bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
             imgPhoto.VerticalResolution);

    Graphics grPhoto = Graphics.FromImage(bmPhoto);
    grPhoto.Clear(Color.Black);
    grPhoto.InterpolationMode =
        InterpolationMode.HighQualityBicubic;

    grPhoto.DrawImage(imgPhoto,
        new Rectangle(destX, destY, destWidth, destHeight),
        new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
        GraphicsUnit.Pixel);

    grPhoto.Dispose();
    return bmPhoto;
}

希望这可以帮助你。

2

2
非常奇怪的是底层的 GDI+ 图像类不需要保持流处于打开状态。 (http://msdn.microsoft.com/en-us/library/ms535371.aspx) 但是通过 Reflector 进行搜索后,我最终发现了原因:他们必须将 .NET Stream 包装成适用于 GDI+ 的 IStream。接口是引用计数的,而 .NET Stream 对象则不是。由于它们只是在新接口中包装流对象,而不是复制实际数据,因此您必须保留源对象。 - Ian Boyd

1

需要注意的一件事是blogPhoto和底层数据的消失。它从哪里加载?它是从流中加载的吗?在createSmallerImage之前,该流是否已关闭?从关闭的流中加载的图像通常工作正常,只有偶尔会抛出通用的GDI+错误。


BlogPhotoImage是一个延迟加载的byte[],它从linq-to-sql中加载。 - maxfridbe
你是直接加载还是从MemoryStream中加载?在调用createSmallerImage之前,有没有可能它超出了作用域? - Kris Erickson

0

我不知道可能发生了什么,但也许使用较少的MemoryStreams问题就会消失:

using (Image original = Image.FromStream(new MemoryStream(blogPhoto)))
{
    using (MemoryStream thumbData = new MemoryStream())
    {
        int newWidth;
        int newHeight;
        if ((original.Width <= newMaxWidth) || 
            (original.Height <= newMaxHeight))
        {
            original.Save(thumbData, ImageFormat.Jpeg);
            return thumbData.ToArray();
        }

        if (original.Width > original.Height)
        {
            newWidth = newMaxWidth;
            newHeight = (int)(((float)newWidth / 
                (float)original.Width) * (float)original.Height);
        }
        else
        {
            newHeight = newMaxHeight;
            newWidth = (int)(((float)newHeight / 
                (float)original.Height) * (float)original.Width);
        }

        //original.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero)
        //    .Save(thumbData, ImageFormat.Jpeg);
        //return thumbData.ToArray();

        using (Image thumb = new Bitmap(newWidth, newHeight))
        {
            Graphics g = Graphics.FromImage(thumb);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.DrawImage(original, 0f, 0f, (float)newWidth, (float)newHeight);
            thumb.Save(thumbData, ImageFormat.Jpeg);
        }
    }
}

我有些困惑,因为你调用了以下代码:img.Save(imageSource, ImageFormat.Jpeg);和thumb.Save(imageSource, ImageFormat.Jpeg);然后返回imageSource.ToArray();这不会在内存流中导致两个图像吗? - maxfridbe
抱歉,是我的错误。你可以依靠GetThumbnailImage方法完成你的工作吗?我刚刚更新了我的答案以使用这个方法。 - Rubens Farias
我想我可以这样做(如果这个错误持续存在,我可能会尝试),但规范说: 当所请求的缩略图大小约为120 x 120像素时,GetThumbnailImage方法效果很好。如果您从具有嵌入式缩略图的图像中请求大型缩略图图像(例如300 x 300),则缩略图图像可能会出现明显的质量损失。通过调用DrawImage方法来缩放主图像(而不是缩放嵌入式缩略图)可能更好。 但这对我创建较大的缩略图可能行不通。 - maxfridbe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接