warning removed
[mono.git] / mcs / class / System.Drawing / System.Drawing.Imaging / BmpCodec.cs
1 //
2 // System.Drawing.Imaging.BMPCodec.cs
3 //
4 // Author: 
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>
8 //
9 // (C) 2002/2003 Ximian, Inc.
10
11 namespace System.Drawing.Imaging {
12
13         using System;
14         using System.IO;
15         using System.Drawing.Imaging;
16         using System.Runtime.InteropServices;
17
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.
25         }
26
27         internal enum BitmapFileType : ushort {
28                 BFT_ICON  = 0x4349,   /* 'IC' */
29                 BFT_BITMAP = 0x4d42,   /* 'BM' */
30                 BFT_CURSOR = 0x5450   /* 'PT' */
31         }
32
33         internal enum BitmapCompression : uint {
34                 BI_RGB        = 0,
35                 BI_RLE8       = 1,
36                 BI_RLE4       = 2,
37                 BI_BITFIELDS  = 3
38         }
39
40         [StructLayout(LayoutKind.Sequential)]
41         internal struct BITMAPINFOHEADER_FLAT {
42                 internal int      biSize;
43                 internal int      biWidth;
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; 
55
56                 static int WIDTHBYTES(int i) {
57                         return ((i+31)&(~31))/8;  /* ULONG aligned ! */
58                 }
59                         
60                 public int DibWidthBytesN(int n) {
61                         return WIDTHBYTES(biWidth * n);
62                 }
63                         
64                 public int DibWidthBytes() {
65                         return DibWidthBytesN(biBitCount);
66                 }
67                 
68                 public int DibSizeImage() {
69                         return biSizeImage == 0 ? DibWidthBytes() * biHeight : biSizeImage;
70                 }
71                 
72                 public int DibNumColors() {
73                         return biClrUsed == 0 && biBitCount <= 8 ? (1 << biBitCount) : biClrUsed;
74                 }
75
76                 public void FixBitmapInfo(){
77                         if (biSizeImage == 0) 
78                                 biSizeImage = DibSizeImage();
79                         if (biClrUsed == 0)
80                                 biClrUsed = DibNumColors();
81                 }
82             
83                 float HorizontalResolution {
84                         get {
85                                 return (float)biXPelsPerMeter * 254.0F / 10000.0F;
86                         }
87                 }
88                 
89                 float VerticalResolution {
90                         get {
91                                 return (float)biYPelsPerMeter * 254.0F / 10000.0F;
92                         }
93                 }
94
95                 public void Initialize( InternalImageInfo info) {
96                         biSize = 40;
97                         biWidth = info.Size.Width;
98                         biHeight = info.Size.Height;
99                         biPlanes = 1;
100                         biBitCount = (short)System.Drawing.Image.GetPixelFormatSize(info.PixelFormat);
101                         biCompression = (int)BitmapCompression.BI_RGB;
102                         biSizeImage = (int)info.RawImageBytes.Length;
103                         biXPelsPerMeter = 0;
104                         biYPelsPerMeter = 0;
105                         biClrUsed = 0;
106                         biClrImportant = 0;
107                 }
108         }
109
110         internal class BMPCodec {
111                 
112                 internal BMPCodec() {
113                 }
114                 
115                 internal static ImageCodecInfo CodecInfo {
116                         get {
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";
122                                 info.Version = 1;
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);
135                                 return info;
136                         }
137                 }
138
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();
148                         return true;
149                 }
150                 
151                 bool ReadInfoHeader( Stream stream, out BITMAPINFOHEADER_FLAT bmih) {
152                         bmih = new BITMAPINFOHEADER_FLAT();
153                         try {
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();
166                                 
167                                 // Currently only BITMAPINFOHEADER
168                                 if( bmih.biSize != 40) return false;
169                                 
170                                 bmih.FixBitmapInfo();
171
172                                 int numColors = bmih.DibNumColors();
173                                 int index = 0;
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();
179                                 }
180                         }
181                         catch( Exception e) {
182                                 return false;
183                         }
184                         return true;
185                 }
186                 
187                 internal static void DecodeDelegate (Stream stream, InternalImageInfo info) {
188                         BMPCodec bmp = new BMPCodec();
189                         bmp.Decode (stream, info);
190                 }
191                 
192                 internal bool Decode( Stream stream, InternalImageInfo info) {
193                         if( stream.Length < 14 + 40/* sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)*/)
194                                 return false;
195                         long startPosition = stream.Position;
196                         
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()];
202                         int index = 0;
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]);
206                                 index += 4;
207                                 colorEntryIdx++;
208                         }
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;
213
214                         switch (bmih.biBitCount) {
215                                 case 24:
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);
221                                 }
222                                 else {
223                                 }
224                                 break;
225                                 case 32:
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);
231                                 }
232                                 else {
233                                 }
234                                 break;
235                                 default:
236                                         throw new NotImplementedException(String.Format("This format is not yet supported : {0} bpp", bmih.biBitCount));
237                         }
238                         return true;
239                 }
240
241                 internal static void EncodeDelegate (Stream stream, InternalImageInfo info) {
242                         BMPCodec bmp = new BMPCodec();
243                         bmp.Encode (stream, info);
244                 }
245                 
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);
258
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);
274                         return true;
275                 }
276         }
277 }