【问题标题】:Converting 24 bpp to 4 bpp with GDI+使用 GDI+ 将 24 bpp 转换为 4 bpp
【发布时间】:2011-01-31 19:52:21
【问题描述】:

我的程序目前将 4 bpp(每像素位数)的 TIFF 作为位图,将其转换为图形,添加一些文本字符串,然后将其再次保存为 TIFF 文件。默认情况下,输出的 Bitmap.Save() TIFF 文件似乎是 24 bpp(无论输入如何),并且比原始 TIFF 大很多。

是否可以保持与输出输入相同的 4 bpp 调色板编码,如果不能,如何将 Bitmap PixelFormat 从 24bpp 转换为 4 bpp-indexed?

我在Bob Powell: Locking Bits 看到了一个将 24 bpp 转换为 1 bpp 的示例,但不知道如何为 4 bpp 执行此操作。

【问题讨论】:

    标签: vb.net gdi+ bitmap tiff


    【解决方案1】:

    这是我在here 发布的课程的修改版本。它使用原始源站点的 cmets 中的 4bpp 逻辑。

    Public Class BitmapEncoder
        ''' <summary>
        ''' Copies a bitmap into a 1bpp/4bpp/8bpp bitmap of the same dimensions, fast
        ''' </summary>
        ''' <param name="b">original bitmap</param>
        ''' <param name="bpp">1 or 8, target bpp</param>
        ''' <returns>a 1bpp copy of the bitmap</returns>
        Public Shared Function ConvertBitmapToSpecified(ByVal b As System.Drawing.Bitmap, ByVal bpp As Integer) As System.Drawing.Bitmap
            Select Case bpp
                Case 1
                Case 4
                Case 8
                Case Else
                    Throw New ArgumentException("bpp must be 1, 4 or 8")
            End Select
    
    
            ' Plan: built into Windows GDI is the ability to convert
            ' bitmaps from one format to another. Most of the time, this
            ' job is actually done by the graphics hardware accelerator card
            ' and so is extremely fast. The rest of the time, the job is done by
            ' very fast native code.
            ' We will call into this GDI functionality from C#. Our plan:
            ' (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
            ' (2) Create a GDI monochrome hbitmap
            ' (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
            ' (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
    
            Dim w As Integer = b.Width, h As Integer = b.Height
            Dim hbm As IntPtr = b.GetHbitmap()
            ' this is step (1)
            '
            ' Step (2): create the monochrome bitmap.
            ' "BITMAPINFO" is an interop-struct which we define below.
            ' In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
            Dim bmi As New BITMAPINFO()
            bmi.biSize = 40
            ' the size of the BITMAPHEADERINFO struct
            bmi.biWidth = w
            bmi.biHeight = h
            bmi.biPlanes = 1
            ' "planes" are confusing. We always use just 1. Read MSDN for more info.
            bmi.biBitCount = CShort(bpp)
            ' ie. 1bpp or 8bpp
            bmi.biCompression = BI_RGB
            ' ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
            bmi.biSizeImage = CUInt((((w + 7) And &HFFFFFFF8) * h / 8))
            bmi.biXPelsPerMeter = 1000000
            ' not really important
            bmi.biYPelsPerMeter = 1000000
            ' not really important
            ' Now for the colour table.
            Dim ncols As UInteger = CUInt(1) << bpp
            ' 2 colours for 1bpp; 256 colours for 8bpp
            bmi.biClrUsed = ncols
            bmi.biClrImportant = ncols
            bmi.cols = New UInteger(255) {}
            ' The structure always has fixed size 256, even if we end up using fewer colours
            If bpp = 1 Then
                bmi.cols(0) = MAKERGB(0, 0, 0)
                bmi.cols(1) = MAKERGB(255, 255, 255)
            ElseIf bpp = 4 Then
                bmi.biClrUsed = 16
                bmi.biClrImportant = 16
                Dim colv1 As Integer() = New Integer(15) {8, 24, 38, 56, 72, 88, 104, 120, 136, 152, 168, 184, 210, 216, 232, 248}
    
                For i As Integer = 0 To 15
                    bmi.cols(i) = MAKERGB(colv1(i), colv1(i), colv1(i))
                Next
            ElseIf bpp = 8 Then
                For i As Integer = 0 To ncols - 1
                    bmi.cols(i) = MAKERGB(i, i, i)
                Next
            End If
            ' For 8bpp we've created an palette with just greyscale colours.
            ' You can set up any palette you want here. Here are some possibilities:
            ' greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
            ' rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
            '          for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
            ' optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
            ' 
            ' Now create the indexed bitmap "hbm0"
            Dim bits0 As IntPtr
            ' not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            Dim hbm0 As IntPtr = CreateDIBSection(IntPtr.Zero, bmi, DIB_RGB_COLORS, bits0, IntPtr.Zero, 0)
            '
            ' Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
            ' GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
            Dim sdc As IntPtr = GetDC(IntPtr.Zero)
            ' First we obtain the DC for the screen
            ' Next, create a DC for the original hbitmap
            Dim hdc As IntPtr = CreateCompatibleDC(sdc)
            SelectObject(hdc, hbm)
            ' and create a DC for the monochrome hbitmap
            Dim hdc0 As IntPtr = CreateCompatibleDC(sdc)
            SelectObject(hdc0, hbm0)
            ' Now we can do the BitBlt:
            BitBlt(hdc0, 0, 0, w, h, hdc, _
             0, 0, SRCCOPY)
            ' Step (4): convert this monochrome hbitmap back into a Bitmap:
            Dim b0 As System.Drawing.Bitmap = System.Drawing.Bitmap.FromHbitmap(hbm0)
            '
            ' Finally some cleanup.
            DeleteDC(hdc)
            DeleteDC(hdc0)
            ReleaseDC(IntPtr.Zero, sdc)
            DeleteObject(hbm)
            DeleteObject(hbm0)
            '
            Return b0
        End Function
    
    
        Private Shared SRCCOPY As Integer = &HCC0020
        Private Shared BI_RGB As UInteger = 0
        Private Shared DIB_RGB_COLORS As UInteger = 0
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
        End Function
    
        <System.Runtime.InteropServices.DllImport("user32.dll")> _
        Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr
        End Function
    
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) As IntPtr
        End Function
    
        <System.Runtime.InteropServices.DllImport("user32.dll")> _
        Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As Integer
        End Function
    
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function DeleteDC(ByVal hdc As IntPtr) As Integer
        End Function
    
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr
        End Function
    
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function BitBlt(ByVal hdcDst As IntPtr, ByVal xDst As Integer, ByVal yDst As Integer, ByVal w As Integer, ByVal h As Integer, ByVal hdcSrc As IntPtr, _
         ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal rop As Integer) As Integer
        End Function
    
    
        <System.Runtime.InteropServices.DllImport("gdi32.dll")> _
        Private Shared Function CreateDIBSection(ByVal hdc As IntPtr, ByRef bmi As BITMAPINFO, ByVal Usage As UInteger, ByRef bits As IntPtr, ByVal hSection As IntPtr, ByVal dwOffset As UInteger) As IntPtr
        End Function
    
        <System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)> _
        Private Structure BITMAPINFO
            Public biSize As UInteger
            Public biWidth As Integer, biHeight As Integer
            Public biPlanes As Short, biBitCount As Short
            Public biCompression As UInteger, biSizeImage As UInteger
            Public biXPelsPerMeter As Integer, biYPelsPerMeter As Integer
            Public biClrUsed As UInteger, biClrImportant As UInteger
            <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=256)> _
            Public cols As UInteger()
        End Structure
    
        Private Shared Function MAKERGB(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer) As UInteger
            Return CUInt((b And 255)) Or CUInt(((r And 255) << 8)) Or CUInt(((g And 255) << 16))
        End Function
        Private Sub New()
    
        End Sub
    
    End Class
    

    使用它:

        'Load your image
        Using B As New Bitmap("c:\test.tiff")
            'Do a bunch of stuff to it
            '...'
    
            'Convert it to 4BPP
            Using I = BitmapEncoder.ConvertBitmapToSpecified(B, 4)
                'Save to disk
                I.Save("c:\test2.tiff")
            End Using
        End Using
    

    【讨论】:

    • 需要注意的是,这里输出的是 PNG 格式,而不是 TIF。不管怎样,这正是我所需要的。
    猜你喜欢
    • 2023-03-23
    • 2021-07-11
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多