<<  悲剧了……|NOIP2009 初赛普及组 (pas) 参考答案  >>
    VB6 GDI+ 入门教程[10] Bitmap魔法(3):图片保存 - [Visual Basic]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://vistaswx.blogbus.com/logs/48404882.html

    话说有没有注意我把所有教程名字改了呢?呵呵为了增加收录率呗。

    话说有没有注意我修改过Gdi+模块了呢?赶快看看,哦,多出来好多Sub/Function哟。其实这些都是为了方便大家而写的 :)。

    其实呢图片保存应该放在Image部分,但是Bitmap中用到图片保存也不少,所以我就单独开辟一个来讲这个“文件保存”的问题,这里演示就用Bitmap来演示。

    1.Clsid,Encode

    图片怎么保存的呢?我们先研究BMP。众所周知,BMP是无损图像格式,BMP就是老老实实把图片上每一个点保存下来,因此同样尺寸的2份BMP大小是一样的。

    由于它没有压缩,但是压缩需求一定是有的,于是出现了形形色色其他图片储存格式,例如GIF,JPG,PNG。图片要保存为这些格式的文件,都要经过一定的算法变换来压缩储存。在GDI+中我们无需知道图片保存的算法,但是怎么表示他们呢?就有了Clsid。GDI+由于某个目的只认Clsid,即类标识符,但是Clsid是一个字节数组,表示不方便,所以就有了相应的字符型表示方法Encode——我们给GDI+的Clsid首先通过一个API转换成相应格式才能用:ClsidFromString。

    附:Clsid from MSDN

    CLSID 是类标识符的缩写,同样 GUID 是全局唯一标识符的缩写。这两种缩写都是指一个 128 位的整数,其重复的统计概率很小,因此可以用作计算机和网络中的唯一标识符。此标识符通常表示为一个由 16 个成员组成的字节数组,或者表示为由十六进制数字组成的特殊格式字符串(其中字符 a-f 或 A-F 表示十进制数 10-15)。此字符串表示形式由 32 个连续的十六进制数字组成,或者由分别包含 8 个、4 个、4 个、4 个和 12 个十六进制数字的组组成,各组之间用连字符分隔。以连字符连接的字符串表示形式也可以用圆括号或大括号 ({}) 括起来。

    2.Params

    Param,即参数,保存图片也有参数,例如众所周知的JPEG有一个压缩比的参数。同样的,Params参数的传递也要通过Clsid,不过正是因为这样所以代码有点麻烦。

    3.GdipSaveImageToFile

    现在是真正的保存图像函数,先来看我写在模块中的一个已完成的函数。

    Public Function SaveImageToPNG(ByVal Image As Long, ByVal Path As String) As GpStatus
        Dim Params As EncoderParameters, EncParams() As Byte
        ReDim EncParams(Len(Params) - 1)
    
        SaveImageToPNG = GdipSaveImageToFile(Image, StrPtr(Path), GetImageEncoderClsid(PNG), EncParams(0))
        Erase EncParams
    End Function

    其中GetImageEncoderClsid(PNG)为获取PNG的Clsid的一个函数。我们保存32位PNG不需要任何参数,因此可以看见我们仅仅是传递给他一片空白数据而已(起始位EncParams(0)),由于数据有长度,即使是空白数据我们也要有足够的空白,因此我们之前重定义这片空白大小为Len(Params)即EncoderParameters结构体的长度。

    我们再来看看有参数保存是怎么回事。

    Public Function SaveImageToJPG(ByVal Image As Long, ByVal Path As String, ByVal Quality As Long) As GpStatus
        Dim Params As EncoderParameters, EncParams() As Byte
        
        Params.count = 1
        CLSIDFromString StrPtr(EncoderQuality), Params.Parameter.Guid
        Params.Parameter.NumberOfValues = 1
        Params.Parameter.type = 4
        Params.Parameter.value = VarPtr(Quality)
        ReDim EncParams(Len(Params) - 1)
        CopyMemory EncParams(0), Params, Len(Params)
        
        SaveImageToJPG = GdipSaveImageToFile(Image, StrPtr(Path), GetImageEncoderClsid(JPG), EncParams(0))
        Erase EncParams
    End Function

    我们首先设置了Params.count,这里储存了你传入的参数个数,本例中只设置了Quality一个参数,因此Params.count = 1。Params.Parameter.type = 4这个怎么理解呢?这个4其实是EncoderParameterValueTypeLong,Long字节长度,这个值其实跟Value有关系,因为Value可能接受不同的数据类型,它们长度不一样,因此我们要事先传给他Value数据长度,当然这里只能是4……如果你希望VB IDE出错或程序崩溃你可以写大一点,可惜不能BSOD呵呵(Blue Screen)……最后我们还要CopyMemory,因为我们传给他是一个Byte数组,而我们的Params是一个结构体,所以我们要复制这个结构体数据到这个Byte数组中,这样才能传递这个Byte数组给GDI+函数。

    4.[封装]SaveImageTo的使用

    我在模块中已经封装好几个常用的东西:GetImageEncoderClsid用于获取图像编码的Clsid;而SaveImageTo是一组我为了方便大家使用而写的函数,功能是保存Image/Bitmap以一个指定格式到一个文件。

    具体函数列表:

    Public Function SaveImageToPNG(ByVal Image As Long, ByVal Path As String) As GpStatus
    Public Function SaveImageToJPG(ByVal Image As Long, ByVal Path As String, ByVal Quality As Long) As GpStatus
    Public Function SaveImageToGIF(ByVal Image As Long, ByVal Path As String) As GpStatus
    Public Function SaveImageToBMP(ByVal Image As Long, ByVal Path As String) As GpStatus

    Image为传入的Image/Bitmap地址,Path为保存文件的路径,SaveImageToJPG中Quality为保存图像的质量(0~100)。返回值为GdipSaveImageToFile的返回值,代表保存文件的状态,0表示OK(成功)。

    我还在模块中加入了CreateBitmap和CreateBitmapWithGraphics函数,两者都是从内存创建位图,后者在创建位图后还将这个位图与Graphics绑定并传回。

    接下来演示我加入的这几个函数怎么使用。例:以不同格式保存图片

    Option Explicit
    
    Private Sub Form_Load()
        Dim Bitmap As Long, Graphics As Long, Brush As Long
    
        InitGDIPlus
        CreateBitmapWithGraphics Bitmap, Graphics, 50, 50
        
        GdipCreateSolidFill &H80FF0000, Brush  '半透明红色。保存后仅在PNG中有半透明效果。
        GdipFillRectangleI Graphics, Brush, 0, 0, 50, 50
    
        GdipDeleteBrush Brush
        GdipDeleteGraphics Graphics
    
        SaveImageToPNG Bitmap, "c:\1.png"
        SaveImageToJPG Bitmap, "c:\1.jpg", 100
        SaveImageToGIF Bitmap, "c:\1.gif"
        SaveImageToBMP Bitmap, "c:\1.bmp"
        
        GdipDisposeImage Bitmap
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        TerminateGDIPlus
    End Sub




    评论

  • 請問一下我只想利用此模組來轉換圖片的顏色 並另存新檔,但不想讓他在讀檔的時候出現在Form上

    請問該在哪邊作修改呢

    謝謝
    vIstaswx回复Tea说:
    原先从Form的HDC创建Graphics修改为:
    1.从内存创建Bitmap
    2.GdipGetImageGraphicsContext将Bitmap与Graphics绑定。

    参见本教程最后一个代码(以上两步已被封装为CreateBitmapWithGraphics)
    2009-11-13 16:59:58
  • 抱歉,已经找到原因,不小心在另一过程中把位图销毁了
    vIstaswx回复flybird说:
    呵呵。
    2009-10-24 19:54:31
  • 博主,请教一个问题,我使用 GdipCreateBitmapFromScan0 创建并使用这个位图,但是在本次操作中只能使用,否则会给出 ObjectBusy 错误,如下:

    ... 初始化 GDI+ ...

    GdipCreateBitmapFromScan0 W, H, 0, PixelFormat32bppARGB, ByVal 0&, bitmap
    GdipGetImageGraphicsContext bitmap, graphics
    GdipSetInterpolationMode graphics, InterpolationModeHighQualityBicubic

    ... 对场景 graphics 绘制内容 ...

    ... 使用这个位图 bitmap (如绘制或保存) ... <- 在这步如果使用多次 bitmap 将会给出 ObjectBusy 错误

    ... 清理所有对象 ...

    ... 卸载 GDI+ ...