摸索了3天,终于成功实现了使用已知byte[]数据,生成Bitmap(256色灰度 BMP位图)。
其中遇到了很多问题:
如何在C#(GDI+)中操作Bitmap,获取位图的各种数据,就像在C++|中一样
如何修改Bitmap的调色板
如何解决位图显示错位的问题,即位图的4字节对齐问题
如何用数据生成位图
总结一下,希望对需要的朋友有所帮助,少走弯路。
///
<summary>
/// 使用byte[]数据,生成256色灰度 BMP 位图
/// </summary>
/// <param name="originalImageData"></param>
/// <param name="originalWidth"></param>
/// <param name="originalHeight"></param>
/// <returns></returns>
public static Bitmap CreateBitmap( byte [] originalImageData, int originalWidth, int originalHeight)
{
// 指定8位格式,即256色
Bitmap resultBitmap = new Bitmap(originalWidth, originalHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
// 将该位图存入内存中
MemoryStream curImageStream = new MemoryStream();
resultBitmap.Save(curImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
curImageStream.Flush();
// 由于位图数据需要DWORD对齐(4byte倍数),计算需要补位的个数
int curPadNum = ((originalWidth * 8 + 31 ) / 32 * 4 ) - originalWidth;
// 最终生成的位图数据大小
int bitmapDataSize = ((originalWidth * 8 + 31 ) / 32 * 4 ) * originalHeight;
// 数据部分相对文件开始偏移,具体可以参考位图文件格式
int dataOffset = ReadData(curImageStream, 10 , 4 );
// 改变调色板,因为默认的调色板是32位彩色的,需要修改为256色的调色板
int paletteStart = 54 ;
int paletteEnd = dataOffset;
int color = 0 ;
for ( int i = paletteStart; i < paletteEnd; i += 4 )
{
byte [] tempColor = new byte [ 4 ];
tempColor[ 0 ] = ( byte )color;
tempColor[ 1 ] = ( byte )color;
tempColor[ 2 ] = ( byte )color;
tempColor[ 3 ] = ( byte ) 0 ;
color ++ ;
curImageStream.Position = i;
curImageStream.Write(tempColor, 0 , 4 );
}
// 最终生成的位图数据,以及大小,高度没有变,宽度需要调整
byte [] destImageData = new byte [bitmapDataSize];
int destWidth = originalWidth + curPadNum;
// 生成最终的位图数据,注意的是,位图数据 从左到右,从下到上,所以需要颠倒
for ( int originalRowIndex = originalHeight - 1 ; originalRowIndex >= 0 ; originalRowIndex -- )
{
int destRowIndex = originalHeight - originalRowIndex - 1 ;
for ( int dataIndex = 0 ; dataIndex < originalWidth; dataIndex ++ )
{
// 同时还要注意,新的位图数据的宽度已经变化destWidth,否则会产生错位
destImageData[destRowIndex * destWidth + dataIndex] = originalImageData[originalRowIndex * originalWidth + dataIndex];
}
}
// 将流的Position移到数据段
curImageStream.Position = dataOffset;
// 将新位图数据写入内存中
curImageStream.Write(destImageData, 0 , bitmapDataSize);
curImageStream.Flush();
// 将内存中的位图写入Bitmap对象
resultBitmap = new Bitmap(curImageStream);
return resultBitmap;
}
/// <summary>
/// 从内存流中指定位置,读取数据
/// </summary>
/// <param name="curStream"></param>
/// <param name="startPosition"></param>
/// <param name="length"></param>
/// <returns></returns>
public static int ReadData(MemoryStream curStream, int startPosition, int length)
{
int result = - 1 ;
byte [] tempData = new byte [length];
curStream.Position = startPosition;
curStream.Read(tempData, 0 , length);
result = BitConverter.ToInt32(tempData, 0 );
return result;
}
/// <summary>
/// 向内存流中指定位置,写入数据
/// </summary>
/// <param name="curStream"></param>
/// <param name="startPosition"></param>
/// <param name="length"></param>
/// <param name="value"></param>
public static void WriteData(MemoryStream curStream, int startPosition, int length, int value)
{
curStream.Position = startPosition;
curStream.Write(BitConverter.GetBytes(value), 0 , length);
}
/// 使用byte[]数据,生成256色灰度 BMP 位图
/// </summary>
/// <param name="originalImageData"></param>
/// <param name="originalWidth"></param>
/// <param name="originalHeight"></param>
/// <returns></returns>
public static Bitmap CreateBitmap( byte [] originalImageData, int originalWidth, int originalHeight)
{
// 指定8位格式,即256色
Bitmap resultBitmap = new Bitmap(originalWidth, originalHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
// 将该位图存入内存中
MemoryStream curImageStream = new MemoryStream();
resultBitmap.Save(curImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
curImageStream.Flush();
// 由于位图数据需要DWORD对齐(4byte倍数),计算需要补位的个数
int curPadNum = ((originalWidth * 8 + 31 ) / 32 * 4 ) - originalWidth;
// 最终生成的位图数据大小
int bitmapDataSize = ((originalWidth * 8 + 31 ) / 32 * 4 ) * originalHeight;
// 数据部分相对文件开始偏移,具体可以参考位图文件格式
int dataOffset = ReadData(curImageStream, 10 , 4 );
// 改变调色板,因为默认的调色板是32位彩色的,需要修改为256色的调色板
int paletteStart = 54 ;
int paletteEnd = dataOffset;
int color = 0 ;
for ( int i = paletteStart; i < paletteEnd; i += 4 )
{
byte [] tempColor = new byte [ 4 ];
tempColor[ 0 ] = ( byte )color;
tempColor[ 1 ] = ( byte )color;
tempColor[ 2 ] = ( byte )color;
tempColor[ 3 ] = ( byte ) 0 ;
color ++ ;
curImageStream.Position = i;
curImageStream.Write(tempColor, 0 , 4 );
}
// 最终生成的位图数据,以及大小,高度没有变,宽度需要调整
byte [] destImageData = new byte [bitmapDataSize];
int destWidth = originalWidth + curPadNum;
// 生成最终的位图数据,注意的是,位图数据 从左到右,从下到上,所以需要颠倒
for ( int originalRowIndex = originalHeight - 1 ; originalRowIndex >= 0 ; originalRowIndex -- )
{
int destRowIndex = originalHeight - originalRowIndex - 1 ;
for ( int dataIndex = 0 ; dataIndex < originalWidth; dataIndex ++ )
{
// 同时还要注意,新的位图数据的宽度已经变化destWidth,否则会产生错位
destImageData[destRowIndex * destWidth + dataIndex] = originalImageData[originalRowIndex * originalWidth + dataIndex];
}
}
// 将流的Position移到数据段
curImageStream.Position = dataOffset;
// 将新位图数据写入内存中
curImageStream.Write(destImageData, 0 , bitmapDataSize);
curImageStream.Flush();
// 将内存中的位图写入Bitmap对象
resultBitmap = new Bitmap(curImageStream);
return resultBitmap;
}
/// <summary>
/// 从内存流中指定位置,读取数据
/// </summary>
/// <param name="curStream"></param>
/// <param name="startPosition"></param>
/// <param name="length"></param>
/// <returns></returns>
public static int ReadData(MemoryStream curStream, int startPosition, int length)
{
int result = - 1 ;
byte [] tempData = new byte [length];
curStream.Position = startPosition;
curStream.Read(tempData, 0 , length);
result = BitConverter.ToInt32(tempData, 0 );
return result;
}
/// <summary>
/// 向内存流中指定位置,写入数据
/// </summary>
/// <param name="curStream"></param>
/// <param name="startPosition"></param>
/// <param name="length"></param>
/// <param name="value"></param>
public static void WriteData(MemoryStream curStream, int startPosition, int length, int value)
{
curStream.Position = startPosition;
curStream.Write(BitConverter.GetBytes(value), 0 , length);
}