Move the sources to trunk
[opencv] / otherlibs / highgui / grfmt_tiff.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 /****************************************************************************************\
43     A part of the file implements TIFF reader on base of libtiff library
44     (see otherlibs/_graphics/readme.txt for copyright notice)
45 \****************************************************************************************/
46
47 #include "_highgui.h"
48 #include "grfmt_tiff.h"
49
50 static const char fmtSignTiffII[] = "II\x2a\x00";
51 static const char fmtSignTiffMM[] = "MM\x00\x2a";
52
53 GrFmtTiff::GrFmtTiff()
54 {
55     m_sign_len = 4;
56     m_signature = "";
57     m_description = "TIFF Files (*.tiff;*.tif)";
58 }
59
60 GrFmtTiff::~GrFmtTiff()
61 {
62 }
63
64 bool GrFmtTiff::CheckSignature( const char* signature )
65 {
66     return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
67            memcmp( signature, fmtSignTiffMM, 4 ) == 0;
68 }
69
70
71 GrFmtReader* GrFmtTiff::NewReader( const char* filename )
72 {
73     return new GrFmtTiffReader( filename );
74 }
75
76
77 GrFmtWriter* GrFmtTiff::NewWriter( const char* filename )
78 {
79     return new GrFmtTiffWriter( filename );
80 }
81
82
83 #ifdef HAVE_TIFF
84
85 #include "tiff.h"
86 #include "tiffio.h"
87
88 static int grfmt_tiff_err_handler_init = 0;
89
90 static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {}
91
92 GrFmtTiffReader::GrFmtTiffReader( const char* filename ) : GrFmtReader( filename )
93 {
94     m_tif = 0;
95
96     if( !grfmt_tiff_err_handler_init )
97     {
98         grfmt_tiff_err_handler_init = 1;
99
100         TIFFSetErrorHandler( GrFmtSilentTIFFErrorHandler );
101         TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler );
102     }
103 }
104
105
106 GrFmtTiffReader::~GrFmtTiffReader()
107 {
108 }
109
110
111 void  GrFmtTiffReader::Close()
112 {
113     if( m_tif )
114     {
115         TIFF* tif = (TIFF*)m_tif;
116         TIFFClose( tif );
117         m_tif = 0;
118     }
119 }
120
121
122 bool  GrFmtTiffReader::CheckFormat( const char* signature )
123 {
124     return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
125            memcmp( signature, fmtSignTiffMM, 4 ) == 0;
126 }
127
128
129 bool  GrFmtTiffReader::ReadHeader()
130 {
131     char errmsg[1024];
132     bool result = false;
133
134     Close();
135     TIFF* tif = TIFFOpen( m_filename, "r" );
136
137     if( tif )
138     {
139         int width = 0, height = 0, photometric = 0, compression = 0;
140         m_tif = tif;
141
142         if( TIFFRGBAImageOK( tif, errmsg ) &&
143             TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &width ) &&
144             TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &height ) &&
145             TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ) && 
146             (!TIFFGetField( tif, TIFFTAG_COMPRESSION, &compression ) ||
147             (compression != COMPRESSION_LZW &&
148              compression != COMPRESSION_OJPEG)))
149         {
150             m_width = width;
151             m_height = height;
152             m_iscolor = photometric > 1;
153             
154             result = true;
155         }
156     }
157
158     if( !result )
159         Close();
160
161     return result;
162 }
163
164
165 bool  GrFmtTiffReader::ReadData( uchar* data, int step, int color )
166 {
167     bool result = false;
168     uchar* buffer = 0;
169
170     color = color > 0 || (color < 0 && m_iscolor);
171
172     if( m_tif && m_width && m_height )
173     {
174         TIFF* tif = (TIFF*)m_tif;
175         int tile_width0 = m_width, tile_height0 = 0;
176         int x, y, i;
177         int is_tiled = TIFFIsTiled(tif);
178         
179         if( !is_tiled &&
180             TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 ) ||
181             is_tiled &&
182             TIFFGetField( tif, TIFFTAG_TILEWIDTH, &tile_width0 ) &&
183             TIFFGetField( tif, TIFFTAG_TILELENGTH, &tile_height0 ))
184         {
185             if( tile_width0 <= 0 )
186                 tile_width0 = m_width;
187
188             if( tile_height0 <= 0 )
189                 tile_height0 = m_height;
190             
191             buffer = new uchar[tile_height0*tile_width0*4];
192
193             for( y = 0; y < m_height; y += tile_height0, data += step*tile_height0 )
194             {
195                 int tile_height = tile_height0;
196
197                 if( y + tile_height > m_height )
198                     tile_height = m_height - y;
199
200                 for( x = 0; x < m_width; x += tile_width0 )
201                 {
202                     int tile_width = tile_width0, ok;
203
204                     if( x + tile_width > m_width )
205                         tile_width = m_width - x;
206
207                     if( !is_tiled )
208                         ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer );
209                     else
210                         ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer );
211
212                     if( !ok )
213                         goto exit_func;
214
215                     for( i = 0; i < tile_height; i++ )
216                         if( color )
217                             icvCvt_BGRA2BGR_8u_C4C3R( buffer + i*tile_width*4, 0,
218                                           data + x*3 + step*(tile_height - i - 1), 0,
219                                           cvSize(tile_width,1), 2 );
220                         else
221                             icvCvt_BGRA2Gray_8u_C4C1R( buffer + i*tile_width*4, 0,
222                                            data + x + step*(tile_height - i - 1), 0,
223                                            cvSize(tile_width,1), 2 );
224                 }
225             }
226
227             result = true;
228         }
229     }
230
231 exit_func:
232
233     Close();
234     delete[] buffer;
235
236     return result;
237 }
238
239 #else
240
241 static const int  tiffMask[] = { 0xff, 0xff, 0xffffffff, 0xffff, 0xffffffff };
242
243 /************************ TIFF reader *****************************/
244
245 GrFmtTiffReader::GrFmtTiffReader( const char* filename ) : GrFmtReader( filename )
246 {
247     m_offsets = 0;
248     m_maxoffsets = 0;
249     m_strips = -1;
250     m_max_pal_length = 0;
251     m_temp_palette = 0;
252 }
253
254
255 GrFmtTiffReader::~GrFmtTiffReader()
256 {
257     Close();
258
259     delete[] m_offsets;
260     delete[] m_temp_palette;
261 }
262
263 void  GrFmtTiffReader::Close()
264 {
265     m_strm.Close();
266 }
267
268
269 bool  GrFmtTiffReader::CheckFormat( const char* signature )
270 {
271     return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
272            memcmp( signature, fmtSignTiffMM, 4 ) == 0;
273 }
274
275
276 int   GrFmtTiffReader::GetWordEx()
277 {
278     int val = m_strm.GetWord();
279     if( m_byteorder == TIFF_ORDER_MM )
280         val = ((val)>>8)|(((val)&0xff)<<8);
281     return val;
282 }
283
284
285 int   GrFmtTiffReader::GetDWordEx()
286 {
287     int val = m_strm.GetDWord();
288     if( m_byteorder == TIFF_ORDER_MM )
289         val = BSWAP( val );
290     return val;
291 }
292
293
294 int  GrFmtTiffReader::ReadTable( int offset, int count,
295                                  TiffFieldType fieldType,
296                                  int*& array, int& arraysize )
297 {
298     int i;
299     
300     if( count < 0 )
301         return RBS_BAD_HEADER;
302     
303     if( fieldType != TIFF_TYPE_SHORT &&
304         fieldType != TIFF_TYPE_LONG &&
305         fieldType != TIFF_TYPE_BYTE )
306         return RBS_BAD_HEADER;
307
308     if( count > arraysize )
309     {
310         delete[] array;
311         arraysize = arraysize*3/2;
312         if( arraysize < count )
313             arraysize = count;
314         array = new int[arraysize];
315     }
316
317     if( count > 1 )
318     {
319         int pos = m_strm.GetPos();
320         m_strm.SetPos( offset );
321
322         if( fieldType == TIFF_TYPE_LONG )
323         {
324             if( m_byteorder == TIFF_ORDER_MM )
325                 for( i = 0; i < count; i++ )
326                     array[i] = ((RMByteStream&)m_strm).GetDWord();
327             else
328                 for( i = 0; i < count; i++ )
329                     array[i] = ((RLByteStream&)m_strm).GetDWord();
330         }
331         else if( fieldType == TIFF_TYPE_SHORT )
332         {
333             if( m_byteorder == TIFF_ORDER_MM )
334                 for( i = 0; i < count; i++ )
335                     array[i] = ((RMByteStream&)m_strm).GetWord();
336             else
337                 for( i = 0; i < count; i++ )
338                     array[i] = ((RLByteStream&)m_strm).GetWord();
339         }
340         else // fieldType == TIFF_TYPE_BYTE
341             for( i = 0; i < count; i++ )
342                 array[i] = m_strm.GetByte();
343
344         m_strm.SetPos(pos);
345     }
346     else
347     {
348         assert( (offset & ~tiffMask[fieldType]) == 0 );
349         array[0] = offset;
350     }
351
352     return 0;
353 }
354
355
356 bool  GrFmtTiffReader::ReadHeader()
357 {
358     bool result = false;
359     int  photometric = -1;
360     int  channels = 1;
361     int  pal_length = -1;
362
363     const int MAX_CHANNELS = 4;
364     int  bpp_arr[MAX_CHANNELS];
365
366     assert( strlen(m_filename) != 0 );
367     if( !m_strm.Open( m_filename )) return false;
368
369     m_width = -1;
370     m_height = -1;
371     m_strips = -1;
372     m_bpp = 1;
373     m_compression = TIFF_UNCOMP;
374     m_rows_per_strip = -1;
375     m_iscolor = false;
376
377     if( setjmp( m_strm.JmpBuf()) == 0 )
378     {
379         m_byteorder = (TiffByteOrder)m_strm.GetWord();
380         m_strm.Skip( 2 );
381         int header_offset = GetDWordEx();
382
383         m_strm.SetPos( header_offset );
384
385         // read the first tag directory
386         int i, j, count = GetWordEx();
387
388         for( i = 0; i < count; i++ )
389         {
390             // read tag
391             TiffTag tag = (TiffTag)GetWordEx();
392             TiffFieldType fieldType = (TiffFieldType)GetWordEx();
393             int count = GetDWordEx();
394             int value = GetDWordEx();
395             if( count == 1 )
396             {
397                 if( m_byteorder == TIFF_ORDER_MM )
398                 {
399                     if( fieldType == TIFF_TYPE_SHORT )
400                         value = (unsigned)value >> 16;
401                     else if( fieldType == TIFF_TYPE_BYTE )
402                         value = (unsigned)value >> 24;
403                 }
404
405                 value &= tiffMask[fieldType];
406             }
407
408             switch( tag )
409             {
410             case  TIFF_TAG_WIDTH:
411                 m_width = value;
412                 break;
413
414             case  TIFF_TAG_HEIGHT:
415                 m_height = value;
416                 break;
417
418             case  TIFF_TAG_BITS_PER_SAMPLE:
419                 {
420                     int* bpp_arr_ref = bpp_arr;
421
422                     if( count > MAX_CHANNELS )
423                         BAD_HEADER_ERR();
424
425                     if( ReadTable( value, count, fieldType, bpp_arr_ref, count ) < 0 )
426                         BAD_HEADER_ERR();
427                 
428                     for( j = 1; j < count; j++ )
429                     {
430                         if( bpp_arr[j] != bpp_arr[0] )
431                             BAD_HEADER_ERR();
432                     }
433
434                     m_bpp = bpp_arr[0];
435                 }
436
437                 break;
438
439             case  TIFF_TAG_COMPRESSION:
440                 m_compression = (TiffCompression)value;
441                 if( m_compression != TIFF_UNCOMP &&
442                     m_compression != TIFF_HUFFMAN &&
443                     m_compression != TIFF_PACKBITS )
444                     BAD_HEADER_ERR();
445                 break;
446
447             case  TIFF_TAG_PHOTOMETRIC:
448                 photometric = value;
449                 if( (unsigned)photometric > 3 )
450                     BAD_HEADER_ERR();
451                 break;
452
453             case  TIFF_TAG_STRIP_OFFSETS:
454                 m_strips = count;
455                 if( ReadTable( value, count, fieldType, m_offsets, m_maxoffsets ) < 0 )
456                     BAD_HEADER_ERR();
457                 break;
458
459             case  TIFF_TAG_SAMPLES_PER_PIXEL:
460                 channels = value;
461                 if( channels != 1 && channels != 3 && channels != 4 )
462                     BAD_HEADER_ERR();
463                 break;
464
465             case  TIFF_TAG_ROWS_PER_STRIP:
466                 m_rows_per_strip = value;
467                 break;
468
469             case  TIFF_TAG_PLANAR_CONFIG:
470                 {
471                 int planar_config = value;
472                 if( planar_config != 1 )
473                     BAD_HEADER_ERR();
474                 }
475                 break;
476
477             case  TIFF_TAG_COLOR_MAP:
478                 if( fieldType != TIFF_TYPE_SHORT || count < 2 )
479                     BAD_HEADER_ERR();
480                 if( ReadTable( value, count, fieldType,
481                                m_temp_palette, m_max_pal_length ) < 0 )
482                     BAD_HEADER_ERR();
483                 pal_length = count / 3;
484                 if( pal_length > 256 )
485                     BAD_HEADER_ERR();
486                 for( i = 0; i < pal_length; i++ )
487                 {
488                     m_palette[i].r = (uchar)(m_temp_palette[i] >> 8);
489                     m_palette[i].g = (uchar)(m_temp_palette[i + pal_length] >> 8);
490                     m_palette[i].b = (uchar)(m_temp_palette[i + pal_length*2] >> 8);
491                 }
492                 break;
493             case  TIFF_TAG_STRIP_COUNTS:
494                 break;
495             }
496         }
497
498         if( m_strips == 1 && m_rows_per_strip == -1 )
499             m_rows_per_strip = m_height;
500
501         if( m_width > 0 && m_height > 0 && m_strips > 0 &&
502             (m_height + m_rows_per_strip - 1)/m_rows_per_strip == m_strips )
503         {
504             switch( m_bpp )
505             {
506             case 1:
507                 if( photometric == 0 || photometric == 1 && channels == 1 )
508                 {
509                     FillGrayPalette( m_palette, m_bpp, photometric == 0 );
510                     result = true;
511                     m_iscolor = false;
512                 }
513                 break;
514             case 4:
515             case 8:
516                 if( (photometric == 0 || photometric == 1 ||
517                      photometric == 3 && pal_length == (1 << m_bpp)) &&
518                     m_compression != TIFF_HUFFMAN && channels == 1 )
519                 {
520                     if( pal_length < 0 )
521                     {
522                         FillGrayPalette( m_palette, m_bpp, photometric == 0 );
523                         m_iscolor = false;
524                     }
525                     else
526                     {
527                         m_iscolor = IsColorPalette( m_palette, m_bpp );
528                     }
529                     result = true;
530                 }
531                 else if( photometric == 2 && pal_length < 0 &&
532                          (channels == 3 || channels == 4) &&
533                          m_compression == TIFF_UNCOMP )
534                 {
535                     m_bpp = 8*channels;
536                     m_iscolor = true;
537                     result = true;
538                 }
539                 break;
540             default:
541                 BAD_HEADER_ERR();
542             }
543         }
544 bad_header_exit:
545         ;
546     }
547
548     if( !result )
549     {
550         m_strips = -1;
551         m_width = m_height = -1;
552         m_strm.Close();
553     }
554
555     return result;
556 }
557
558
559 bool  GrFmtTiffReader::ReadData( uchar* data, int step, int color )
560 {
561     const  int buffer_size = 1 << 12;
562     uchar  buffer[buffer_size];
563     uchar  gray_palette[256];
564     bool   result = false;
565     uchar* src = buffer;
566     int    src_pitch = (m_width*m_bpp + 7)/8;
567     int    y = 0;
568
569     if( m_strips < 0 || !m_strm.IsOpened())
570         return false;
571     
572     if( src_pitch+32 > buffer_size )
573         src = new uchar[src_pitch+32];
574
575     if( !color )
576         if( m_bpp <= 8 )
577         {
578             CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
579         }
580
581     if( setjmp( m_strm.JmpBuf()) == 0 )
582     {
583         for( int s = 0; s < m_strips; s++ )
584         {
585             int y_limit = m_rows_per_strip;
586
587             y_limit += y;
588             if( y_limit > m_height ) y_limit = m_height;
589
590             m_strm.SetPos( m_offsets[s] );
591
592             if( m_compression == TIFF_UNCOMP )
593             {
594                 for( ; y < y_limit; y++, data += step )
595                 {
596                     m_strm.GetBytes( src, src_pitch );
597                     if( color )
598                         switch( m_bpp )
599                         {
600                         case 1:
601                             FillColorRow1( data, src, m_width, m_palette );
602                             break;
603                         case 4:
604                             FillColorRow4( data, src, m_width, m_palette );
605                             break;
606                         case 8:
607                             FillColorRow8( data, src, m_width, m_palette );
608                             break;
609                         case 24:
610                             icvCvt_RGB2BGR_8u_C3R( src, 0, data, 0, cvSize(m_width,1) );
611                             break;
612                         case 32:
613                             icvCvt_BGRA2BGR_8u_C4C3R( src, 0, data, 0, cvSize(m_width,1), 2 );
614                             break;
615                         default:
616                             assert(0);
617                             goto bad_decoding_end;
618                         }
619                     else
620                         switch( m_bpp )
621                         {
622                         case 1:
623                             FillGrayRow1( data, src, m_width, gray_palette );
624                             break;
625                         case 4:
626                             FillGrayRow4( data, src, m_width, gray_palette );
627                             break;
628                         case 8:
629                             FillGrayRow8( data, src, m_width, gray_palette );
630                             break;
631                         case 24:
632                             icvCvt_BGR2Gray_8u_C3C1R( src, 0, data, 0, cvSize(m_width,1), 2 );
633                             break;
634                         case 32:
635                             icvCvt_BGRA2Gray_8u_C4C1R( src, 0, data, 0, cvSize(m_width,1), 2 );
636                             break;
637                         default:
638                             assert(0);
639                             goto bad_decoding_end;
640                         }
641                 }
642             }
643             else
644             {
645             }
646
647             result = true;
648
649 bad_decoding_end:
650
651             ;
652         }
653     }
654
655     if( src != buffer ) delete[] src; 
656     return result;
657 }
658
659 #endif
660
661 //////////////////////////////////////////////////////////////////////////////////////////
662
663 GrFmtTiffWriter::GrFmtTiffWriter( const char* filename ) : GrFmtWriter( filename )
664 {
665 }
666
667 GrFmtTiffWriter::~GrFmtTiffWriter()
668 {
669 }
670
671 void  GrFmtTiffWriter::WriteTag( TiffTag tag, TiffFieldType fieldType,
672                                  int count, int value )
673 {
674     m_strm.PutWord( tag );
675     m_strm.PutWord( fieldType );
676     m_strm.PutDWord( count );
677     m_strm.PutDWord( value );
678 }
679
680
681 bool  GrFmtTiffWriter::WriteImage( const uchar* data, int step,
682                                    int width, int height, int /*depth*/, int channels )
683 {
684     bool result = false;
685     int fileStep = width*channels;
686
687     assert( data && width > 0 && height > 0 && step >= fileStep);
688
689     if( m_strm.Open( m_filename ) )
690     {
691         int rowsPerStrip = (1 << 13)/fileStep;
692
693         if( rowsPerStrip < 1 )
694             rowsPerStrip = 1;
695
696         if( rowsPerStrip > height )
697             rowsPerStrip = height;
698
699         int i, stripCount = (height + rowsPerStrip - 1) / rowsPerStrip;
700 /*#if defined _DEBUG || !defined WIN32
701         int uncompressedRowSize = rowsPerStrip * fileStep;
702 #endif*/
703         int directoryOffset = 0;
704
705         int* stripOffsets = new int[stripCount];
706         short* stripCounts = new short[stripCount];
707         uchar* buffer = new uchar[fileStep + 32];
708         int  stripOffsetsOffset = 0;
709         int  stripCountsOffset = 0;
710         int  bitsPerSample = 8; // TODO support 16 bit
711         int  y = 0;
712
713         m_strm.PutBytes( fmtSignTiffII, 4 );
714         m_strm.PutDWord( directoryOffset );
715
716         // write an image data first (the most reasonable way
717         // for compressed images)
718         for( i = 0; i < stripCount; i++ )
719         {
720             int limit = y + rowsPerStrip;
721
722             if( limit > height )
723                 limit = height;
724
725             stripOffsets[i] = m_strm.GetPos();
726
727             for( ; y < limit; y++, data += step )
728             {
729                 if( channels == 3 )
730                     icvCvt_BGR2RGB_8u_C3R( data, 0, buffer, 0, cvSize(width,1) );
731                 else if( channels == 4 )
732                     icvCvt_BGRA2RGBA_8u_C4R( data, 0, buffer, 0, cvSize(width,1) );
733
734                 m_strm.PutBytes( channels > 1 ? buffer : data, fileStep );
735             }
736
737             stripCounts[i] = (short)(m_strm.GetPos() - stripOffsets[i]);
738             /*assert( stripCounts[i] == uncompressedRowSize ||
739                     stripCounts[i] < uncompressedRowSize &&
740                     i == stripCount - 1);*/
741         }
742
743         if( stripCount > 2 )
744         {
745             stripOffsetsOffset = m_strm.GetPos();
746             for( i = 0; i < stripCount; i++ )
747                 m_strm.PutDWord( stripOffsets[i] );
748
749             stripCountsOffset = m_strm.GetPos();
750             for( i = 0; i < stripCount; i++ )
751                 m_strm.PutWord( stripCounts[i] );
752         }
753         else if(stripCount == 2)
754         {
755             stripOffsetsOffset = m_strm.GetPos();
756             for (i = 0; i < stripCount; i++)
757             {
758                 m_strm.PutDWord (stripOffsets [i]);
759             }
760             stripCountsOffset = stripCounts [0] + (stripCounts [1] << 16);
761         }
762         else
763         {
764             stripOffsetsOffset = stripOffsets[0];
765             stripCountsOffset = stripCounts[0];
766         }
767
768         if( channels > 1 )
769         {
770             bitsPerSample = m_strm.GetPos();
771             m_strm.PutWord(8);
772             m_strm.PutWord(8);
773             m_strm.PutWord(8);
774             if( channels == 4 )
775                 m_strm.PutWord(8);
776         }
777
778         directoryOffset = m_strm.GetPos();
779
780         // write header
781         m_strm.PutWord( 9 );
782
783         /* warning: specification 5.0 of Tiff want to have tags in
784            ascending order. This is a non-fatal error, but this cause
785            warning with some tools. So, keep this in ascending order */
786
787         WriteTag( TIFF_TAG_WIDTH, TIFF_TYPE_LONG, 1, width );
788         WriteTag( TIFF_TAG_HEIGHT, TIFF_TYPE_LONG, 1, height );
789         WriteTag( TIFF_TAG_BITS_PER_SAMPLE,
790                   TIFF_TYPE_SHORT, channels, bitsPerSample );
791         WriteTag( TIFF_TAG_COMPRESSION, TIFF_TYPE_LONG, 1, TIFF_UNCOMP );
792         WriteTag( TIFF_TAG_PHOTOMETRIC, TIFF_TYPE_SHORT, 1, channels > 1 ? 2 : 1 );
793
794         WriteTag( TIFF_TAG_STRIP_OFFSETS, TIFF_TYPE_LONG,
795                   stripCount, stripOffsetsOffset );
796
797         WriteTag( TIFF_TAG_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, channels );
798         WriteTag( TIFF_TAG_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, rowsPerStrip );
799         
800         WriteTag( TIFF_TAG_STRIP_COUNTS,
801                   stripCount > 1 ? TIFF_TYPE_SHORT : TIFF_TYPE_LONG,
802                   stripCount, stripCountsOffset );
803
804         m_strm.PutDWord(0);
805         m_strm.Close();
806
807         // write directory offset
808         FILE* f = fopen( m_filename, "r+b" );
809         buffer[0] = (uchar)directoryOffset;
810         buffer[1] = (uchar)(directoryOffset >> 8);
811         buffer[2] = (uchar)(directoryOffset >> 16);
812         buffer[3] = (uchar)(directoryOffset >> 24);
813
814         fseek( f, 4, SEEK_SET );
815         fwrite( buffer, 1, 4, f );
816         fclose(f);
817
818         delete[]  stripOffsets;
819         delete[]  stripCounts;
820         delete[] buffer;
821
822         result = true;
823     }
824     return result;
825 }
826