2 // System.Drawing.Imaging.BMPCodec.cs
5 // Alexandre Pigolkine (pigolkine@gmx.de)
6 // BITMAPINFOHEADER,Decode functions implemented using code/ideas from
7 // CxImage (c) 07/Aug/2001 <ing.davide.pizzolato@libero.it>
9 // (C) 2002/2003 Ximian, Inc.
11 namespace System.Drawing.Imaging {
15 using System.Drawing.Imaging;
16 using System.Runtime.InteropServices;
18 internal struct BITMAPFILEHEADER { // File info header
19 public ushort bfType; // Specifies the type of file. This member must be BM.
20 public uint bfSize; // Specifies the size of the file, in bytes.
21 public ushort bfReserved1; // Reserved; must be set to zero.
22 public ushort bfReserved2; // Reserved; must be set to zero.
23 public uint bfOffBits; // Specifies the byte offset from the BITMAPFILEHEADER
24 // structure to the actual bitmap data in the file.
27 internal enum BitmapFileType : ushort {
28 BFT_ICON = 0x4349, /* 'IC' */
29 BFT_BITMAP = 0x4d42, /* 'BM' */
30 BFT_CURSOR = 0x5450 /* 'PT' */
33 internal enum BitmapCompression : uint {
40 [StructLayout(LayoutKind.Sequential)]
41 internal struct BITMAPINFOHEADER_FLAT {
44 internal int biHeight;
45 internal short biPlanes;
46 internal short biBitCount;
47 internal int biCompression;
48 internal int biSizeImage;
49 internal int biXPelsPerMeter;
50 internal int biYPelsPerMeter;
51 internal int biClrUsed;
52 internal int biClrImportant;
53 [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=1024)]
54 internal byte[] bmiColors;
56 static int WIDTHBYTES(int i) {
57 return ((i+31)&(~31))/8; /* ULONG aligned ! */
60 public int DibWidthBytesN(int n) {
61 return WIDTHBYTES(biWidth * n);
64 public int DibWidthBytes() {
65 return DibWidthBytesN(biBitCount);
68 public int DibSizeImage() {
69 return biSizeImage == 0 ? DibWidthBytes() * biHeight : biSizeImage;
72 public int DibNumColors() {
73 return biClrUsed == 0 && biBitCount <= 8 ? (1 << biBitCount) : biClrUsed;
76 public void FixBitmapInfo(){
78 biSizeImage = DibSizeImage();
80 biClrUsed = DibNumColors();
83 float HorizontalResolution {
85 return (float)biXPelsPerMeter * 254.0F / 10000.0F;
89 float VerticalResolution {
91 return (float)biYPelsPerMeter * 254.0F / 10000.0F;
95 public void Initialize( InternalImageInfo info) {
97 biWidth = info.Size.Width;
98 biHeight = info.Size.Height;
100 biBitCount = (short)System.Drawing.Image.GetPixelFormatSize(info.PixelFormat);
101 biCompression = (int)BitmapCompression.BI_RGB;
102 biSizeImage = (int)info.RawImageBytes.Length;
110 internal class BMPCodec {
112 internal BMPCodec() {
115 internal static ImageCodecInfo CodecInfo {
117 ImageCodecInfo info = new ImageCodecInfo();
118 info.Flags = ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.Builtin | ImageCodecFlags.SupportBitmap;
119 info.FormatDescription = "BITMAP file format";
120 info.FormatID = System.Drawing.Imaging.ImageFormat.Bmp.Guid;
121 info.MimeType = "image/bmp";
123 byte[][] signaturePatterns = new byte[1][];
124 signaturePatterns[0] = new byte[2];
125 signaturePatterns[0][0] = 0x42;
126 signaturePatterns[0][1] = 0x4d;
127 info.SignaturePatterns = signaturePatterns;
128 byte[][] signatureMasks = new byte[1][];
129 signatureMasks[0] = new byte[2];
130 signatureMasks[0][0] = 0xff;
131 signatureMasks[0][1] = 0xff;
132 info.SignatureMasks = signatureMasks;
133 info.decode += new ImageCodecInfo.DecodeFromStream(BMPCodec.DecodeDelegate);
134 info.encode += new ImageCodecInfo.EncodeToStream(BMPCodec.EncodeDelegate);
139 bool ReadFileHeader( Stream stream, out BITMAPFILEHEADER bmfh) {
140 bmfh = new BITMAPFILEHEADER();
141 BinaryReader bs = new BinaryReader(stream);
142 bmfh.bfType = bs.ReadUInt16();
143 if(bmfh.bfType != (ushort)BitmapFileType.BFT_BITMAP) return false;
144 bmfh.bfSize = bs.ReadUInt32();
145 bmfh.bfReserved1 = bs.ReadUInt16();
146 bmfh.bfReserved2 = bs.ReadUInt16();
147 bmfh.bfOffBits = bs.ReadUInt32();
151 bool ReadInfoHeader( Stream stream, out BITMAPINFOHEADER_FLAT bmih) {
152 bmih = new BITMAPINFOHEADER_FLAT();
154 BinaryReader bs = new BinaryReader(stream);
155 bmih.biSize = bs.ReadInt32();
156 bmih.biWidth = bs.ReadInt32();
157 bmih.biHeight = bs.ReadInt32();
158 bmih.biPlanes = bs.ReadInt16();
159 bmih.biBitCount = bs.ReadInt16();
160 bmih.biCompression = bs.ReadInt32();
161 bmih.biSizeImage = bs.ReadInt32();
162 bmih.biXPelsPerMeter = bs.ReadInt32();
163 bmih.biYPelsPerMeter = bs.ReadInt32();
164 bmih.biClrUsed = bs.ReadInt32();
165 bmih.biClrImportant = bs.ReadInt32();
167 // Currently only BITMAPINFOHEADER
168 if( bmih.biSize != 40) return false;
170 bmih.FixBitmapInfo();
172 int numColors = bmih.DibNumColors();
174 for (int i = 0; i < numColors; i++) {
175 bmih.bmiColors[index++] = (byte)stream.ReadByte();
176 bmih.bmiColors[index++] = (byte)stream.ReadByte();
177 bmih.bmiColors[index++] = (byte)stream.ReadByte();
178 bmih.bmiColors[index++] = (byte)stream.ReadByte();
181 catch( Exception e) {
187 internal static void DecodeDelegate (Stream stream, InternalImageInfo info) {
188 BMPCodec bmp = new BMPCodec();
189 bmp.Decode (stream, info);
192 internal bool Decode( Stream stream, InternalImageInfo info) {
193 if( stream.Length < 14 + 40/* sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)*/)
195 long startPosition = stream.Position;
197 BITMAPFILEHEADER bmfh;
198 BITMAPINFOHEADER_FLAT bmih;
199 if (!ReadFileHeader (stream, out bmfh)) return false;
200 if (!ReadInfoHeader (stream, out bmih)) return false;
201 Color[] colorEntries = new Color[bmih.DibNumColors()];
203 for( int colorEntryIdx = 0; colorEntryIdx < colorEntries.Length; colorEntryIdx++) {
204 // FIXME: is alpha can be used here
205 colorEntries[colorEntryIdx] = Color.FromArgb(bmih.bmiColors[index+3], bmih.bmiColors[index+2], bmih.bmiColors[index+1], bmih.bmiColors[index]);
209 info.Palette = new ColorPalette(1, colorEntries);
210 info.Size = new Size(bmih.biWidth, bmih.biHeight);
211 info.Stride = (int)bmih.DibWidthBytes();
212 info.RawFormat = System.Drawing.Imaging.ImageFormat.Bmp;
214 switch (bmih.biBitCount) {
216 info.PixelFormat = PixelFormat.Format24bppRgb;
217 if (bmfh.bfOffBits != 0L) stream.Seek (startPosition + bmfh.bfOffBits,SeekOrigin.Begin);
218 if (bmih.biCompression == (uint)BitmapCompression.BI_RGB) {
219 info.RawImageBytes = new byte[bmih.biSizeImage];
220 stream.Read(info.RawImageBytes, 0, (int)bmih.biSizeImage);
226 info.PixelFormat = PixelFormat.Format32bppArgb;
227 if (bmfh.bfOffBits != 0L) stream.Seek (startPosition + bmfh.bfOffBits,SeekOrigin.Begin);
228 if (bmih.biCompression == (uint)BitmapCompression.BI_RGB) {
229 info.RawImageBytes = new byte[bmih.biSizeImage];
230 stream.Read(info.RawImageBytes, 0, (int)bmih.biSizeImage);
236 throw new NotImplementedException(String.Format("This format is not yet supported : {0} bpp", bmih.biBitCount));
241 internal static void EncodeDelegate (Stream stream, InternalImageInfo info) {
242 BMPCodec bmp = new BMPCodec();
243 bmp.Encode (stream, info);
246 internal bool Encode( Stream stream, InternalImageInfo info) {
247 BITMAPFILEHEADER bmfh = new BITMAPFILEHEADER();
248 bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
249 bmfh.bfType = (ushort)BitmapFileType.BFT_BITMAP;
250 bmfh.bfOffBits = (uint)(14 + 40 + info.Palette.Entries.Length * 4);
251 bmfh.bfSize = (uint)(bmfh.bfOffBits + info.RawImageBytes.Length);
252 BinaryWriter bw = new BinaryWriter(stream);
253 bw.Write(bmfh.bfType);
254 bw.Write(bmfh.bfSize);
255 bw.Write(bmfh.bfReserved1);
256 bw.Write(bmfh.bfReserved2);
257 bw.Write(bmfh.bfOffBits);
259 BITMAPINFOHEADER_FLAT bmih = new BITMAPINFOHEADER_FLAT();
260 bmih.Initialize(info);
261 bw.Write(bmih.biSize);
262 bw.Write(bmih.biWidth);
263 bw.Write(bmih.biHeight);
264 bw.Write(bmih.biPlanes);
265 bw.Write(bmih.biBitCount);
266 bw.Write(bmih.biCompression);
267 bw.Write(bmih.biSizeImage);
268 bw.Write(bmih.biXPelsPerMeter);
269 bw.Write(bmih.biYPelsPerMeter);
270 bw.Write(bmih.biClrUsed);
271 bw.Write(bmih.biClrImportant);
272 // FIXME: write palette here
273 stream.Write(info.RawImageBytes, 0, info.RawImageBytes.Length);