Home | History | Annotate | Download | only in highgui
      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 
    827