Implemented EncoderParameter.cs and EncoderParameters.cs.
[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 //        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>
9 //
10 // (C) 2002/2003 Ximian, Inc.
11
12 namespace System.Drawing.Imaging {
13
14         using System;
15         using System.IO;
16         using System.Drawing.Imaging;
17         using System.Runtime.InteropServices;
18         
19
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.
27         }
28
29         internal enum BitmapFileType : ushort {
30                 BFT_ICON  = 0x4349,   /* 'IC' */
31                 BFT_BITMAP = 0x4d42,   /* 'BM' */
32                 BFT_CURSOR = 0x5450   /* 'PT' */
33         }
34
35         internal enum BitmapCompression : uint {
36                 BI_RGB        = 0,
37                 BI_RLE8       = 1,
38                 BI_RLE4       = 2,
39                 BI_BITFIELDS  = 3
40         }
41
42         
43         [StructLayout(LayoutKind.Sequential)]
44         internal struct BITMAPINFOHEADER_FLAT {
45                 internal int      biSize;
46                 internal int      biWidth;
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; 
58
59                 static int WIDTHBYTES(int i) {
60                         return ((i+31)&(~31))/8;  /* ULONG aligned ! */
61                 }
62                         
63                 public int DibWidthBytesN(int n) {
64                         return WIDTHBYTES(biWidth * n);
65                 }
66                         
67                 public int DibWidthBytes() {
68                         return DibWidthBytesN(biBitCount);
69                 }
70                 
71                 public int DibSizeImage() {
72                         return biSizeImage == 0 ? DibWidthBytes() * biHeight : biSizeImage;
73                 }
74                 
75                 public int DibNumColors() {
76                         return biClrUsed == 0 && biBitCount <= 8 ? (1 << biBitCount) : biClrUsed;
77                 }
78
79                 public void FixBitmapInfo(){
80                         if (biSizeImage == 0) 
81                                 biSizeImage = DibSizeImage();
82                         if (biClrUsed == 0)
83                                 biClrUsed = DibNumColors();
84                 }
85             
86                 float HorizontalResolution {
87                         get {
88                                 return (float)biXPelsPerMeter * 254.0F / 10000.0F;
89                         }
90                 }
91                 
92                 float VerticalResolution {
93                         get {
94                                 return (float)biYPelsPerMeter * 254.0F / 10000.0F;
95                         }
96                 }
97
98                 public void Initialize (BitmapData info)
99                 {
100                         biSize = 40;
101                         biWidth = info.Width;
102                         biHeight = info.Height;
103                         biPlanes = 1;
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;
107                         biXPelsPerMeter = 0;
108                         biYPelsPerMeter = 0;
109                         biClrUsed = 0;
110                         biClrImportant = 0;
111                         
112                 }
113         }
114
115         internal class BMPCodec {
116                 
117                 static  int BITMAPINFOHEADER_SIZE = 40;
118                         
119                 internal BMPCodec() {
120                 }
121                 
122                 internal static ImageCodecInfo CodecInfo {
123                         get {
124                                 ImageCodecInfo info = new ImageCodecInfo ();
125                                 info.Flags = ImageCodecFlags.Encoder | ImageCodecFlags.Decoder |
126                                         ImageCodecFlags.Builtin | ImageCodecFlags.SupportBitmap;
127
128                                 info.FormatDescription = "BITMAP file format";
129                                 info.FormatID = System.Drawing.Imaging.ImageFormat.Bmp.Guid;
130                                 info.MimeType = "image/bmp";
131                                 info.Version = 1;
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);
144                                 return info;
145                         }
146                 }
147
148                 bool ReadFileHeader (Stream stream, out BITMAPFILEHEADER bmfh) {
149                         bmfh = new BITMAPFILEHEADER();
150                         BinaryReader bs = new BinaryReader(stream);
151
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();
158                         return true;
159                 }
160                 
161                 bool ReadInfoHeader (Stream stream, out BITMAPINFOHEADER_FLAT bmih) {
162                         bmih = new BITMAPINFOHEADER_FLAT();
163                         try {
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();
176                                 
177                                 // Currently only BITMAPINFOHEADER
178                                 if (bmih.biSize != BITMAPINFOHEADER_SIZE) return false;
179                                 
180                                 bmih.FixBitmapInfo();
181
182                                 int numColors = bmih.DibNumColors();
183                                 int index = 0;
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();
189                                 }
190                         }
191                         catch (Exception e) {
192                                 return false;
193                         }
194                         return true;
195                 }
196                 
197                 internal static void DecodeDelegate (Image image, Stream stream, BitmapData info)
198                 {
199                         BMPCodec bmp = new BMPCodec();
200                         bmp.Decode (image, stream, info);
201                 }
202                 
203                 internal bool Decode (Image image, Stream stream, BitmapData info)
204                 {                                                       
205                         if (stream.Length < 14 + BITMAPINFOHEADER_SIZE/* sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)*/)
206                                 return false;
207                         long startPosition = stream.Position;
208                         
209                         BITMAPFILEHEADER        bmfh;
210                         BITMAPINFOHEADER_FLAT   bmih;
211                         
212                         if (!ReadFileHeader (stream, out bmfh))
213                                 return false;
214                         if (!ReadInfoHeader (stream, out bmih))
215                                 return false;
216                         
217                         Color[] colorEntries = new Color[bmih.DibNumColors()];
218                         int index = 0;
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]);
222                                 index += 4;
223                                 colorEntryIdx++;
224                         }
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();
230
231                         switch (bmih.biBitCount) {
232                         case 24:
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);
237
238                                 if (bmih.biCompression == (uint)BitmapCompression.BI_RGB) {
239                                         
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();
244                                                                                 
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                                   
247                                         while(offset>=0){                                                                       
248                                                 stream.Read(bt, 0, info.Stride); 
249                                                 Marshal.Copy (bt, 0, (IntPtr)( baseadr + offset), info.Stride);                                         
250                                                 offset -= info.Stride;                          
251                                         }                                                                               
252                                         
253                                         Console.WriteLine ("BmpCodec: 24 bits bitmap", bmih.biSizeImage);
254                                         info.Scan0 = lfBuffer;
255                                         info.Allocated=true;            
256                         
257                                 } else {
258                                         //
259                                         // FIXME
260                                         // 
261                                         Console.WriteLine ("BmpCodec: The {0} compression is not supported", bmih.biCompression);
262                                 }
263                                 break;
264                         case 32:
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) {
270                                         
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();
275                                                                                 
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                                   
278                                         while(offset>=0){                                                                       
279                                                 stream.Read(bt, 0, info.Stride); 
280                                                 Marshal.Copy (bt, 0, (IntPtr)( baseadr + offset), info.Stride);                                         
281                                                 offset -= info.Stride;                          
282                                         }                                                                                                       
283                                         
284                                         info.Scan0 = lfBuffer;
285                                         info.Allocated=true;                                            
286                                         
287                                 } else {
288                                         //
289                                         // FIXME
290                                         // 
291                                         Console.WriteLine ("BmpCodec: The {0} compression is not supported", bmih.biCompression);
292                                 }
293                                 break;
294                         default:
295                                 throw new NotImplementedException(String.Format("This format is not yet supported : {0} bpp", bmih.biBitCount));
296                         }
297
298                         return true;
299                 }
300
301                 internal static void EncodeDelegate (Image image, Stream stream)
302                 {
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);
308                 }
309                 
310                 internal bool Encode (Image image, Stream stream, BitmapData info)
311                 {
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);
318                                              
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);
325
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");
340                         
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);                                                
347
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();
352                         
353                         Console.WriteLine ("Offset ->" + offset);                               
354                         
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 
357                         //      bitmap.                                                 
358                         while(offset>=0){                               
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);
363                                 offset -= stride;                                       
364                         }
365                         return true;
366                 }
367         }
368 }