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