首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 位圖指標運算陷阱:Stride 與像素存取的正確姿勢

     編輯:關於C語言

問題背景

在將 C++ 影像處理演算法移植至 C# 時,發現使用 uint* 指標存取 32 位 ARGB 位圖的邏輯並不完全相容。C++ 中 UINT* 每次遞增 (++) 正好移動一個像素 (4 字節);但 C# 若直接套用此邏輯,當 Stride != Width * 4 (即鎖定部分區域或記憶體對齊導致每行有填充位元組) 時,會發生存取越界或資料錯誤。

核心差異分析

  • C++:UINT* 指標運算以型別大小 (4 bytes) 為單位,ptr++ 等同 ptr + 1,自動跨越一個像素。
  • C# unsafe:uint* 行為相同,但 位圖資料在記憶體中是連續的位元組流,行寬由 BitmapData.Stride 決定。若 Stride > Width * 4,每行末尾有填充位元組,單純用 ++ 會誤讀填充區,導致下一行起始位置偏移。

正確的像素定址公式

無論語言,安全做法皆為 位元組指標 (byte*) + 手動計算位移:

// 假設 bitmapData 為 Bitmap.LockBits 取得的 BitmapData
byte* ptr = (byte*)bitmapData.Scan0.ToPointer();
int stride = bitmapData.Stride; // 可能為負值 (自下而上)

for (int y = 0; y < height; y++)
{
    byte* row = ptr + y * stride;
    for (int x = 0; x < width; x++)
    {
        // 取得第 x 個像素的 ARGB (32 bits = 4 bytes)
        uint pixel = *(uint*)(row + x * 4);
        // ... 處理 pixel ...
        // 寫回
        *(uint*)(row + x * 4) = newPixel;
    }
}

補充說明

  • 完整鎖定:LockBits 整張圖時,通常 Stride == Width * 4,此時 uint* 遞增 可能 僅能運氣好不出錯,但不可靠。
  • 部分鎖定:LockBits 指定矩形區域時,Stride 幾乎必定大於 Width * 4,直接用 uint* 遞增 必定出錯。
  • 效能考量:現代 JIT 編譯器對 byte* 迴圈優化極好,且避免難以除錯的記憶體錯誤,強烈建議統一使用 byte* 配合 Stride 計算。

結論

C# 位圖指標操作務必尊重 Stride,採用 byte* 逐位元組定址,才能在所有鎖定模式下正確、穩定地處理像素資料。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved