* ItfImage.cs: Reformatted and removed sections that were
[mono.git] / mcs / class / System.Drawing / System.Drawing.Imaging / JPEGCodec.cs
1 //
2 // System.Drawing.Imaging.JPEGCodec.cs
3 //
4 // Author: 
5 //              Alexandre Pigolkine (pigolkine@gmx.de)
6 //
7 // (C) 2002/2003 Ximian, Inc.
8
9 #if DECLARE_CDECL_DELEGATES
10 namespace cdeclCallback {
11         using System;
12         internal class cdeclRedirector {
13                 internal delegate void MethodVoidIntPtr(IntPtr param);
14                 internal delegate int MethodIntIntPtr(IntPtr param);
15                 internal delegate void MethodVoidIntPtrInt(IntPtr param, int param1);
16                 internal delegate int MethodIntIntPtrInt(IntPtr param,int param1);
17                 internal delegate void MethodVoidIntPtrIntPtr(IntPtr png_structp,IntPtr png_const_charp);
18                 internal delegate void MethodVoidIntPtrIntPtrInt(IntPtr png_structp,IntPtr bytep, int size);
19         }
20 }
21 #endif
22
23 namespace System.Drawing.Imaging
24 {
25         using System;
26         using System.IO;
27         using System.Drawing.Imaging;
28         using System.Runtime.InteropServices;
29         using cdeclCallback;
30
31         /// <summary>
32         /// Summary description for JPEGCodec.
33         /// </summary>
34         internal class JPEGCodec
35         {
36                 enum J_COLOR_SPACE : int {
37                         JCS_UNKNOWN             = 0,            /* error/unspecified */
38                         JCS_GRAYSCALE   = 1,            /* monochrome */
39                         JCS_RGB                 = 2,            /* red/green/blue */
40                         JCS_YCbCr               = 3,            /* Y/Cb/Cr (also known as YUV) */
41                         JCS_CMYK                = 4,            /* C/M/Y/K */
42                         JCS_YCCK                = 5                     /* Y/Cb/Cr/K */
43                 }
44
45                 [StructLayout(LayoutKind.Sequential)]
46                 internal struct jpeg_error_mgr_get {
47                         public IntPtr a1;
48                         public IntPtr a2;
49                         public IntPtr a3;
50                         public IntPtr a4;
51                         public IntPtr a5;
52                         public int msg_code;
53                         [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=20)]
54                         public int[] param_array;
55                         public int trace_level;
56                         public int num_warnings;
57                         public int jpeg_message_table;
58                         public int last_jpeg_message;
59                         public int addon_message_table;
60                         public int first_addon_message;
61                         public int last_addon_message;
62                 };
63
64                 [StructLayout(LayoutKind.Sequential)]
65                 internal struct jpeg_error_mgr {
66                         public cdeclRedirector.MethodVoidIntPtr error_exit;
67                         public IntPtr a2;
68                         public IntPtr a3;
69                         public IntPtr a4;
70                         public IntPtr a5;
71                         public int msg_code;
72                         [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=20)]
73                         public int[] param_array;
74                         public int trace_level;
75                         public int num_warnings;
76                         public int jpeg_message_table;
77                         public int last_jpeg_message;
78                         public int addon_message_table;
79                         public int first_addon_message;
80                         public int last_addon_message;
81                 };
82
83                 [StructLayout(LayoutKind.Sequential)]
84                 struct jpeg_source_mgr {
85                         public IntPtr next_input_byte; /* => next byte to read from buffer */
86                         public uint   bytes_in_buffer;  /* # of bytes remaining in buffer */
87
88                         //[MarshalAs(UnmanagedType.FunctionPtr)]
89                         public cdeclRedirector.MethodVoidIntPtr init_source;
90                         //[MarshalAs(UnmanagedType.FunctionPtr)]
91                         public cdeclRedirector.MethodIntIntPtr fill_input_buffer;
92                         //[MarshalAs(UnmanagedType.FunctionPtr)]
93                         public cdeclRedirector.MethodVoidIntPtrInt skip_input_data;
94                         //[MarshalAs(UnmanagedType.FunctionPtr)]
95                         public cdeclRedirector.MethodIntIntPtrInt resync_to_restart;
96                         //[MarshalAs(UnmanagedType.FunctionPtr)]
97                         public cdeclRedirector.MethodVoidIntPtr term_source;
98                 };
99
100                 [StructLayout(LayoutKind.Sequential)]
101                 struct jpeg_destination_mgr {
102                         public IntPtr next_output_byte; /* => next byte to write in buffer */
103                         public uint   free_in_buffer;   /* # of byte spaces remaining in buffer */
104
105                         //[MarshalAs(UnmanagedType.FunctionPtr)]
106                         public cdeclRedirector.MethodVoidIntPtr init_destination;
107                         //[MarshalAs(UnmanagedType.FunctionPtr)]
108                         public cdeclRedirector.MethodIntIntPtr empty_output_buffer;
109                         //[MarshalAs(UnmanagedType.FunctionPtr)]
110                         public cdeclRedirector.MethodVoidIntPtr term_destination;
111                 };
112
113                 class jpeg_compress_decompress_base {
114                         byte[] raw_struct_array;
115                         IntPtr raw_error_mgr = IntPtr.Zero;
116                         IntPtr raw_source_mgr = IntPtr.Zero;
117                         IntPtr raw_destination_mgr = IntPtr.Zero;
118
119                         internal struct structure_fields {
120                                 internal int structure_size;
121                                 internal int QUANTIZE_COLORS;
122                                 internal int ACTUAL_NUMBER_OF_COLORS;
123                                 internal int OUT_COLOR_SPACE;
124                                 internal int OUTPUT_WIDTH;
125                                 internal int OUTPUT_HEIGHT;
126                                 internal int OUT_COLOR_COMPONENT;
127                                 internal int OUTPUT_COMPONENTS;
128                                 internal int OUTPUT_SCANLINE;
129                                 internal int OUT_COLORMAP;
130                                 internal int IMAGE_WIDTH;
131                                 internal int IMAGE_HEIGHT;
132                                 internal int INPUT_COMPONENTS;
133                                 internal int IN_COLOR_SPACE;
134                                 internal int NEXT_SCAN_LINE;
135                         };
136
137                         structure_fields[] known_libraries;
138                         int current_library_index;
139                         
140                         public jpeg_compress_decompress_base(structure_fields[] known_libraries, int start_index) {
141                                 this.known_libraries = known_libraries;
142                                 current_library_index = start_index;
143                                 raw_struct_array = new byte[known_libraries[current_library_index].structure_size];
144                         }
145
146                         public void switch_to_struct_size(int size) {
147                                 if (raw_struct_array.Length == size) return;
148
149                                 bool structureFound = false;
150                                 for( int i = 0; i < known_libraries.Length; i++) {
151                                         if( known_libraries[i].structure_size == size) {
152                                                 current_library_index = i;
153                                                 raw_struct_array = new byte[known_libraries[current_library_index].structure_size];
154                                                 structureFound = true;
155                                                 break;
156                                         }
157                                 }
158                                 if (!structureFound){
159                                         throw new Exception(String.Format("JPEG Codec cannot work with existing libjpeg.Structure size {0}.", size));
160                                 }
161                         }
162
163                         public byte[] raw_struct {
164                                 get {
165                                         return raw_struct_array;
166                                 }
167                         }
168
169                         unsafe protected void copyToStruct( int value, int offset) {
170                                 fixed( byte* pd = raw_struct_array) {
171                                         *((int*)(pd + offset)) = value;
172                                 }
173                         }
174
175                         unsafe protected int copyFromStruct( int offset) {
176                                 int result = 0;
177                                 fixed( byte* pd = raw_struct_array) {
178                                         result = *((int*)(pd + offset));
179                                 }
180                                 return result;
181                         }
182
183                         public jpeg_error_mgr jpeg_error_mgr {
184                                 set {
185                                         raw_error_mgr = Marshal.AllocHGlobal(Marshal.SizeOf(value));
186                                         Marshal.StructureToPtr(value, raw_error_mgr, false);
187                                         copyToStruct(raw_error_mgr.ToInt32(), 0);
188                                 }
189                         }
190
191                         public jpeg_source_mgr jpeg_source_mgr {
192                                 set {
193                                         raw_source_mgr = Marshal.AllocHGlobal( Marshal.SizeOf(value));
194                                         Marshal.StructureToPtr( value, raw_source_mgr, false);
195                                         copyToStruct(raw_source_mgr.ToInt32(), 24);
196                                 }
197                         }
198
199                         public jpeg_destination_mgr jpeg_destination_mgr {
200                                 set {
201                                         raw_destination_mgr = Marshal.AllocHGlobal( Marshal.SizeOf(value));
202                                         Marshal.StructureToPtr( value, raw_destination_mgr, false);
203                                         copyToStruct(raw_destination_mgr.ToInt32(), 24);
204                                 }
205                         }
206
207                         public int Stride {
208                                 get {
209                                         return OutputWidth * OutputComponents;
210                                 }
211                         }
212
213                         public Color[] ColorMap {
214                                 get {
215                                         int actual_number_of_colors = copyFromStruct(known_libraries[current_library_index].ACTUAL_NUMBER_OF_COLORS);
216                                         IntPtr nativeMap = (IntPtr)copyFromStruct(known_libraries[current_library_index].OUT_COLORMAP);
217                                         Color[] map = new Color[actual_number_of_colors];
218                                         if (nativeMap != IntPtr.Zero) {
219                                                 byte[] byteMap = new byte[OutColorComponents * actual_number_of_colors];
220                                                 Marshal.Copy( (IntPtr)Marshal.ReadInt32(nativeMap), byteMap, 0, byteMap.Length);
221                                         }
222                                         return map;
223                                 }
224                         }
225
226                         public J_COLOR_SPACE OutColorSpace {
227                                 get {
228                                         return (J_COLOR_SPACE)copyFromStruct(known_libraries[current_library_index].OUT_COLOR_SPACE);
229                                 }
230                                 set {
231                                         copyToStruct((int)value,known_libraries[current_library_index].OUT_COLOR_SPACE);
232                                 }
233                         }
234
235                         public bool QuantizeColors {
236                                 get {
237                                         return raw_struct[known_libraries[current_library_index].QUANTIZE_COLORS] != (byte)0 ? true : false;
238                                 }
239                                 set {
240                                         raw_struct[known_libraries[current_library_index].QUANTIZE_COLORS] = value ? (byte)1 : (byte)0;
241                                 }
242                         }
243
244                         public int OutputWidth {
245                                 get {
246                                         return copyFromStruct(known_libraries[current_library_index].OUTPUT_WIDTH);
247                                 }
248                         }
249
250                         public int OutputHeight {
251                                 get {
252                                         return copyFromStruct(known_libraries[current_library_index].OUTPUT_HEIGHT);
253                                 }
254                         }
255
256                         public int OutColorComponents {
257                                 get {
258                                         return copyFromStruct(known_libraries[current_library_index].OUT_COLOR_COMPONENT);
259                                 }
260                         }
261
262                         public int OutputComponents {
263                                 get {
264                                         return copyFromStruct(known_libraries[current_library_index].OUTPUT_COMPONENTS);
265                                 }
266                         }
267
268                         public int OutputScanLine {
269                                 get {
270                                         return copyFromStruct(known_libraries[current_library_index].OUTPUT_SCANLINE);
271                                 }
272                         }
273
274                         public int ImageWidth {
275                                 set {
276                                         copyToStruct(value, known_libraries[current_library_index].IMAGE_WIDTH);
277                                 }
278                         }
279
280                         public int ImageHeight {
281                                 get {
282                                         return copyFromStruct(known_libraries[current_library_index].IMAGE_HEIGHT);
283                                 }
284                                 set {
285                                         copyToStruct(value, known_libraries[current_library_index].IMAGE_HEIGHT);
286                                 }
287                         }
288
289                         public int InputComponents {
290                                 set {
291                                         copyToStruct(value, known_libraries[current_library_index].INPUT_COMPONENTS);
292                                 }
293                         }
294
295                         public J_COLOR_SPACE InColorSpace {
296                                 set {
297                                         copyToStruct((int)value, known_libraries[current_library_index].IN_COLOR_SPACE);
298                                 }
299                         }
300
301                         public int NextScanLine {
302                                 get {
303                                         return copyFromStruct(known_libraries[current_library_index].NEXT_SCAN_LINE);
304                                 }
305                         }
306                 }
307         
308                 class jpeg_decompress_struct : jpeg_compress_decompress_base {
309
310                         const int GNU_JPEG_DLL_WINDOWS = 0;
311                         const int LINUX_LIBJPEG = 1;
312                         const int KNOWN_JPEG_LINRARIES = 2;
313
314                         static bool offsets_initialized;
315                         static jpeg_compress_decompress_base.structure_fields[] known_jpeg_libraries;
316                         static int current_library_index = LINUX_LIBJPEG;
317                         
318                         static void initialize_jpeg_decompress_structs() {
319                                 if (!offsets_initialized) {
320                                         known_jpeg_libraries = new jpeg_compress_decompress_base.structure_fields[KNOWN_JPEG_LINRARIES];
321                                         // GNU JPEG Windows version
322                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].structure_size = 432;
323                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].QUANTIZE_COLORS = 74;
324                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].ACTUAL_NUMBER_OF_COLORS = 112;
325                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUT_COLOR_SPACE = 44;
326                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUTPUT_WIDTH = 92;
327                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUTPUT_HEIGHT = 96;
328                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUT_COLOR_COMPONENT = 100;
329                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUTPUT_COMPONENTS = 104;
330                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUTPUT_SCANLINE = 120;
331                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].OUT_COLORMAP = 116;
332
333                                         // libjpeg Linux version
334                                         known_jpeg_libraries[LINUX_LIBJPEG].structure_size = 464;
335                                         known_jpeg_libraries[LINUX_LIBJPEG].QUANTIZE_COLORS = 84;
336                                         known_jpeg_libraries[LINUX_LIBJPEG].ACTUAL_NUMBER_OF_COLORS = 132;
337                                         known_jpeg_libraries[LINUX_LIBJPEG].OUT_COLOR_SPACE = 44;
338                                         known_jpeg_libraries[LINUX_LIBJPEG].OUTPUT_WIDTH = 112;
339                                         known_jpeg_libraries[LINUX_LIBJPEG].OUTPUT_HEIGHT = 116;
340                                         known_jpeg_libraries[LINUX_LIBJPEG].OUT_COLOR_COMPONENT = 120;
341                                         known_jpeg_libraries[LINUX_LIBJPEG].OUTPUT_COMPONENTS = 124;
342                                         known_jpeg_libraries[LINUX_LIBJPEG].OUTPUT_SCANLINE = 140;
343                                         known_jpeg_libraries[LINUX_LIBJPEG].OUT_COLORMAP = 136;
344
345                                         offsets_initialized = true;
346                                 }
347                         }
348
349                         static jpeg_decompress_struct() {
350                                 initialize_jpeg_decompress_structs();
351                         }
352
353                         public jpeg_decompress_struct() : base(known_jpeg_libraries, LINUX_LIBJPEG) {
354                         }
355                 }
356
357                 class jpeg_compress_struct : jpeg_compress_decompress_base {
358
359                         const int GNU_JPEG_DLL_WINDOWS = 0;
360                         const int LINUX_LIBJPEG = 1;
361                         const int KNOWN_JPEG_LINRARIES = 2;
362
363                         static bool offsets_initialized;
364                         static jpeg_compress_decompress_base.structure_fields[] known_jpeg_libraries;
365                         static int current_library_index = LINUX_LIBJPEG;
366                         
367                         static void initialize_jpeg_compress_structs() {
368                                 if (!offsets_initialized) {
369                                         known_jpeg_libraries = new jpeg_compress_decompress_base.structure_fields[KNOWN_JPEG_LINRARIES];
370                                         // GNU JPEG Windows version
371                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].structure_size = 360;
372                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].IMAGE_WIDTH = 28;
373                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].IMAGE_HEIGHT = 32;
374                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].INPUT_COMPONENTS = 36;
375                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].IN_COLOR_SPACE = 40;
376                                         known_jpeg_libraries[GNU_JPEG_DLL_WINDOWS].NEXT_SCAN_LINE = 208;
377
378                                         // libjpeg Linux version
379                                         known_jpeg_libraries[LINUX_LIBJPEG].structure_size = 372;
380                                         known_jpeg_libraries[LINUX_LIBJPEG].IMAGE_WIDTH = 28;
381                                         known_jpeg_libraries[LINUX_LIBJPEG].IMAGE_HEIGHT = 32;
382                                         known_jpeg_libraries[LINUX_LIBJPEG].INPUT_COMPONENTS = 36;
383                                         known_jpeg_libraries[LINUX_LIBJPEG].IN_COLOR_SPACE = 40;
384                                         known_jpeg_libraries[LINUX_LIBJPEG].NEXT_SCAN_LINE = 220;
385
386                                         offsets_initialized = true;
387                                 }
388                         }
389
390                         static jpeg_compress_struct() {
391                                 initialize_jpeg_compress_structs();
392                         }
393
394                         public jpeg_compress_struct() : base(known_jpeg_libraries, LINUX_LIBJPEG) {
395                         }
396                 }
397
398                 [StructLayout(LayoutKind.Sequential)]
399                 internal struct JSAMPARRAY {
400                         // FIXME: This code is not working on Mono(** ERROR **: Invalid IL code at...). Report a bug and change it later.
401                         //const int MAX_SCAN_LINES = 10;
402                         //[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=MAX_SCAN_LINES)]
403                         //internal  IntPtr[] JSAMPLES;
404                         internal IntPtr JSAMPLE0;
405                         internal IntPtr JSAMPLE1;
406
407                         internal  JSAMPARRAY(int len) {
408 /*
409                                 JSAMPLES = new IntPtr[MAX_SCAN_LINES];
410                                 for (int i = 0; i < MAX_SCAN_LINES; i++) {
411                                         JSAMPLES[i] = Marshal.AllocHGlobal(len);
412                                 }
413 */                              
414                                 JSAMPLE0 = Marshal.AllocHGlobal(len);
415                                 JSAMPLE1 = Marshal.AllocHGlobal(len);
416                         }
417
418                         internal  void Dispose() {
419 /*
420                                 for (int i = 0; i < MAX_SCAN_LINES; i++) {
421                                         Marshal.FreeHGlobal(JSAMPLES[i]);
422                                 }
423 */                              
424                                 Marshal.FreeHGlobal(JSAMPLE0);
425                                 Marshal.FreeHGlobal(JSAMPLE1);
426                         }
427                 }
428
429                 const string JPEGLibrary = "jpeg";
430                 [DllImport(JPEGLibrary, EntryPoint="jpeg_CreateCompress", CallingConvention=CallingConvention.Cdecl)]
431                 internal static extern void jpeg_create_compress(byte[] info, int version, int structure_size);
432                 
433                 [DllImport(JPEGLibrary, EntryPoint="jpeg_CreateDecompress", CallingConvention=CallingConvention.Cdecl)]
434                 internal static extern void jpeg_create_decompress(byte[] info, int version, int structure_size);
435                 
436                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
437                 internal static extern void jpeg_std_error(ref jpeg_error_mgr_get err_mgr);
438
439                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
440                 internal static extern void jpeg_set_defaults(byte[] cinfo);
441
442                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
443                 internal static extern void jpeg_set_quality(byte[] cinfo, int quality, int force_baseline);\r
444                 
445                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
446                 internal static extern int jpeg_read_header(byte[] cinfo, int condition);
447                 
448                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
449                 internal static extern void jpeg_calc_output_dimensions(byte[] cinfo);
450
451                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
452                 internal static extern int jpeg_start_compress(byte[] cinfo, int write_all_tables);
453                 
454                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
455                 internal static extern int jpeg_start_decompress(byte[] cinfo);
456                 
457                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
458                 internal static extern int jpeg_read_scanlines(byte[] cinfo, ref JSAMPARRAY buffer, int num);
459
460                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
461                 internal static extern int jpeg_write_scanlines(byte[] cinfo, ref JSAMPARRAY scanlines, int num_lines);\r
462                 
463                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
464                 internal static extern int jpeg_finish_compress(byte[] cinfo);
465                 
466                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
467                 internal static extern int jpeg_finish_decompress(byte[] cinfo);
468                 
469                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
470                 internal static extern void jpeg_destroy_compress(byte[] cinfo);
471                 
472                 [DllImport(JPEGLibrary, CallingConvention=CallingConvention.Cdecl)]
473                 internal static extern void jpeg_destroy_decompress(byte[] cinfo);
474
475                 Stream fs;
476                 IntPtr buffer;
477                 int readwriteSize = 4096;
478
479                 // Source manager callbacks
480                 void init_source( IntPtr cinfo) {
481                         buffer = Marshal.AllocHGlobal(readwriteSize);
482                 }
483
484                 int fill_input_buffer( IntPtr cinfo) {
485                         byte[] result = new byte[readwriteSize];
486                         int readed = fs.Read(result, 0, readwriteSize);
487                         Marshal.Copy(result, 0, buffer, readed);
488                         IntPtr srcAddr = (IntPtr)Marshal.ReadInt32(cinfo, 24);
489                         Marshal.WriteInt32(srcAddr, 0, buffer.ToInt32());
490                         Marshal.WriteInt32(srcAddr, 4, readed);
491                         return 1;
492                 }
493
494                 void skip_input_data( IntPtr cinfo, int num_bytes) {
495                         //byte[] result = new byte[num_bytes];
496                         //fs.Read(result, 0, num_bytes);
497                         fs.Seek(num_bytes, SeekOrigin.Current);
498                 }
499
500                 int resync_to_restart( IntPtr cinfo, int desired){
501                         return 0;
502                 }
503
504                 void term_source( IntPtr cinfo) {
505                         Marshal.FreeHGlobal(buffer);
506                 }
507
508                 // Destination manager callbacks
509                 void init_destination( IntPtr cinfo) {
510                         buffer = Marshal.AllocHGlobal(readwriteSize);
511                         IntPtr srcAddr = (IntPtr)Marshal.ReadInt32(cinfo, 24);
512                         Marshal.WriteInt32(srcAddr, 0, buffer.ToInt32());
513                         Marshal.WriteInt32(srcAddr, 4, readwriteSize);
514                 }
515
516                 int empty_output_buffer( IntPtr cinfo) {
517                         IntPtr srcAddr = (IntPtr)Marshal.ReadInt32(cinfo, 24);
518                         IntPtr bufferPtr = (IntPtr)Marshal.ReadInt32(srcAddr, 0);
519                         int bytes = readwriteSize - Marshal.ReadInt32(srcAddr, 4);
520
521                         // FIXME: shall we have a buffer as a member variable here ?
522                         byte[] result = new byte[readwriteSize];
523                         Marshal.Copy(buffer, result, 0, readwriteSize);
524                         fs.Write(result, 0, readwriteSize);
525                         Marshal.WriteInt32(srcAddr, 0, buffer.ToInt32());
526                         Marshal.WriteInt32(srcAddr, 4, readwriteSize);
527                         return 1;
528                 }
529
530                 void term_destination( IntPtr cinfo) {
531                         IntPtr srcAddr = (IntPtr)Marshal.ReadInt32(cinfo, 24);
532                         IntPtr bufferPtr = (IntPtr)Marshal.ReadInt32(srcAddr, 0);
533                         int bytes = readwriteSize - Marshal.ReadInt32(srcAddr, 4);
534                         byte[] result = new byte[bytes];
535                         Marshal.Copy(buffer, result, 0, bytes);
536                         fs.Write(result, 0, bytes);
537                         Marshal.FreeHGlobal(buffer);
538                 }
539
540                 class RetryInitializationException : Exception {
541                         int libraryStructureSize;
542                         public RetryInitializationException(int structureSize) {
543                                 this.libraryStructureSize = structureSize;
544                         }
545                         public int LibraryStructureSize {
546                                 get {
547                                         return libraryStructureSize;
548                                 }
549                         }
550                 }
551                 
552                 enum JPEGErrorCodes : int {
553                         JERR_BAD_STRUCT_SIZE = 21
554                 }
555
556                 void error_exit( IntPtr cinfo) {
557                         jpeg_error_mgr mgr = new jpeg_error_mgr();
558                         IntPtr err_raw = (IntPtr)Marshal.ReadInt32(cinfo, 0);
559                         mgr = (jpeg_error_mgr)Marshal.PtrToStructure(err_raw, mgr.GetType());
560                         if ( mgr.msg_code == (int)JPEGErrorCodes.JERR_BAD_STRUCT_SIZE) {
561                                 throw new RetryInitializationException(mgr.param_array[0]);
562                         }
563                         throw new Exception();
564                 }
565
566                 internal JPEGCodec() {
567                 }
568
569                 internal static ImageCodecInfo CodecInfo {
570                         get {
571                                 ImageCodecInfo info = new ImageCodecInfo();
572                                 info.Flags = ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.Builtin | ImageCodecFlags.SupportBitmap;
573                                 info.FormatDescription = "JPEG file format";
574                                 info.FormatID = System.Drawing.Imaging.ImageFormat.Jpeg.Guid;
575                                 info.MimeType = "image/jpeg";
576                                 info.Version = 1;
577                                 byte[][] signaturePatterns = new byte[1][];
578                                 signaturePatterns[0] = new byte[]{0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00};
579                                 info.SignaturePatterns = signaturePatterns;
580                                 byte[][] signatureMasks = new byte[1][];
581                                 signatureMasks[0] = new byte[]{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
582                                 info.SignatureMasks = signatureMasks;
583                                 info.decode += new ImageCodecInfo.DecodeFromStream(JPEGCodec.DecodeDelegate);
584                                 info.encode += new ImageCodecInfo.EncodeToStream(JPEGCodec.EncodeDelegate);
585                                 return info;
586                         }
587                 }
588
589                 internal static void DecodeDelegate (Stream stream, InternalImageInfo info) {
590                         JPEGCodec jpeg = new JPEGCodec();
591                         jpeg.Decode (stream, info);
592                 }
593
594                 internal static void EncodeDelegate (Stream stream, InternalImageInfo info) {
595                         JPEGCodec jpeg = new JPEGCodec();
596                         jpeg.Encode (stream, info);
597                 }
598
599                 internal unsafe void switch_color_bytes( byte[] image) {
600                         fixed(byte* start = image) {
601                                 byte *pb = start;
602                                 byte t1;
603                                 for( int ic = 0; ic < image.Length; ic +=3) {
604                                         t1 = *pb;
605                                         *(pb) = *(pb+2);
606                                         *(pb+2) = t1;
607                                         pb += 3;
608                                 }
609                         }
610                 }
611
612                 internal bool Decode( Stream stream, InternalImageInfo info) {
613                         fs = stream;
614
615                         jpeg_error_mgr_get mgr = new jpeg_error_mgr_get();
616                         mgr.param_array = new int[20];
617                         jpeg_std_error( ref mgr);
618                         jpeg_error_mgr mgr_real = new jpeg_error_mgr();
619                         mgr_real.param_array = new int[20];
620                         mgr_real.error_exit = new cdeclCallback.cdeclRedirector.MethodVoidIntPtr(this.error_exit);
621                         mgr_real.msg_code = mgr.msg_code;
622                         mgr_real.a2 = mgr.a2;
623                         mgr_real.a3 = mgr.a3;
624                         mgr_real.a4 = mgr.a4;
625                         mgr_real.a5 = mgr.a5;
626                         mgr_real.trace_level = mgr.trace_level;
627                         mgr_real.num_warnings = mgr.num_warnings;
628                         mgr_real.last_jpeg_message = mgr.last_jpeg_message;
629                         mgr_real.first_addon_message = mgr.first_addon_message;
630                         mgr_real.last_addon_message = mgr.last_addon_message;
631                         mgr_real.jpeg_message_table = mgr.jpeg_message_table;
632                         
633                         jpeg_decompress_struct cinfo = new jpeg_decompress_struct();
634                         cinfo.jpeg_error_mgr = mgr_real;
635                         bool initializedOk = false;
636                         do {
637                                 try {
638                                         jpeg_create_decompress(cinfo.raw_struct, 62, cinfo.raw_struct.Length);
639                                         initializedOk = true;
640                                 }
641                                 catch( RetryInitializationException ex) {
642                                         initializedOk = false;
643                                         cinfo.switch_to_struct_size(ex.LibraryStructureSize);
644                                         cinfo.jpeg_error_mgr = mgr_real;
645                                 }
646                         }while( !initializedOk);
647
648                         jpeg_source_mgr smgr = new jpeg_source_mgr();
649                         smgr.next_input_byte = IntPtr.Zero;
650                         smgr.bytes_in_buffer = 0;
651
652                         smgr.init_source = new cdeclRedirector.MethodVoidIntPtr(this.init_source);
653                         smgr.fill_input_buffer = new cdeclRedirector.MethodIntIntPtr(this.fill_input_buffer);
654                         smgr.skip_input_data = new cdeclRedirector.MethodVoidIntPtrInt(this.skip_input_data);
655                         smgr.resync_to_restart = new cdeclRedirector.MethodIntIntPtrInt(this.resync_to_restart);
656                         smgr.term_source = new cdeclRedirector.MethodVoidIntPtr(this.term_source);
657                         cinfo.jpeg_source_mgr = smgr;
658
659                         jpeg_read_header( cinfo.raw_struct, 1);
660
661                         jpeg_calc_output_dimensions(cinfo.raw_struct);
662                         jpeg_start_decompress(cinfo.raw_struct);
663
664                         int row_width = cinfo.Stride;
665                         while ((row_width & 3) != 0) row_width++;
666                         int pad_bytes = (row_width - cinfo.Stride);
667
668                         if (cinfo.OutColorSpace == J_COLOR_SPACE.JCS_RGB) {
669                                 if (cinfo.QuantizeColors) {
670                                         info.PixelFormat = PixelFormat.Format8bppIndexed;
671                                 }
672                                 else {
673                                         info.PixelFormat = PixelFormat.Format24bppRgb;
674                                 }
675                         }
676                         else {
677                                 info.PixelFormat = PixelFormat.Format8bppIndexed;
678                         }
679                         info.Size = new Size(cinfo.OutputWidth,cinfo.OutputHeight);
680                         info.Stride = row_width;
681                         info.Palette = new ColorPalette(1, cinfo.ColorMap);
682                         info.PixelFormat = PixelFormat.Format24bppRgb;
683                         info.RawImageBytes = new byte[(cinfo.OutputHeight) * row_width];
684
685                         JSAMPARRAY outbuf = new JSAMPARRAY(cinfo.Stride);
686                         int outputIndex = info.RawImageBytes.Length - row_width;
687
688                         while (cinfo.OutputScanLine < cinfo.OutputHeight) {
689                                 // FIXME: switch to the Length after fixing a run-time error
690                                 int readed = jpeg_read_scanlines(cinfo.raw_struct, ref outbuf, 1 /*outbuf.JSAMPLES.Length*/);
691                                 for (int i = 0; i < readed; i++) {
692                                         // FIXME: switch to .JSAMPLES[i] after fix of run-time error
693                                         //Marshal.Copy(outbuf.JSAMPLES[i], info.RawImageBytes, outputIndex, cinfo.Stride);
694                                         Marshal.Copy(outbuf.JSAMPLE0, info.RawImageBytes, outputIndex, cinfo.Stride);
695                                         outputIndex -= row_width;
696                                 }
697                         }
698                         // FIXME: not sure if this always works
699                         switch_color_bytes(info.RawImageBytes);
700                         jpeg_finish_decompress(cinfo.raw_struct);
701                         jpeg_destroy_decompress(cinfo.raw_struct);
702                         return true;
703                 }
704
705                 internal unsafe bool Encode( Stream stream, InternalImageInfo info) {
706                 
707                         int bpp = Image.GetPixelFormatSize(info.PixelFormat) / 8;
708                         if( bpp != 3 && bpp != 4) {
709                                 throw new ArgumentException(String.Format("Supplied pixel format is not yet supported: {0}, {1} bpp", info.PixelFormat, Image.GetPixelFormatSize(info.PixelFormat)));
710                         }
711
712                         fs = stream;
713
714                         jpeg_error_mgr_get mgr = new jpeg_error_mgr_get();
715                         mgr.param_array = new int[20];
716                         jpeg_std_error( ref mgr);
717                         jpeg_error_mgr mgr_real = new jpeg_error_mgr();
718                         mgr_real.param_array = new int[20];
719                         mgr_real.error_exit = new cdeclCallback.cdeclRedirector.MethodVoidIntPtr(this.error_exit);
720                         mgr_real.msg_code = mgr.msg_code;
721                         mgr_real.a2 = mgr.a2;
722                         mgr_real.a3 = mgr.a3;
723                         mgr_real.a4 = mgr.a4;
724                         mgr_real.a5 = mgr.a5;
725                         mgr_real.trace_level = mgr.trace_level;
726                         mgr_real.num_warnings = mgr.num_warnings;
727                         mgr_real.last_jpeg_message = mgr.last_jpeg_message;
728                         mgr_real.first_addon_message = mgr.first_addon_message;
729                         mgr_real.last_addon_message = mgr.last_addon_message;
730                         mgr_real.jpeg_message_table = mgr.jpeg_message_table;
731                         
732                         jpeg_compress_struct cinfo = new jpeg_compress_struct();
733                         cinfo.jpeg_error_mgr = mgr_real;
734                         bool initializedOk = false;
735                         do {
736                                 try {
737                                         jpeg_create_compress(cinfo.raw_struct, 62, cinfo.raw_struct.Length);
738                                         initializedOk = true;
739                                 }
740                                 catch( RetryInitializationException ex) {
741                                         initializedOk = false;
742                                         cinfo.switch_to_struct_size(ex.LibraryStructureSize);
743                                         cinfo.jpeg_error_mgr = mgr_real;
744                                 }
745                         }while( !initializedOk);
746
747                         jpeg_destination_mgr dmgr = new jpeg_destination_mgr();
748                         dmgr.next_output_byte = IntPtr.Zero;
749                         dmgr.free_in_buffer = 0;
750
751                         dmgr.init_destination = new cdeclRedirector.MethodVoidIntPtr(this.init_destination);
752                         dmgr.empty_output_buffer = new cdeclRedirector.MethodIntIntPtr(this.empty_output_buffer);
753                         dmgr.term_destination = new cdeclRedirector.MethodVoidIntPtr(this.term_destination);
754                         cinfo.jpeg_destination_mgr = dmgr;
755
756                         int row_width = info.Size.Width;
757                         while ((row_width & 3) != 0) row_width++;
758
759                         cinfo.ImageWidth = info.Size.Width;
760                         cinfo.ImageHeight = info.Size.Height;
761                         cinfo.InputComponents = 3;
762                         cinfo.InColorSpace = J_COLOR_SPACE.JCS_RGB;
763
764                         jpeg_set_defaults( cinfo.raw_struct);
765
766                         jpeg_start_compress( cinfo.raw_struct, 1);
767
768                         int row_bytes_width = row_width * 3;
769                         int src_row_bytes_width = row_width * bpp;
770                         JSAMPARRAY inbuf = new JSAMPARRAY(row_bytes_width);
771
772                         int outputIndex = info.RawImageBytes.Length - src_row_bytes_width;
773                         byte[] buffer = new byte[row_bytes_width];
774                         fixed( byte *psrc = info.RawImageBytes, pbuf = buffer) {
775                                 byte* curSrc = null;
776                                 byte* curDst = null;
777                                 while (cinfo.NextScanLine < cinfo.ImageHeight) {
778                                         curSrc = psrc + outputIndex;
779                                         curDst = pbuf;
780                                         for( int i = 0; i < row_width; i++) {
781                                                 *curDst++ = *(curSrc+2);
782                                                 *curDst++ = *(curSrc+1);
783                                                 *curDst++ = *curSrc;
784                                                 curSrc += bpp;
785                                         }
786                                         Marshal.Copy( buffer, 0, inbuf.JSAMPLE0, row_bytes_width);
787                                         outputIndex -= src_row_bytes_width;
788                                         jpeg_write_scanlines(cinfo.raw_struct, ref inbuf, 1 /*inbuf.JSAMPLES.Length*/);
789                                 }
790                         }
791
792                         jpeg_finish_compress(cinfo.raw_struct);
793                         jpeg_destroy_compress(cinfo.raw_struct);
794
795                         return true;
796                 }
797         }
798 }