2 // System.Drawing.Imaging.BMPCodec.cs
5 // Alexandre Pigolkine (pigolkine@gmx.de)
6 // Jordi Mas i Hernàndez (jmas@softcatala.org>, 2004
7 // BITMAPINFOHEADER,Decode functions implemented using code/ideas from
8 // CxImage (c) 07/Aug/2001 <ing.davide.pizzolato@libero.it>
10 // (C) 2002/2003 Ximian, Inc.
12 namespace System.Drawing.Imaging {
16 using System.Drawing.Imaging;
17 using System.Runtime.InteropServices;
20 internal struct BITMAPFILEHEADER { // File info header
21 public ushort bfType; // Specifies the type of file. This member must be BM.
22 public uint bfSize; // Specifies the size of the file, in bytes.
23 public ushort bfReserved1; // Reserved; must be set to zero.
24 public ushort bfReserved2; // Reserved; must be set to zero.
25 public uint bfOffBits; // Specifies the byte offset from the BITMAPFILEHEADER
26 // structure to the actual bitmap data in the file.
29 internal enum BitmapFileType : ushort {
30 BFT_ICON = 0x4349, /* 'IC' */
31 BFT_BITMAP = 0x4d42, /* 'BM' */
32 BFT_CURSOR = 0x5450 /* 'PT' */
35 internal enum BitmapCompression : uint {
43 [StructLayout(LayoutKind.Sequential)]
44 internal struct BITMAPINFOHEADER_FLAT {
47 internal int biHeight;
48 internal short biPlanes;
49 internal short biBitCount;
50 internal int biCompression;
51 internal int biSizeImage;
52 internal int biXPelsPerMeter;
53 internal int biYPelsPerMeter;
54 internal int biClrUsed;
55 internal int biClrImportant;
56 [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=1024)]
57 internal byte[] bmiColors;
59 static int WIDTHBYTES(int i) {
60 return ((i+31)&(~31))/8; /* ULONG aligned ! */
63 public int DibWidthBytesN(int n) {
64 return WIDTHBYTES(biWidth * n);
67 public int DibWidthBytes() {
68 return DibWidthBytesN(biBitCount);
71 public int DibSizeImage() {
72 return biSizeImage == 0 ? DibWidthBytes() * biHeight : biSizeImage;
75 public int DibNumColors() {
76 return biClrUsed == 0 && biBitCount <= 8 ? (1 << biBitCount) : biClrUsed;
79 public void FixBitmapInfo(){
81 biSizeImage = DibSizeImage();
83 biClrUsed = DibNumColors();
86 float HorizontalResolution {
88 return (float)biXPelsPerMeter * 254.0F / 10000.0F;
92 float VerticalResolution {
94 return (float)biYPelsPerMeter * 254.0F / 10000.0F;
98 public void Initialize (BitmapData info)
101 biWidth = info.Width;
102 biHeight = info.Height;
104 biBitCount = (short)System.Drawing.Image.GetPixelFormatSize (info.PixelFormat);
105 biCompression = (int)BitmapCompression.BI_RGB;
106 biSizeImage = (int) info.Height * info.Width * Image.GetPixelFormatSize (info.PixelFormat) / 8;
115 internal class BMPCodec {
117 static int BITMAPINFOHEADER_SIZE = 40;
119 internal BMPCodec() {
122 internal static ImageCodecInfo CodecInfo {
124 ImageCodecInfo info = new ImageCodecInfo ();
125 info.Flags = ImageCodecFlags.Encoder | ImageCodecFlags.Decoder |
126 ImageCodecFlags.Builtin | ImageCodecFlags.SupportBitmap;
128 info.FormatDescription = "BITMAP file format";
129 info.FormatID = System.Drawing.Imaging.ImageFormat.Bmp.Guid;
130 info.MimeType = "image/bmp";
132 byte[][] signaturePatterns = new byte[1][];
133 signaturePatterns[0] = new byte[2];
134 signaturePatterns[0][0] = 0x42;
135 signaturePatterns[0][1] = 0x4d;
136 info.SignaturePatterns = signaturePatterns;
137 byte[][] signatureMasks = new byte[1][];
138 signatureMasks[0] = new byte[2];
139 signatureMasks[0][0] = 0xff;
140 signatureMasks[0][1] = 0xff;
141 info.SignatureMasks = signatureMasks;
142 info.decode += new ImageCodecInfo.DecodeFromStream(BMPCodec.DecodeDelegate);
143 info.encode += new ImageCodecInfo.EncodeToStream(BMPCodec.EncodeDelegate);
148 bool ReadFileHeader (Stream stream, out BITMAPFILEHEADER bmfh) {
149 bmfh = new BITMAPFILEHEADER();
150 BinaryReader bs = new BinaryReader(stream);
152 bmfh.bfType = bs.ReadUInt16();
153 if(bmfh.bfType != (ushort)BitmapFileType.BFT_BITMAP) return false;
154 bmfh.bfSize = bs.ReadUInt32();
155 bmfh.bfReserved1 = bs.ReadUInt16();
156 bmfh.bfReserved2 = bs.ReadUInt16();
157 bmfh.bfOffBits = bs.ReadUInt32();
161 bool ReadInfoHeader (Stream stream, out BITMAPINFOHEADER_FLAT bmih) {
162 bmih = new BITMAPINFOHEADER_FLAT();
164 BinaryReader bs = new BinaryReader(stream);
165 bmih.biSize = bs.ReadInt32();
166 bmih.biWidth = bs.ReadInt32();
167 bmih.biHeight = bs.ReadInt32();
168 bmih.biPlanes = bs.ReadInt16();
169 bmih.biBitCount = bs.ReadInt16();
170 bmih.biCompression = bs.ReadInt32();
171 bmih.biSizeImage = bs.ReadInt32();
172 bmih.biXPelsPerMeter = bs.ReadInt32();
173 bmih.biYPelsPerMeter = bs.ReadInt32();
174 bmih.biClrUsed = bs.ReadInt32();
175 bmih.biClrImportant = bs.ReadInt32();
177 // Currently only BITMAPINFOHEADER
178 if (bmih.biSize != BITMAPINFOHEADER_SIZE) return false;
180 bmih.FixBitmapInfo();
182 int numColors = bmih.DibNumColors();
184 for (int i = 0; i < numColors; i++) {
185 bmih.bmiColors[index++] = (byte)stream.ReadByte();
186 bmih.bmiColors[index++] = (byte)stream.ReadByte();
187 bmih.bmiColors[index++] = (byte)stream.ReadByte();
188 bmih.bmiColors[index++] = (byte)stream.ReadByte();
191 catch (Exception e) {
197 internal static void DecodeDelegate (Image image, Stream stream, BitmapData info)
199 BMPCodec bmp = new BMPCodec();
200 bmp.Decode (image, stream, info);
203 internal bool Decode (Image image, Stream stream, BitmapData info)
205 if (stream.Length < 14 + BITMAPINFOHEADER_SIZE/* sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)*/)
207 long startPosition = stream.Position;
209 BITMAPFILEHEADER bmfh;
210 BITMAPINFOHEADER_FLAT bmih;
212 if (!ReadFileHeader (stream, out bmfh))
214 if (!ReadInfoHeader (stream, out bmih))
217 Color[] colorEntries = new Color[bmih.DibNumColors()];
219 for (int colorEntryIdx = 0; colorEntryIdx < colorEntries.Length; colorEntryIdx++) {
220 // FIXME: is alpha can be used here
221 colorEntries[colorEntryIdx] = Color.FromArgb(bmih.bmiColors[index+3], bmih.bmiColors[index+2], bmih.bmiColors[index+1], bmih.bmiColors[index]);
225 image.Palette = new ColorPalette(1, colorEntries);
226 image.SetRawFormat (System.Drawing.Imaging.ImageFormat.Bmp);
227 info.Width = bmih.biWidth;
228 info.Height = bmih.biHeight;
229 info.Stride = (int)bmih.DibWidthBytes();
231 switch (bmih.biBitCount) {
233 Console.WriteLine ("BmpCodec: 24 bits bitmap", bmih.biSizeImage);
234 info.PixelFormat = PixelFormat.Format24bppRgb;
235 if (bmfh.bfOffBits != 0L)
236 stream.Seek (startPosition + bmfh.bfOffBits,SeekOrigin.Begin);
238 if (bmih.biCompression == (uint)BitmapCompression.BI_RGB) {
240 IntPtr lfBuffer = Marshal.AllocHGlobal(bmih.biSizeImage);
241 byte[] bt = new byte[info.Stride];
242 int offset = (info.Height-1) * info.Stride;
243 int baseadr = lfBuffer.ToInt32();
245 // DIB are stored upside down. That means that the uppest row which
246 // appears on the screen actually is the lowest row stored in the bitmap
248 stream.Read(bt, 0, info.Stride);
249 Marshal.Copy (bt, 0, (IntPtr)( baseadr + offset), info.Stride);
250 offset -= info.Stride;
253 Console.WriteLine ("BmpCodec: 24 bits bitmap", bmih.biSizeImage);
254 info.Scan0 = lfBuffer;
261 Console.WriteLine ("BmpCodec: The {0} compression is not supported", bmih.biCompression);
265 info.PixelFormat = PixelFormat.Format32bppArgb;
266 Console.WriteLine ("BmpCodec: 32 bits bitmap", bmih.biSizeImage);
267 if (bmfh.bfOffBits != 0L)
268 stream.Seek (startPosition + bmfh.bfOffBits,SeekOrigin.Begin);
269 if (bmih.biCompression == (uint)BitmapCompression.BI_RGB) {
271 IntPtr lfBuffer = Marshal.AllocHGlobal(bmih.biSizeImage);
272 byte[] bt = new byte[info.Stride];
273 int offset = (info.Height-1) * info.Stride;
274 int baseadr = lfBuffer.ToInt32();
276 // DIB are stored upside down. That means that the uppest row which
277 // appears on the screen actually is the lowest row stored in the bitmap
279 stream.Read(bt, 0, info.Stride);
280 Marshal.Copy (bt, 0, (IntPtr)( baseadr + offset), info.Stride);
281 offset -= info.Stride;
284 info.Scan0 = lfBuffer;
291 Console.WriteLine ("BmpCodec: The {0} compression is not supported", bmih.biCompression);
295 throw new NotImplementedException(String.Format("This format is not yet supported : {0} bpp", bmih.biBitCount));
301 internal static void EncodeDelegate (Image image, Stream stream)
303 BMPCodec bmp = new BMPCodec();
304 BitmapData info = ((Bitmap)image).LockBits (new Rectangle (new Point (0,0), image.Size),
305 ImageLockMode.ReadOnly, image.PixelFormat);
306 bmp.Encode (image, stream, info);
307 ((Bitmap)image).UnlockBits (info);
310 internal bool Encode (Image image, Stream stream, BitmapData info)
312 BITMAPFILEHEADER bmfh = new BITMAPFILEHEADER();
313 bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
314 bmfh.bfType = (ushort)BitmapFileType.BFT_BITMAP;
315 bmfh.bfOffBits = (uint)(14 + BITMAPINFOHEADER_SIZE + image.Palette.Entries.Length * 4);
316 int line_size = info.Stride;
317 bmfh.bfSize = (uint)(bmfh.bfOffBits + info.Height * line_size);
319 BinaryWriter bw = new BinaryWriter(stream);
320 bw.Write(bmfh.bfType);
321 bw.Write(bmfh.bfSize);
322 bw.Write(bmfh.bfReserved1);
323 bw.Write(bmfh.bfReserved2);
324 bw.Write(bmfh.bfOffBits);
326 BITMAPINFOHEADER_FLAT bmih = new BITMAPINFOHEADER_FLAT();
327 bmih.Initialize(info);
328 bw.Write(bmih.biSize);
329 bw.Write(bmih.biWidth);
330 bw.Write(bmih.biHeight);
331 bw.Write(bmih.biPlanes);
332 bw.Write(bmih.biBitCount);
333 bw.Write(bmih.biCompression);
334 bw.Write(bmih.biSizeImage);
335 bw.Write(bmih.biXPelsPerMeter);
336 bw.Write(bmih.biYPelsPerMeter);
337 bw.Write(bmih.biClrUsed);
338 bw.Write(bmih.biClrImportant);
339 Console.WriteLine ("FIXME: BmpCodec: Write palette here");
341 Console.WriteLine ("biWidth ->" + bmih.biWidth);
342 Console.WriteLine ("Height->" + bmih.biHeight);
343 Console.WriteLine ("LineSize ->" + line_size);
344 Console.WriteLine ("Address ->" + info.Scan0.ToInt32());
345 Console.WriteLine ("Stride ->" + info.Stride);
346 Console.WriteLine ("Planes ->" + bmih.biPlanes);
348 byte [] line_buffer = new byte [line_size];
349 int stride = info.Stride;
350 int offset = (info.Height-1) * stride;
351 int baseadr = info.Scan0.ToInt32();
353 Console.WriteLine ("Offset ->" + offset);
355 // DIB are stored upside down. That means that the uppest row which
356 // appears on the screen actually is the lowest row stored in the
359 //FIXME: not an optimal way to specify starting address
360 //FIXME: Bitmaps are stored in DWORD alignments
361 Marshal.Copy ((IntPtr)( baseadr + offset), line_buffer, 0, line_size);
362 stream.Write(line_buffer, 0, line_size);