none
關於SetPixel太慢的問題 RRS feed

  • 問題

  • 大家好,如題目所說,我爬文之後

     

    http://msdn2.microsoft.com/zh-tw/library/5ey6h79d.aspx

     

    去看過裡面的程式範例,我先造著打出來,

     

    正常結果應該是整張圖片成紅色吧?可是執行結果不如預期?

     

    可以請教是哪裡錯誤嗎?

     

    程式碼區塊

            ' Create a new bitmap.
            Dim bmp As New Bitmap(PictureBox1.Width, PictureBox1.Height)

     

            ' Lock the bitmap's bits. 
            Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
            Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, _
                Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)

     

            ' Get the address of the first line.
            Dim ptr As IntPtr = bmpData.Scan0

     

            ' Declare an array to hold the bytes of the bitmap.
            ' This code is specific to a bitmap with 24 bits per pixels.
            Dim bytes As Integer = bmp.Width * bmp.Height * 3
            Dim rgbValues(bytes - 1) As Byte

     

            ' Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)

     

            ' Set every red value to 255. 
            For counter As Integer = 2 To rgbValues.Length - 1 Step 3
                rgbValues(counter) = 255
            Next

     

            ' Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes)

     

            ' Unlock the bits.
            bmp.UnlockBits(bmpData)

     

            ' Draw the modified image.
            'e.Graphics.DrawImage(bmp, 0, 150)
            PictureBox1.Image = bmp

     

     


    2007年11月24日 上午 08:23

解答

  • 你不能直接引用該篇的程式碼原因在於:

    那個範例是從 Jpeg 圖檔產生 Bitmap 區塊,Jpeg 圖檔格式為 24 bits ,但是你是直接創建一個新的 Bitmap 區塊,為 32 bits 格式。

     

    直接建立新的 Bitmap 時(如上建立程式碼),在未指定 PixelFormat 的情況下,預設為 Format32bppArgb ,亦即顏色的 16 進位值為 &HAARRGGBB ,Windows 的位元組為 little endian byte order 故每 4 bytes 為 Byte(0) = BB, Byte(1) = GG, Byte(2) = RR, Byte(3) = AA ,AA = 0 表示 100% 透明,所以什麼都看不見,AA = 255 表示完全非透明,就是一般的顏色。

    故範例程式碼修改要考慮是 4 bytes 。

    程式碼區塊
    ' 宣告應改為 4 Bytes
    Dim bytes As Integer = bmp.Width * bmp.Height * 4

     

     

    程式碼區塊
    For counter As Integer = 3 To rgbValues.Length - 1 Step 4 ' 迴圈應改為 4 Bytes
    rgbValues(counter - 1) = 255 ' 設定紅色
    rgbValues(counter) = 255 ' 設定透明度為 0
    Next

     

     

    另一種改法是直接處理 4 Bytes 的整數,修改的幅度比較大,但是效能會比分別設定兩個 Bytes 會更快:

    程式碼區塊
    Dim pixels As Integer = bmp.Width * bmp.Height ' 計算點數
    Dim rgbValues(pixels - 1) As Integer
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, pixels)
    For counter As Integer = 0 To rgbValues.Length - 1
    rgbValues(counter) = -65536 ' &HFFFF0000,直接設定透明度為 0 及紅色的常數值
    Next
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, pixels)

     


     

    補充說明已註解在該篇上。
    2007年11月24日 上午 09:56
    版主

所有回覆

  • 你若沒指定 PixelColor 時,預設是 32 bits 的顏色,也就是 AARRGGBB,aa 為透明度,aa = FF 時為不透明。

     

    你可以改用 Color 類別產生,例如:

    程式碼區塊

      Dim redColor As Color = Color.FromArgb(255, 255, 0, 0)
      Dim redInteger As Integer = redColor.ToArgb()

     

     

    你要直接指定數值時,

    程式碼區塊

    redInteger = &HFFFF0000

     

     

    FF 代表不透明 (255)

    FF 代表全紅 (255)

     

    亦即你用的 255 = &H000000FF ,亦即為透明藍,100% 的透明度等於看不見...

    2007年11月24日 上午 09:12
    版主
  • 你不能直接引用該篇的程式碼原因在於:

    那個範例是從 Jpeg 圖檔產生 Bitmap 區塊,Jpeg 圖檔格式為 24 bits ,但是你是直接創建一個新的 Bitmap 區塊,為 32 bits 格式。

     

    直接建立新的 Bitmap 時(如上建立程式碼),在未指定 PixelFormat 的情況下,預設為 Format32bppArgb ,亦即顏色的 16 進位值為 &HAARRGGBB ,Windows 的位元組為 little endian byte order 故每 4 bytes 為 Byte(0) = BB, Byte(1) = GG, Byte(2) = RR, Byte(3) = AA ,AA = 0 表示 100% 透明,所以什麼都看不見,AA = 255 表示完全非透明,就是一般的顏色。

    故範例程式碼修改要考慮是 4 bytes 。

    程式碼區塊
    ' 宣告應改為 4 Bytes
    Dim bytes As Integer = bmp.Width * bmp.Height * 4

     

     

    程式碼區塊
    For counter As Integer = 3 To rgbValues.Length - 1 Step 4 ' 迴圈應改為 4 Bytes
    rgbValues(counter - 1) = 255 ' 設定紅色
    rgbValues(counter) = 255 ' 設定透明度為 0
    Next

     

     

    另一種改法是直接處理 4 Bytes 的整數,修改的幅度比較大,但是效能會比分別設定兩個 Bytes 會更快:

    程式碼區塊
    Dim pixels As Integer = bmp.Width * bmp.Height ' 計算點數
    Dim rgbValues(pixels - 1) As Integer
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, pixels)
    For counter As Integer = 0 To rgbValues.Length - 1
    rgbValues(counter) = -65536 ' &HFFFF0000,直接設定透明度為 0 及紅色的常數值
    Next
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, pixels)

     


     

    補充說明已註解在該篇上。
    2007年11月24日 上午 09:56
    版主
  • 感謝璉璉大大的詳細解說!

    我完全明白了解了!謝謝!

    2007年11月24日 下午 05:55
  • 我也被類似問題困擾了幾年,經由此問答間找到了解決方案,並將此技巧寫成一個VB模組。
    願與大家分享:
    http://ycc.dwu.edu.tw/ImgProc/FastPixelModule.htm
    2009年4月12日 上午 12:23