Merge pull request #5357 from fxtentacle:ha-2.4.11
[platform/upstream/opencv.git] / modules / highgui / src / 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 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 /****************************************************************************************\
44     A part of the file implements TIFF reader on base of libtiff library
45     (see otherlibs/_graphics/readme.txt for copyright notice)
46 \****************************************************************************************/
47
48 #include <stdarg.h>
49
50 #include "precomp.hpp"
51 #include "grfmt_tiff.hpp"
52
53 #define int64 int64_tiff
54 #define uint64 uint64_tiff
55
56 #ifdef HAVE_TIFF
57 # include "tiff.h"
58 # include "tiffio.h"
59 #endif
60
61 namespace cv
62 {
63 static const char fmtSignTiffII[] = "II\x2a\x00";
64
65 #ifdef HAVE_TIFF
66
67 static const char fmtSignTiffMM[] = "MM\x00\x2a";
68 static int grfmt_tiff_err_handler_init = 0;
69 static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {}
70
71 TiffDecoder::TiffDecoder()
72 {
73     m_tif = 0;
74     if( !grfmt_tiff_err_handler_init )
75     {
76         grfmt_tiff_err_handler_init = 1;
77
78         TIFFSetErrorHandler( GrFmtSilentTIFFErrorHandler );
79         TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler );
80     }
81 }
82
83
84 void TiffDecoder::close()
85 {
86     if( m_tif )
87     {
88         TIFF* tif = (TIFF*)m_tif;
89         TIFFClose( tif );
90         m_tif = 0;
91     }
92 }
93
94 TiffDecoder::~TiffDecoder()
95 {
96     close();
97 }
98
99 size_t TiffDecoder::signatureLength() const
100 {
101     return 4;
102 }
103
104 bool TiffDecoder::checkSignature( const string& signature ) const
105 {
106     return signature.size() >= 4 &&
107         (memcmp(signature.c_str(), fmtSignTiffII, 4) == 0 ||
108         memcmp(signature.c_str(), fmtSignTiffMM, 4) == 0);
109 }
110
111 ImageDecoder TiffDecoder::newDecoder() const
112 {
113     return new TiffDecoder;
114 }
115
116 bool TiffDecoder::readHeader()
117 {
118     bool result = false;
119
120     close();
121     // TIFFOpen() mode flags are different to fopen().  A 'b' in mode "rb" has no effect when reading.
122     // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
123     TIFF* tif = TIFFOpen( m_filename.c_str(), "r" );
124
125     if( tif )
126     {
127         uint32 wdth = 0, hght = 0;
128         uint16 photometric = 0;
129         m_tif = tif;
130
131         if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) &&
132             TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) &&
133             TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ))
134         {
135             uint16 bpp=8, ncn = photometric > 1 ? 3 : 1;
136             TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp );
137             TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn );
138
139             m_width = wdth;
140             m_height = hght;
141             if( bpp > 8 &&
142                ((photometric != 2 && photometric != 1) ||
143                 (ncn != 1 && ncn != 3 && ncn != 4)))
144                 bpp = 8;
145
146             switch(bpp)
147             {
148                 case 8:
149                     m_type = CV_MAKETYPE(CV_8U, photometric > 1 ? 3 : 1);
150                     break;
151                 case 16:
152                     m_type = CV_MAKETYPE(CV_16U, photometric > 1 ? 3 : 1);
153                     break;
154
155                 case 32:
156                     m_type = CV_MAKETYPE(CV_32F, photometric > 1 ? 3 : 1);
157                     break;
158                 case 64:
159                     m_type = CV_MAKETYPE(CV_64F, photometric > 1 ? 3 : 1);
160                     break;
161
162                 default:
163                     result = false;
164             }
165             result = true;
166         }
167     }
168
169     if( !result )
170         close();
171
172     return result;
173 }
174
175
176 bool  TiffDecoder::readData( Mat& img )
177 {
178     bool result = false;
179     bool color = img.channels() > 1;
180     uchar* data = img.data;
181
182     if( img.depth() != CV_8U && img.depth() != CV_16U && img.depth() != CV_32F && img.depth() != CV_64F )
183         return false;
184
185     if( m_tif && m_width && m_height )
186     {
187         TIFF* tif = (TIFF*)m_tif;
188         uint32 tile_width0 = m_width, tile_height0 = 0;
189         int x, y, i;
190         int is_tiled = TIFFIsTiled(tif);
191         uint16 photometric;
192         TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric );
193         uint16 bpp = 8, ncn = photometric > 1 ? 3 : 1;
194         TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp );
195         TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn );
196         const int bitsPerByte = 8;
197         int dst_bpp = (int)(img.elemSize1() * bitsPerByte);
198
199         if(dst_bpp == 8)
200         {
201             char errmsg[1024];
202             if(!TIFFRGBAImageOK( tif, errmsg ))
203             {
204                 close();
205                 return false;
206             }
207         }
208
209         if( (!is_tiled) ||
210             (is_tiled &&
211             TIFFGetField( tif, TIFFTAG_TILEWIDTH, &tile_width0 ) &&
212             TIFFGetField( tif, TIFFTAG_TILELENGTH, &tile_height0 )))
213         {
214             if(!is_tiled)
215                 TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 );
216
217             if( tile_width0 <= 0 )
218                 tile_width0 = m_width;
219
220             if( tile_height0 <= 0 ||
221                 (!is_tiled && tile_height0 == std::numeric_limits<uint32>::max()) )
222                 tile_height0 = m_height;
223
224             if(dst_bpp == 8) {
225                 // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
226                 bpp = 8;
227                 ncn = 4;
228             }
229             const size_t buffer_size = (bpp/bitsPerByte) * ncn * tile_height0 * tile_width0;
230             AutoBuffer<uchar> _buffer( buffer_size );
231             uchar* buffer = _buffer;
232             ushort* buffer16 = (ushort*)buffer;
233             float* buffer32 = (float*)buffer;
234             double* buffer64 = (double*)buffer;
235             int tileidx = 0;
236
237             for( y = 0; y < m_height; y += tile_height0, data += img.step*tile_height0 )
238             {
239                 int tile_height = tile_height0;
240
241                 if( y + tile_height > m_height )
242                     tile_height = m_height - y;
243
244                 for( x = 0; x < m_width; x += tile_width0, tileidx++ )
245                 {
246                     int tile_width = tile_width0, ok;
247
248                     if( x + tile_width > m_width )
249                         tile_width = m_width - x;
250
251                     switch(dst_bpp)
252                     {
253                         case 8:
254                         {
255                             uchar * bstart = buffer;
256                             if( !is_tiled )
257                                 ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer );
258                             else
259                             {
260                                 ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer );
261                                 //Tiles fill the buffer from the bottom up
262                                 bstart += (tile_height0 - tile_height) * tile_width0 * 4;
263                             }
264                             if( !ok )
265                             {
266                                 close();
267                                 return false;
268                             }
269
270                             for( i = 0; i < tile_height; i++ )
271                                 if( color )
272                                     icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0,
273                                                              data + x*3 + img.step*(tile_height - i - 1), 0,
274                                                              cvSize(tile_width,1), 2 );
275                                 else
276                                     icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0,
277                                                               data + x + img.step*(tile_height - i - 1), 0,
278                                                               cvSize(tile_width,1), 2 );
279                             break;
280                         }
281
282                         case 16:
283                         {
284                             if( !is_tiled )
285                                 ok = (int)TIFFReadEncodedStrip( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0;
286                             else
287                                 ok = (int)TIFFReadEncodedTile( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0;
288
289                             if( !ok )
290                             {
291                                 close();
292                                 return false;
293                             }
294
295                             for( i = 0; i < tile_height; i++ )
296                             {
297                                 if( color )
298                                 {
299                                     if( ncn == 1 )
300                                     {
301                                         icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width0*ncn, 0,
302                                                                   (ushort*)(data + img.step*i) + x*3, 0,
303                                                                   cvSize(tile_width,1) );
304                                     }
305                                     else if( ncn == 3 )
306                                     {
307                                         icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0,
308                                                                (ushort*)(data + img.step*i) + x*3, 0,
309                                                                cvSize(tile_width,1) );
310                                     }
311                                     else
312                                     {
313                                         icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0,
314                                                                (ushort*)(data + img.step*i) + x*3, 0,
315                                                                cvSize(tile_width,1), 2 );
316                                     }
317                                 }
318                                 else
319                                 {
320                                     if( ncn == 1 )
321                                     {
322                                         memcpy((ushort*)(data + img.step*i)+x,
323                                                buffer16 + i*tile_width0*ncn,
324                                                tile_width*sizeof(buffer16[0]));
325                                     }
326                                     else
327                                     {
328                                         icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0,
329                                                                (ushort*)(data + img.step*i) + x, 0,
330                                                                cvSize(tile_width,1), ncn, 2 );
331                                     }
332                                 }
333                             }
334                             break;
335                         }
336
337                         case 32:
338                         case 64:
339                         {
340                             if( !is_tiled )
341                                 ok = (int)TIFFReadEncodedStrip( tif, tileidx, buffer, buffer_size ) >= 0;
342                             else
343                                 ok = (int)TIFFReadEncodedTile( tif, tileidx, buffer, buffer_size ) >= 0;
344
345                             if( !ok || ncn != 1 )
346                             {
347                                 close();
348                                 return false;
349                             }
350
351                             for( i = 0; i < tile_height; i++ )
352                             {
353                                 if(dst_bpp == 32)
354                                 {
355                                     memcpy((float*)(data + img.step*i)+x,
356                                            buffer32 + i*tile_width0*ncn,
357                                            tile_width*sizeof(buffer32[0]));
358                                 }
359                                 else
360                                 {
361                                     memcpy((double*)(data + img.step*i)+x,
362                                          buffer64 + i*tile_width0*ncn,
363                                          tile_width*sizeof(buffer64[0]));
364                                 }
365                             }
366
367                             break;
368                         }
369                         default:
370                         {
371                             close();
372                             return false;
373                         }
374                     }
375                 }
376             }
377
378             result = true;
379         }
380     }
381
382     close();
383     return result;
384 }
385
386 #endif
387
388 //////////////////////////////////////////////////////////////////////////////////////////
389
390 TiffEncoder::TiffEncoder()
391 {
392     m_description = "TIFF Files (*.tiff;*.tif)";
393 #ifdef HAVE_TIFF
394     m_buf_supported = false;
395 #else
396     m_buf_supported = true;
397 #endif
398 }
399
400 TiffEncoder::~TiffEncoder()
401 {
402 }
403
404 ImageEncoder TiffEncoder::newEncoder() const
405 {
406     return new TiffEncoder;
407 }
408
409 bool TiffEncoder::isFormatSupported( int depth ) const
410 {
411     return depth == CV_8U || depth == CV_16U;
412 }
413
414 void  TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag,
415                              TiffFieldType fieldType,
416                              int count, int value )
417 {
418     strm.putWord( tag );
419     strm.putWord( fieldType );
420     strm.putDWord( count );
421     strm.putDWord( value );
422 }
423
424 #ifdef HAVE_TIFF
425
426 static void readParam(const vector<int>& params, int key, int& value)
427 {
428     for(size_t i = 0; i + 1 < params.size(); i += 2)
429         if(params[i] == key)
430         {
431             value = params[i+1];
432             break;
433         }
434 }
435
436 bool  TiffEncoder::writeLibTiff( const Mat& img, const vector<int>& params)
437 {
438     int channels = img.channels();
439     int width = img.cols, height = img.rows;
440     int depth = img.depth();
441
442     int bitsPerChannel = -1;
443     switch (depth)
444     {
445         case CV_8U:
446         {
447             bitsPerChannel = 8;
448             break;
449         }
450         case CV_16U:
451         {
452             bitsPerChannel = 16;
453             break;
454         }
455         default:
456         {
457             return false;
458         }
459     }
460
461     const int bitsPerByte = 8;
462     size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte;
463
464     int rowsPerStrip = (int)((1 << 13)/fileStep);
465     readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
466
467     if( rowsPerStrip < 1 )
468         rowsPerStrip = 1;
469
470     if( rowsPerStrip > height )
471         rowsPerStrip = height;
472
473
474     // do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode.
475     // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
476     TIFF* pTiffHandle = TIFFOpen(m_filename.c_str(), "w");
477     if (!pTiffHandle)
478     {
479         return false;
480     }
481
482     // defaults for now, maybe base them on params in the future
483     int   compression  = COMPRESSION_LZW;
484     int   predictor    = PREDICTOR_HORIZONTAL;
485
486     readParam(params, TIFFTAG_COMPRESSION, compression);
487     readParam(params, TIFFTAG_PREDICTOR, predictor);
488
489     int   colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
490
491     if ( !TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width)
492       || !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height)
493       || !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel)
494       || !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression)
495       || !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace)
496       || !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels)
497       || !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)
498       || !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip)
499        )
500     {
501         TIFFClose(pTiffHandle);
502         return false;
503     }
504
505     if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor) )
506     {
507         TIFFClose(pTiffHandle);
508         return false;
509     }
510
511     // row buffer, because TIFFWriteScanline modifies the original data!
512     size_t scanlineSize = TIFFScanlineSize(pTiffHandle);
513     AutoBuffer<uchar> _buffer(scanlineSize+32);
514     uchar* buffer = _buffer;
515     if (!buffer)
516     {
517         TIFFClose(pTiffHandle);
518         return false;
519     }
520
521     for (int y = 0; y < height; ++y)
522     {
523         switch(channels)
524         {
525             case 1:
526             {
527                 memcpy(buffer, img.data + img.step * y, scanlineSize);
528                 break;
529             }
530
531             case 3:
532             {
533                 if (depth == CV_8U)
534                     icvCvt_BGR2RGB_8u_C3R( img.data + img.step*y, 0, buffer, 0, cvSize(width,1) );
535                 else
536                     icvCvt_BGR2RGB_16u_C3R( (const ushort*)(img.data + img.step*y), 0, (ushort*)buffer, 0, cvSize(width,1) );
537                 break;
538             }
539
540             case 4:
541             {
542                 if (depth == CV_8U)
543                     icvCvt_BGRA2RGBA_8u_C4R( img.data + img.step*y, 0, buffer, 0, cvSize(width,1) );
544                 else
545                     icvCvt_BGRA2RGBA_16u_C4R( (const ushort*)(img.data + img.step*y), 0, (ushort*)buffer, 0, cvSize(width,1) );
546                 break;
547             }
548
549             default:
550             {
551                 TIFFClose(pTiffHandle);
552                 return false;
553             }
554         }
555
556         int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0);
557         if (writeResult != 1)
558         {
559             TIFFClose(pTiffHandle);
560             return false;
561         }
562     }
563
564     TIFFClose(pTiffHandle);
565     return true;
566 }
567
568 #endif
569
570 #ifdef HAVE_TIFF
571 bool  TiffEncoder::write( const Mat& img, const vector<int>& params)
572 #else
573 bool  TiffEncoder::write( const Mat& img, const vector<int>& /*params*/)
574 #endif
575 {
576     int channels = img.channels();
577     int width = img.cols, height = img.rows;
578     int depth = img.depth();
579
580     if (depth != CV_8U && depth != CV_16U)
581         return false;
582
583     int bytesPerChannel = depth == CV_8U ? 1 : 2;
584     int fileStep = width * channels * bytesPerChannel;
585
586     WLByteStream strm;
587
588     if( m_buf )
589     {
590         if( !strm.open(*m_buf) )
591             return false;
592     }
593     else
594     {
595 #ifdef HAVE_TIFF
596       return writeLibTiff(img, params);
597 #else
598       if( !strm.open(m_filename) )
599           return false;
600 #endif
601     }
602
603     int rowsPerStrip = (1 << 13)/fileStep;
604
605     if( rowsPerStrip < 1 )
606         rowsPerStrip = 1;
607
608     if( rowsPerStrip > height )
609         rowsPerStrip = height;
610
611     int i, stripCount = (height + rowsPerStrip - 1) / rowsPerStrip;
612
613     if( m_buf )
614         m_buf->reserve( alignSize(stripCount*8 + fileStep*height + 256, 256) );
615
616 /*#if defined _DEBUG || !defined WIN32
617     int uncompressedRowSize = rowsPerStrip * fileStep;
618 #endif*/
619     int directoryOffset = 0;
620
621     AutoBuffer<int> stripOffsets(stripCount);
622     AutoBuffer<short> stripCounts(stripCount);
623     AutoBuffer<uchar> _buffer(fileStep+32);
624     uchar* buffer = _buffer;
625     int  stripOffsetsOffset = 0;
626     int  stripCountsOffset = 0;
627     int  bitsPerSample = 8 * bytesPerChannel;
628     int  y = 0;
629
630     strm.putBytes( fmtSignTiffII, 4 );
631     strm.putDWord( directoryOffset );
632
633     // write an image data first (the most reasonable way
634     // for compressed images)
635     for( i = 0; i < stripCount; i++ )
636     {
637         int limit = y + rowsPerStrip;
638
639         if( limit > height )
640             limit = height;
641
642         stripOffsets[i] = strm.getPos();
643
644         for( ; y < limit; y++ )
645         {
646             if( channels == 3 )
647             {
648                 if (depth == CV_8U)
649                     icvCvt_BGR2RGB_8u_C3R( img.data + img.step*y, 0, buffer, 0, cvSize(width,1) );
650                 else
651                     icvCvt_BGR2RGB_16u_C3R( (const ushort*)(img.data + img.step*y), 0, (ushort*)buffer, 0, cvSize(width,1) );
652             }
653             else
654             {
655               if( channels == 4 )
656               {
657                 if (depth == CV_8U)
658                     icvCvt_BGRA2RGBA_8u_C4R( img.data + img.step*y, 0, buffer, 0, cvSize(width,1) );
659                 else
660                     icvCvt_BGRA2RGBA_16u_C4R( (const ushort*)(img.data + img.step*y), 0, (ushort*)buffer, 0, cvSize(width,1) );
661               }
662             }
663
664             strm.putBytes( channels > 1 ? buffer : img.data + img.step*y, fileStep );
665         }
666
667         stripCounts[i] = (short)(strm.getPos() - stripOffsets[i]);
668         /*assert( stripCounts[i] == uncompressedRowSize ||
669                 stripCounts[i] < uncompressedRowSize &&
670                 i == stripCount - 1);*/
671     }
672
673     if( stripCount > 2 )
674     {
675         stripOffsetsOffset = strm.getPos();
676         for( i = 0; i < stripCount; i++ )
677             strm.putDWord( stripOffsets[i] );
678
679         stripCountsOffset = strm.getPos();
680         for( i = 0; i < stripCount; i++ )
681             strm.putWord( stripCounts[i] );
682     }
683     else if(stripCount == 2)
684     {
685         stripOffsetsOffset = strm.getPos();
686         for (i = 0; i < stripCount; i++)
687         {
688             strm.putDWord (stripOffsets [i]);
689         }
690         stripCountsOffset = stripCounts [0] + (stripCounts [1] << 16);
691     }
692     else
693     {
694         stripOffsetsOffset = stripOffsets[0];
695         stripCountsOffset = stripCounts[0];
696     }
697
698     if( channels > 1 )
699     {
700         int bitsPerSamplePos = strm.getPos();
701         strm.putWord(bitsPerSample);
702         strm.putWord(bitsPerSample);
703         strm.putWord(bitsPerSample);
704         if( channels == 4 )
705             strm.putWord(bitsPerSample);
706         bitsPerSample = bitsPerSamplePos;
707     }
708
709     directoryOffset = strm.getPos();
710
711     // write header
712     strm.putWord( 9 );
713
714     /* warning: specification 5.0 of Tiff want to have tags in
715        ascending order. This is a non-fatal error, but this cause
716        warning with some tools. So, keep this in ascending order */
717
718     writeTag( strm, TIFF_TAG_WIDTH, TIFF_TYPE_LONG, 1, width );
719     writeTag( strm, TIFF_TAG_HEIGHT, TIFF_TYPE_LONG, 1, height );
720     writeTag( strm, TIFF_TAG_BITS_PER_SAMPLE,
721               TIFF_TYPE_SHORT, channels, bitsPerSample );
722     writeTag( strm, TIFF_TAG_COMPRESSION, TIFF_TYPE_LONG, 1, TIFF_UNCOMP );
723     writeTag( strm, TIFF_TAG_PHOTOMETRIC, TIFF_TYPE_SHORT, 1, channels > 1 ? 2 : 1 );
724
725     writeTag( strm, TIFF_TAG_STRIP_OFFSETS, TIFF_TYPE_LONG,
726               stripCount, stripOffsetsOffset );
727
728     writeTag( strm, TIFF_TAG_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, channels );
729     writeTag( strm, TIFF_TAG_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, rowsPerStrip );
730
731     writeTag( strm, TIFF_TAG_STRIP_COUNTS,
732               stripCount > 1 ? TIFF_TYPE_SHORT : TIFF_TYPE_LONG,
733               stripCount, stripCountsOffset );
734
735     strm.putDWord(0);
736     strm.close();
737
738     if( m_buf )
739     {
740         (*m_buf)[4] = (uchar)directoryOffset;
741         (*m_buf)[5] = (uchar)(directoryOffset >> 8);
742         (*m_buf)[6] = (uchar)(directoryOffset >> 16);
743         (*m_buf)[7] = (uchar)(directoryOffset >> 24);
744     }
745     else
746     {
747         // write directory offset
748         FILE* f = fopen( m_filename.c_str(), "r+b" );
749         buffer[0] = (uchar)directoryOffset;
750         buffer[1] = (uchar)(directoryOffset >> 8);
751         buffer[2] = (uchar)(directoryOffset >> 16);
752         buffer[3] = (uchar)(directoryOffset >> 24);
753
754         fseek( f, 4, SEEK_SET );
755         fwrite( buffer, 1, 4, f );
756         fclose(f);
757     }
758
759     return true;
760 }
761
762 }