.Net中如何将Gif、Jpeg等图片缩小为恰当宽度的图片

如果一个图片的宽度太宽,最好能将它缩小成为一个恰当的宽度,比如500px。如果不预先缩小,太宽的图片传递到浏览器中,用户看不到全图,需要拉动滚动条。太宽的图片文件也会比较大,消耗带宽。

长度的计算

如果原始图片的宽度是w、高度是h,需要等比例缩小宽度为maxWidth,w>maxWidth,那么高度计算如下:

CInt(Math.Ceiling(CDbl(maxWidth) / CDbl(w) * h))

这里对于小数部分采用进一法,是依从Photoshop的处理方法。

基本流程

  • 载入原始Bitmap:建立一个Bitmap对象,将原始图片载入Bitmap;
  • 建立目标Bitmap:建立另外一个Bitmap对象,初始化为原始图片缩小后应该成为的宽度和高度;
  • 建立目标Graphics:基于目标Bitmap,建立Graphics对象,准备进行绘图;
  • 绘制图片并缩小:将原始Bitmap缩小并绘制到Graphics中;
  • 保存目标Bitmap:将目标Bitmap保存成为相应的图片格式。
Imports System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging

......

Private Shared JpegCodecInfo As ImageCodecInfo = GetCodecInfo(ImageFormat.Jpeg)
Private Shared JpegEncParams As EncoderParameters = GetJpegEncParams()

Public Shared Function Resize( _
ByVal srcStm As Stream, _
ByVal dstStm As Stream, _
ByVal maxWidth As Int32 _
) As Boolean
' Load the source image.
Dim srcImg As Bitmap = New Bitmap(srcStm, True)

' Create the destination image.
Dim w As Int32 = srcImg.Width
If w <= maxWidth Then
srcStm.Position = 0
CopyStream(srcStm, dstStm)
Return True
End If
Dim h As Int32 = CInt(Math.Ceiling(CDbl(maxWidth) / CDbl(w) * srcImg.Height))
Dim dstImg As Bitmap = New Bitmap(maxWidth, h)

' Create the graphics and set 2D properties
Dim gfx As Graphics = Graphics.FromImage(dstImg)
gfx.CompositingMode = CompositingMode.SourceCopy
gfx.CompositingQuality = CompositingQuality.HighQuality
gfx.SmoothingMode = SmoothingMode.HighQuality
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality

' Scale transform the source image into destination image
Dim srcRect As Rectangle = New Rectangle(0, 0, srcImg.Width, srcImg.Height)
Dim dstRect As Rectangle = New Rectangle(0, 0, maxWidth, h)
gfx.DrawImage(srcImg, dstRect, srcRect, GraphicsUnit.Pixel)
gfx.Dispose()

' Save into the destination stream
If srcImg.RawFormat.Equals(ImageFormat.Jpeg) Then
dstImg.Save(dstStm, JpegCodecInfo, JpegEncParams)
Else
dstImg.Save(dstStm, srcImg.RawFormat)
End If
dstImg.Dispose()
srcImg.Dispose()
Return True
End Function

Private Shared Sub CopyStream( _
ByVal srcStm As Stream, _
ByVal dstStm As Stream _
)
Dim buf As Byte() = New Byte(BufSize - 1) {}
Dim len As Int32 = srcStm.Read(buf, 0, buf.Length)
Do While len > 0
dstStm.Write(buf, 0, len)
len = srcStm.Read(buf, 0, buf.Length)
Loop
End Sub

Private Shared Function GetCodecInfo( _
ByVal imgfmt As ImageFormat _
) As ImageCodecInfo
Dim imgencs As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders
For i As Int32 = 0 To imgencs.Length - 1
If imgencs(i).FormatID = imgfmt.Guid Then
Return imgencs(i)
End If
Next
Return Nothing
End Function

Private Shared Function GetJpegEncParams( _
) As EncoderParameters
Dim encparams As EncoderParameters = New EncoderParameters(1)
encparams.Param(0) = New EncoderParameter(Encoder.Quality, CType(89L, Int32))
Return encparams
End Function

载入原始Bitmap

Bitmap属于System.Drawing类库,是从Image继承而来的。这个Bitmap并不是BMP文件,可以认为是图片在内存中的内部格式,Bitmap可以从各种支持的图片格式文件中建立,包括Gif、Jpeg、Png、Tiff、Bmp、Wmf、Icon、Emf、Exif。

Dim srcImg As Bitmap = New Bitmap(srcStm, True)

这里是从一个Stream中读入原始图片,也可以直接从文件中读入原始图片。

Dim srcImg As Bitmap = New Bitmap(srcFilePath, True)

建立Bitmap对象时的第二个参数是Boolean类型的useIcm,设置为True的时候表示采用颜色修正(Color Correction)算法,这个参数如果设置为False,会出现目标图片变色的问题。

建立目标Bitmap

用计算好的宽度和高度建立一个新的Bitmap。这个绘图区域每个点(Pixel)的颜色是缺省的Format32bppArgb,即32位色,每8个二进制位分别用于Alpha、红、绿、蓝这些元素。

Dim dstImg As Bitmap = New Bitmap(maxWidth, h)

建立目标Graphics

Bitmap对象上是无法进行绘图操作的,必须建立Graphics对象才能开始进行绘图,这需要用到FromImage方法。Graphics还需要设定若干属性,来说明图片绘制时候的精度。下面这些设定保证了将用图像精度最高、最平滑的方法进行图片缩小。

Dim gfx As Graphics = Graphics.FromImage(dstImg)
gfx.CompositingMode = CompositingMode.SourceCopy
gfx.CompositingQuality = CompositingQuality.HighQuality
gfx.SmoothingMode = SmoothingMode.HighQuality
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality

Graphics属于System.Drawing类库。

绘制图片并缩小

这一个步骤可以试用Graphics.DrawImage方法来一步完成。应该也是可以用Graphics.ScaleTransform这个方法来做的,程序会有所不同,没有试验过。

Dim srcRect As Rectangle = New Rectangle(0, 0, srcImg.Width, srcImg.Height)
Dim dstRect As Rectangle = New Rectangle(0, 0, maxWidth, h)
gfx.DrawImage(srcImg, dstRect, srcRect, GraphicsUnit.Pixel)

保存目标Bitmap

目标图片可以用和原始图片相同的图片格式进行保存。不过,Jpeg图片保存的时候,似乎是用60%的图片质量进行保存。测试后发现,.net这些类的绘图算法不如Photoshop,要吧图片质量提高到89%左右,才能和Photoshop中60%的图片质量相当。

If srcImg.RawFormat.Equals(ImageFormat.Jpeg) Then
dstImg.Save(dstStm, JpegCodecInfo, JpegEncParams)
Else
dstImg.Save(dstStm, srcImg.RawFormat)
End If

这里是向一个目标Stream中保存图片,也可以直接向文件中保存。

dstImg.Save(dstFilePath, srcImg.RawFormat)

代码中的GetCodecInfo和GetJpegEncParams函数是用来准备保存Jpeg图片时候图片质量参数的,前者准备一个ImageCodecInfo对象,后者准备一个EncoderParameters对象,它们都属于System.Drawing.Imaging类库。

释放资源

似乎Bitmap和Graphics的方法会占用很多系统资源,需要在操作完成的时候用Dispose进行释放。

进一步讨论

参见在.Net中采用OctreeQuantizer提高Gif文件的画面质量

代码来源

缩小图片

Google Group: Reduce image size

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

......

Bitmap sourceImage = new Bitmap(fi.FullName);
Bitmap targetImage = new Bitmap(800, 600);

using(Graphics gfx = Graphics.FromImage(targetImage)) {
gfx.DrawImage(
sourceImage,
new Rectangle(0, 0, targetImage.Width, targetImage.Height),
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
GraphicsUnit.Pixel);
gfx.Dispose();
}
targetImage.Save(Path.Combine(target.FullName, fi.Name), ImageFormat.Jpeg);

......

Jpeg格式的图片质量参数设定

MSDN: Encoder.Quality Field

......
myImageCodecInfo = GetEncoderInfo("image/jpeg")
......
myEncoder = Encoder.Quality
......
myEncoderParameter = New EncoderParameter(myEncoder, CType(75L, Int32))
myEncoderParameters.Param(0) = myEncoderParameter
myBitmap.Save("Shapes075.jpg", myImageCodecInfo, myEncoderParameters)
......

Google Group: Problems changing image resolution in VB.NET

......
For Each codec As ImageCodecInfo In codecs
If (codec.MimeType = "image/jpeg") Then
ici = codec
End If
Next
Dim ep As EncoderParameters = New EncoderParameters(1)
ep.Param(0) = New EncoderParameter(Encoder.Quality, Quality)
......
img.Save(s, ici, ep)
......

Graphics的参数影响图片变形时候的图片质量

Google Group: Graphics and resizing images better than Image.GetThumbnailImage quality

With objGraphics
.CompositingMode = Drawing2D.CompositingMode.SourceOver
.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
.InterpolationMode = Drawing2D.InterpolationMode.High
.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
.DrawImage(objNewImage, 0, 0, 160, 120))
.Dispose()
End With
objImageBlank.Save(strNewFileName, System.Drawing.Imaging.ImageFormat.Jpeg)
objNewImage.Dispose()

获得需要的图片ImageCodecInfo

Google Group: Bitmap.Save with Encoder.Compression does not work...

......
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
}
......

作者: 杰棍 [Jegwon]

波波坡原创文章 链接:http://www.bobopo.com/article/code/dotnet_gif_jpeg_resize.htm

标签:

关键词: .Net Framework, VB.Net, Gif, Jpeg, 图片缩小, Resize, Bitmap, DrawImage

创建日期: 2008-01-05

文库 微博 博客 作品 首页