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
50static const char fmtSignTiffII[] = "II\x2a\x00";
51static const char fmtSignTiffMM[] = "MM\x00\x2a";
52
53GrFmtTiff::GrFmtTiff()
54{
55    m_sign_len = 4;
56    m_signature = "";
57    m_description = "TIFF Files (*.tiff;*.tif)";
58}
59
60GrFmtTiff::~GrFmtTiff()
61{
62}
63
64bool GrFmtTiff::CheckSignature( const char* signature )
65{
66    return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
67           memcmp( signature, fmtSignTiffMM, 4 ) == 0;
68}
69
70
71GrFmtReader* GrFmtTiff::NewReader( const char* filename )
72{
73    return new GrFmtTiffReader( filename );
74}
75
76
77GrFmtWriter* 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
88static int grfmt_tiff_err_handler_init = 0;
89
90static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {}
91
92GrFmtTiffReader::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
106GrFmtTiffReader::~GrFmtTiffReader()
107{
108}
109
110
111void  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
122bool  GrFmtTiffReader::CheckFormat( const char* signature )
123{
124    return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
125           memcmp( signature, fmtSignTiffMM, 4 ) == 0;
126}
127
128
129bool  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
165bool  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
231exit_func:
232
233    Close();
234    delete[] buffer;
235
236    return result;
237}
238
239#else
240
241static const int  tiffMask[] = { 0xff, 0xff, 0xffffffff, 0xffff, 0xffffffff };
242
243/************************ TIFF reader *****************************/
244
245GrFmtTiffReader::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
255GrFmtTiffReader::~GrFmtTiffReader()
256{
257    Close();
258
259    delete[] m_offsets;
260    delete[] m_temp_palette;
261}
262
263void  GrFmtTiffReader::Close()
264{
265    m_strm.Close();
266}
267
268
269bool  GrFmtTiffReader::CheckFormat( const char* signature )
270{
271    return memcmp( signature, fmtSignTiffII, 4 ) == 0 ||
272           memcmp( signature, fmtSignTiffMM, 4 ) == 0;
273}
274
275
276int   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
285int   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
294int  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
356bool  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        }
544bad_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
559bool  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
649bad_decoding_end:
650
651            ;
652        }
653    }
654
655    if( src != buffer ) delete[] src;
656    return result;
657}
658
659#endif
660
661//////////////////////////////////////////////////////////////////////////////////////////
662
663GrFmtTiffWriter::GrFmtTiffWriter( const char* filename ) : GrFmtWriter( filename )
664{
665}
666
667GrFmtTiffWriter::~GrFmtTiffWriter()
668{
669}
670
671void  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
681bool  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
827