位图转换的代码说明

时间:2016-07-01 13:57:10

标签: c#-4.0 bitmap

https://stackoverflow.com/a/2574798/159072

public static Bitmap BitmapTo1Bpp(Bitmap img) 
{
  int w = img.Width;
  int h = img.Height;
  //
  Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
  BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

为什么要加法和除法?

  byte[] scan = new byte[(w + 7) / 8];
  for (int y = 0; y < h; y++) 
  {
    for (int x = 0; x < w; x++) 
    {////Why this condition check?
      if (x % 8 == 0) 
      //Why divide by 8?
      scan[x / 8] = 0;
      Color c = img.GetPixel(x, y);
      //Why this condition check?
      if (c.GetBrightness() >= 0.5)
      { 
           // What is going on here?
           scan[x / 8] |= (byte)(0x80 >> (x % 8));
      }
    }
    // Why Martial.Copy() called here?
    Marshal.Copy(scan, 0, (IntPtr)((long)data.Scan0 + data.Stride * y), scan.Length);
  }
  bmp.UnlockBits(data);
  return bmp;
}

1 个答案:

答案 0 :(得分:1)

代码使用了一些基本的位黑客技术,这是必需的,因为它需要设置位,而在C#中可以寻址的最小存储元素是一个字节。我故意避免使用BitArray类。

   int w = img.Width;

我将位图的Width和Height属性复制到局部变量中以加速代码,属性太贵了。请注意,w是位图中的像素数,它表示最终图像中的数量。

   byte[] scan = new byte[(w + 7) / 8];

scan变量将像素存储在位图的一条扫描线中。 1bpp格式每像素使用1位,因此扫描行中的总字节数为w / 8.我添加7以确保值向上舍入,这是必要的,因为整数除法总是截断。 w = 1..7需要1个字节,w = 8..15需要2个字节,等等。

   if (x % 8 == 0) scan[x / 8] = 0;

x % 8表达式表示位号,x / 8表示字节数。此代码在进入扫描行中的下一个字节时将所有像素设置为黑色。另一种方法是在外部循环中重新分配byte []或使用for循环将其重置为0。

   if (c.GetBrightness() >= 0.5)

当源像素足够亮时,像素应设置为白色。否则它会留在黑色。使用Color.Brightness是一种避免处理人眼对亮度的非线性感知的简单方法(亮度〜= 0.299 *红色+ 0.587 *绿色+ 0.114 *蓝色)。

   scan[x / 8] |= (byte)(0x80 >> (x % 8));

在扫描行中将位设置为白色。如上所述,x % 8是位数,它将位数向右移位0x80,它们以此像素格式以相反的顺序存储。