1 //---
2 //
3 // License: MIT
4 //
5 // Author:  David Burken
6 //
7 // Description: Generic image writer class.
8 //
9 //---
10 // $Id$
11 
12 #include <ossim/imaging/ossimWriter.h>
13 #include <ossim/base/ossimBooleanProperty.h>
14 #include <ossim/base/ossimCommon.h>
15 #include <ossim/base/ossimKeywordNames.h>
16 #include <ossim/base/ossimProperty.h>
17 #include <ossim/base/ossimStringProperty.h>
18 #include <ossim/base/ossimRefPtr.h>
19 #include <ossim/base/ossimTiffConstants.h>
20 #include <ossim/base/ossimTrace.h>
21 #include <ossim/imaging/ossimImageGeometry.h>
22 #include <ossim/projection/ossimMapProjection.h>
23 #include <ossim/projection/ossimProjection.h>
24 
25 #include <limits>
26 #include <ostream>
27 
28 using namespace std;
29 
30 static const std::string ALIGN_TILES_KW         = "align_tiles";         // bool
31 static const std::string BLOCK_SIZE_KW          = "block_size";          // unsigned int
32 static const std::string FLUSH_TILES_KW         = "flush_tiles";         // bool
33 static const std::string INCLUDE_BLANK_TILES_KW = "include_blank_tiles"; // bool
34 static const std::string TILE_SIZE_KW           = "tile_size";           // (x,y) in pixels
35 static const std::string TRUE_KW                = "true";
36 
37 static const ossimTrace traceDebug("ossimWriter:debug");
38 
ossimWriter()39 ossimWriter::ossimWriter()
40    : ossimImageFileWriter(),
41      m_str(0),
42      m_ownsStreamFlag(false),
43      m_kwl(new ossimKeywordlist()),
44      m_outputTileSize(OSSIM_DEFAULT_TILE_WIDTH, OSSIM_DEFAULT_TILE_HEIGHT)
45 {
46    // Set default options:
47    ossim::defaultTileSize(m_outputTileSize);
48 
49    m_kwl->addPair( ALIGN_TILES_KW, TRUE_KW );
50    m_kwl->addPair( BLOCK_SIZE_KW, "4096" );
51    m_kwl->addPair( FLUSH_TILES_KW, TRUE_KW );
52    m_kwl->addPair( INCLUDE_BLANK_TILES_KW, TRUE_KW );
53    m_kwl->addPair( TILE_SIZE_KW, m_outputTileSize.toString().string() );
54 }
55 
~ossimWriter()56 ossimWriter::~ossimWriter()
57 {
58    close();
59 
60    // Not a leak, ref ptr.
61    m_kwl = 0;
62 }
63 
close()64 void ossimWriter::close()
65 {
66    if (m_str)
67    {
68       m_str->flush();
69 
70       if (m_ownsStreamFlag)
71       {
72          delete m_str;
73          m_str = 0;
74          m_ownsStreamFlag = false;
75       }
76    }
77 }
78 
getShortName() const79 ossimString ossimWriter::getShortName() const
80 {
81    return ossimString("ossim_writer");
82 }
83 
getLongName() const84 ossimString ossimWriter::getLongName() const
85 {
86    return ossimString("ossim writer");
87 }
88 
getClassName() const89 ossimString ossimWriter::getClassName() const
90 {
91    return ossimString("ossimWriter");
92 }
93 
getExtension() const94 ossimString ossimWriter::getExtension() const
95 {
96    ossimString result = "";
97    if ( theOutputImageType == "ossim_ttbs" ) // tiled tiff band separate
98    {
99       result = "tif";
100    }
101    return result;
102 }
103 
getImageTypeList(std::vector<ossimString> & imageTypeList) const104 void ossimWriter::getImageTypeList(std::vector<ossimString>& imageTypeList) const
105 {
106    // imageTypeList.push_back(ossimString("tiff_tiled_band_separate"));
107    imageTypeList.push_back(ossimString("ossim_ttbs")); // tmp drb
108 }
109 
isOpen() const110 bool ossimWriter::isOpen() const
111 {
112    return ( m_str ? true : false );
113 }
114 
open()115 bool ossimWriter::open()
116 {
117    bool status = false;
118 
119    close();
120 
121    if ( theFilename.size() && hasImageType( theOutputImageType ) )
122    {
123       std::ofstream* os = new std::ofstream();
124       os->open( theFilename.c_str(), ios::out | ios::binary );
125       if( os->is_open() )
126       {
127          m_str = os;
128          m_ownsStreamFlag = true;
129          status = true;
130       }
131       else
132       {
133          delete os;
134          os = 0;
135       }
136    }
137 
138    return status;
139 }
140 
hasImageType(const ossimString & imageType) const141 bool ossimWriter::hasImageType(const ossimString& imageType) const
142 {
143    bool result = false;
144    if ( (imageType == "ossim_ttbs") || (imageType == "image/tif") )
145    {
146       result = true;
147    }
148    return result;
149 }
150 
writeFile()151 bool ossimWriter::writeFile()
152 {
153    bool status = true;
154 
155    if( theInputConnection.valid() &&
156        (getErrorStatus() == ossimErrorCodes::OSSIM_OK) )
157    {
158       //---
159       // Check for stream if master process.
160       // Note only the master process is used for writing...
161       //---
162       if( theInputConnection->isMaster() )
163       {
164          if (!isOpen())
165          {
166             status = open();
167          }
168       }
169 
170       if ( status )
171       {
172          status = writeStream();
173 
174          // Flush and close the stream.
175          close();
176       }
177    }
178 
179    return status;
180 }
181 
writeStream()182 bool ossimWriter::writeStream()
183 {
184    //---
185    // This can be called publically so we must to the same checks as the
186    // writeFile method.
187    //---
188    bool status = false;
189 
190    // Must have a sequencer...
191    if( theInputConnection.valid() )
192    {
193       if ( isOpen() )
194       {
195          if ( theOutputImageType == "ossim_ttbs" )
196          {
197             if ( (theInputConnection->getTileWidth()  !=
198                   static_cast<ossim_uint32>(m_outputTileSize.x)) ||
199                  (theInputConnection->getTileHeight() !=
200                   static_cast<ossim_uint32>(m_outputTileSize.y)) )
201             {
202                theInputConnection->setTileSize(m_outputTileSize);
203             }
204 
205             status = writeStreamTtbs();
206          }
207       }
208    }
209 
210    return status;
211 }
212 
writeStreamTtbs()213 bool ossimWriter::writeStreamTtbs()
214 {
215    // Alway big tiff in native byte order.
216 
217    bool status = false;
218 
219    if ( writeTiffHdr() == true )
220    {
221       std::vector<ossim_uint64>  tile_offsets;
222       std::vector<ossim_uint64>  tile_byte_counts;
223 
224       //---
225       // Min/max arrays must start off empty for
226       // ossimImageData::computeMinMaxPix code.
227       //---
228       std::vector<ossim_float64> minBands(0);
229       std::vector<ossim_float64> maxBands(0);
230 
231       if ( writeTiffTilesBandSeparate(
232               tile_offsets, tile_byte_counts, minBands, maxBands   ) == true )
233       {
234          status = writeTiffTags( tile_offsets, tile_byte_counts, minBands, maxBands );
235       }
236    }
237 
238    return status;
239 }
240 
writeTiffHdr()241 bool ossimWriter::writeTiffHdr()
242 {
243    //---
244    // First two bytes, byte order indication.
245    // "MM"(big endian) or "II"(little endian.
246    //---
247    std::string s;
248    if ( ossim::byteOrder() == OSSIM_LITTLE_ENDIAN )
249    {
250       s = "II";
251    }
252    else
253    {
254       s = "MM";
255    }
256    m_str->write( s.c_str(), 2 );
257 
258    // Version, 42=classic tiff, 43=big tiff.
259    ossim_uint16 us16 = 43;
260    m_str->write( (const char*)&us16, 2 );
261 
262    // Byte size of offsets.
263    us16 = 8;
264    m_str->write( (const char*)&us16, 2 );
265 
266    // Always 0:
267    us16 = 0;
268    m_str->write( (const char*)&us16, 2 );
269 
270    // Offset to the IFD(image file directory).
271    ossim_uint64 ul64 = 16;
272    m_str->write( (const char*)&ul64, 8 );
273 
274    return m_str->good();
275 }
276 
writeTiffTags(const std::vector<ossim_uint64> & tile_offsets,const std::vector<ossim_uint64> & tile_byte_counts,const std::vector<ossim_float64> & minBands,const std::vector<ossim_float64> & maxBands)277 bool ossimWriter::writeTiffTags( const std::vector<ossim_uint64>& tile_offsets,
278                                  const std::vector<ossim_uint64>& tile_byte_counts,
279                                  const std::vector<ossim_float64>& minBands,
280                                  const std::vector<ossim_float64>& maxBands )
281 {
282    bool status = false;
283 
284    ossimRefPtr<ossimMapProjection> mapProj = 0;
285    ossimRefPtr<ossimImageGeometry> geom = theInputConnection->getImageGeometry();
286    if ( geom.valid() )
287    {
288       ossimRefPtr<ossimProjection> proj = geom->getProjection();
289       mapProj = dynamic_cast<ossimMapProjection*>( proj.get() );
290    }
291 
292    // Seek to the IFD.
293    m_str->seekp( 16, std::ios_base::beg );
294 
295    // tag count, this will be rewritten at the end:
296    ossim_uint64 tagCount = 0;
297    m_str->write( (const char*)&tagCount, 8 );
298 
299    //---
300    // This is where the tile offsets, tile byte counts and arrays bytes are
301    // written. Starting at byte position 512 which gives from
302    // 16 -> 512(496 bytes) to write tags.
303    //---
304    std::streamoff arrayWritePos = 512;
305 
306    // Used throughout:
307    ossim_uint16 tag;
308    ossim_uint16 type;
309    ossim_uint64 count;
310    ossim_uint16 value_ui16;
311    ossim_uint32 value_ui32;
312 
313    // image width tag 256:
314    tag   = ossim::OTIFFTAG_IMAGEWIDTH;
315    count = 1;
316    if ( theAreaOfInterest.width() <= OSSIM_DEFAULT_MAX_PIX_UINT16 )
317    {
318       type = ossim::OTIFF_SHORT;
319       value_ui16 = (ossim_uint16)theAreaOfInterest.width();
320       writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
321    }
322    else
323    {
324       type = ossim::OTIFF_LONG;
325       value_ui32 = theAreaOfInterest.width();
326       writeTiffTag<ossim_uint32>( tag, type, count, &value_ui32, arrayWritePos );
327    }
328    ++tagCount;
329 
330    // image length tag 257:
331    tag   = ossim::OTIFFTAG_IMAGELENGTH;
332    count = 1;
333    if ( theAreaOfInterest.height() <= OSSIM_DEFAULT_MAX_PIX_UINT16 )
334    {
335       type = ossim::OTIFF_SHORT;
336       value_ui16 = (ossim_uint16)theAreaOfInterest.height();
337       writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
338    }
339    else
340    {
341       type = ossim::OTIFF_LONG;
342       value_ui32 = theAreaOfInterest.height();
343       writeTiffTag<ossim_uint32>( tag, type, count, &value_ui32, arrayWritePos );
344    }
345    ++tagCount;
346 
347    // bits per sample tag 258:
348    tag   = ossim::OTIFFTAG_BITSPERSAMPLE;
349    count = theInputConnection->getNumberOfOutputBands();
350    type  = ossim::OTIFF_SHORT;
351    value_ui16 = (ossim_uint16)ossim::getBitsPerPixel( theInputConnection->getOutputScalarType() );
352    if ( count == 1 )
353    {
354       writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
355    }
356    else
357    {
358       std::vector<ossim_uint16> v(count, value_ui16);
359       writeTiffTag<ossim_uint16>( tag, type, count, &v.front(), arrayWritePos );
360    }
361    ++tagCount;
362 
363    // compression tag 259:
364    tag   = ossim::OTIFFTAG_COMPRESSION;
365    type  = ossim::OTIFF_SHORT;
366    count = 1;
367    value_ui16 = ossim::COMPRESSION_NONE; // tmp only uncompressed supported.
368    writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
369    ++tagCount;
370 
371    // photo interpretation tag 262:
372    tag   = ossim::OTIFFTAG_PHOTOMETRIC;
373    type  = ossim::OTIFF_SHORT;
374    count = 1;
375    if ( theInputConnection->getNumberOfOutputBands() == 3 )
376    {
377       value_ui16 = ossim::OPHOTO_RGB;
378    }
379    else
380    {
381       value_ui16 = ossim::OPHOTO_MINISBLACK;
382    }
383    writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
384    ++tagCount;
385 
386    // samples per pixel tag 277:
387    tag   = ossim::OTIFFTAG_SAMPLESPERPIXEL;
388    type  = ossim::OTIFF_SHORT;
389    count = 1;
390    value_ui16 = theInputConnection->getNumberOfOutputBands();
391    writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
392    ++tagCount;
393 
394    // Writes two tags 280 and 281:
395    if ( writeMinMaxTiffTags( arrayWritePos ) == true )
396    {
397       tagCount += 2;
398    }
399 
400    // planar conf tag 284:
401    tag   = ossim::OTIFFTAG_PLANARCONFIG;
402    type  = ossim::OTIFF_SHORT;
403    count = 1;
404    value_ui16 = ossim::OTIFFTAG_PLANARCONFIG_SEPARATE;
405    writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
406    ++tagCount;
407 
408    if ( isTiled() )
409    {
410       // tile width tag 322:
411       tag   = ossim::OTIFFTAG_TILEWIDTH;
412       count = 1;
413       if (  m_outputTileSize.x <= OSSIM_DEFAULT_MAX_PIX_UINT16 )
414       {
415          type = ossim::OTIFF_SHORT;
416          value_ui16 = (ossim_uint16)m_outputTileSize.x;
417          writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
418       }
419       else
420       {
421          type = ossim::OTIFF_LONG;
422          value_ui32 = (ossim_uint32)m_outputTileSize.x;
423          writeTiffTag<ossim_uint32>( tag, type, count, &value_ui32, arrayWritePos );
424       }
425       ++tagCount;
426 
427       // tile length tag 323:
428       tag   = ossim::OTIFFTAG_TILELENGTH;
429       count = 1;
430       if (  m_outputTileSize.y <= OSSIM_DEFAULT_MAX_PIX_UINT16 )
431       {
432          type = ossim::OTIFF_SHORT;
433          value_ui16 = (ossim_uint16)m_outputTileSize.y;
434          writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
435       }
436       else
437       {
438          type = ossim::OTIFF_LONG;
439          value_ui32 = (ossim_uint32)m_outputTileSize.y;
440          writeTiffTag<ossim_uint32>( tag, type, count, &value_ui32, arrayWritePos );
441       }
442       ++tagCount;
443    }
444 
445    // tile offsets tag 324:
446    tag   = ossim::OTIFFTAG_TILEOFFSETS;
447    count = tile_offsets.size();
448    type  = ossim::OTIFF_LONG8;
449    writeTiffTag<ossim_uint64>( tag, type, count, &tile_offsets.front(), arrayWritePos );
450    ++tagCount;
451 
452    // tile byte counts tag 325:
453    tag   = ossim::OTIFFTAG_TILEBYTECOUNTS;
454    count = tile_byte_counts.size();
455    type  = ossim::OTIFF_LONG8;
456    writeTiffTag<ossim_uint64>( tag, type, count, &tile_byte_counts.front(), arrayWritePos );
457    ++tagCount;
458 
459    // sample format tag 339:
460    tag   = ossim::OTIFFTAG_SAMPLEFORMAT;
461    count = theInputConnection->getNumberOfOutputBands();
462    type  = ossim::OTIFF_SHORT;
463    value_ui16 = getTiffSampleFormat();
464    if ( count == 1 )
465    {
466       writeTiffTag<ossim_uint16>( tag, type, count, &value_ui16, arrayWritePos );
467    }
468    else
469    {
470       std::vector<ossim_uint16> v(count, value_ui16);
471       writeTiffTag<ossim_uint16>( tag, type, count, &v.front(), arrayWritePos );
472    }
473    ++tagCount;
474 
475    // Writes two tags 340 and 341 (conditional on scalar type):
476    if ( writeSMinSMaxTiffTags( minBands, maxBands, arrayWritePos ) == true )
477    {
478       tagCount += 2;
479    }
480 
481    // Write geo keys if valid map projection:
482    if ( mapProj.valid() )
483    {
484       std::vector<ossim_float64> vf;
485       ossimDpt scale;
486       ossimDpt tie;
487 
488       if ( mapProj->isGeographic() )
489       {
490          ossimGpt gpt;
491          mapProj->lineSampleToWorld( theAreaOfInterest.ul(), gpt );
492          tie.x = gpt.lon;
493          tie.y = gpt.lat;
494          scale = mapProj->getDecimalDegreesPerPixel();
495       }
496       else
497       {
498          mapProj->lineSampleToEastingNorthing( theAreaOfInterest.ul(), tie );
499          scale = mapProj->getMetersPerPixel();
500       }
501 
502       // Need to decide whehter to specify the full 4x4 model transform (in the case of a rotated
503       // image), or simply use scale and offset tags:
504       if (mapProj->isRotated())
505       {
506          // Model transform needed -- tag 34264:
507          auto transform = mapProj->getModelTransform();
508          count = 16; // 4x4 transform matrix
509          tag   = ossim::OMODEL_TRANSFORM_TAG;
510          type  = ossim::OTIFF_DOUBLE;
511          vf.resize( count );
512          auto m = transform.getData();
513          for (int i=0; i<(int)count; ++i)
514             vf.emplace_back(m[i/4][i%4]);
515          writeTiffTag<ossim_float64>( tag, type, count, &vf.front(), arrayWritePos );
516          ++tagCount;
517       }
518       else
519       {
520          // model pixel scale tag 33550:
521          tag = ossim::OMODEL_PIXEL_SCALE_TAG;
522          count = 3; // x, y, z
523          type = ossim::OTIFF_DOUBLE;
524          vf.resize(count);
525          vf[0] = scale.x;
526          vf[1] = scale.y;
527          vf[2] = 0.0;
528          writeTiffTag<ossim_float64>(tag, type, count, &vf.front(), arrayWritePos);
529          ++tagCount;
530 
531          // model tie point tag 33992:
532          tag = ossim::OMODEL_TIE_POINT_TAG;
533          count = 6; // x, y, z
534          type = ossim::OTIFF_DOUBLE;
535          vf.resize(count);
536          vf[0] = 0.0;   // x image point
537          vf[1] = 0.0;   // y image point
538          vf[2] = 0.0;   // z image point
539          vf[3] = tie.x; // longitude or easting
540          vf[4] = tie.y; // latitude of northing
541          vf[5] = 0.0;
542          writeTiffTag<ossim_float64>(tag, type, count, &vf.front(), arrayWritePos);
543          ++tagCount;
544       }
545 
546       // geo key directory tag 34735:
547       tag   = ossim::OGEO_KEY_DIRECTORY_TAG;
548       count = 0; // set later.
549       type  = ossim::OTIFF_SHORT;
550       std::vector<ossim_uint16> vs(0);
551 
552       // No
553       vs.push_back(1);
554       vs.push_back(1);
555       vs.push_back(0);
556       vs.push_back(10); // Updated later.
557 
558       vs.push_back(ossim::OGT_MODEL_TYPE_GEO_KEY); // 1024
559       vs.push_back(0);
560       vs.push_back(1);
561       vs.push_back(mapProj->isGeographic() ? ossim::OMODEL_TYPE_GEOGRAPHIC :
562                    ossim::OMODEL_TYPE_PROJECTED);
563 
564       vs.push_back(ossim::OGT_RASTER_TYPE_GEO_KEY); // 1025
565       vs.push_back(0);
566       vs.push_back(1);
567       vs.push_back(ossim::OPIXEL_IS_POINT);
568 
569       if ( mapProj->isGeographic() )
570       {
571          vs.push_back(ossim::OGEOGRAPHIC_TYPE_GEO_KEY); // 2048
572          vs.push_back(0);
573          vs.push_back(1);
574          vs.push_back((ossim_uint16)(mapProj->getPcsCode()));
575       }
576 
577       vs.push_back(ossim::OGEOG_GEODETIC_DATUM_GEO_KEY); // 2050
578       vs.push_back(0);
579       vs.push_back(1);
580       vs.push_back((ossim_uint16)(mapProj->getDatum()->epsgCode()));
581 
582       if ( mapProj->isGeographic() )
583       {
584          vs.push_back(ossim::OGEOG_ANGULAR_UNITS_GEO_KEY); // 2054
585          vs.push_back(0);
586          vs.push_back(1);
587          vs.push_back(ossim::OANGULAR_DEGREE);
588       }
589 
590       vs.push_back(ossim::OGEOG_ELLIPSOID_GEO_KEY); // 2056
591       vs.push_back(0);
592       vs.push_back(1);
593       vs.push_back((ossim_uint16)(mapProj->getDatum()->ellipsoid()->getEpsgCode()));
594 
595       // Stored in external OOGEO_DOUBLE_PARAMS_TAG
596       vs.push_back(ossim::OGEOG_SEMI_MAJOR_AXIS); // 2057
597       vs.push_back(ossim::OGEO_DOUBLE_PARAMS_TAG);
598       vs.push_back(1);
599       vs.push_back(0);
600 
601       vs.push_back(ossim::OGEOG_SEMI_MINOR_AXIS); // 2058
602       vs.push_back(ossim::OGEO_DOUBLE_PARAMS_TAG);
603       vs.push_back(1);
604       vs.push_back(1);
605 
606       vs.push_back(ossim::OPROJECTED_CS_TYPE_GEO_KEY); // 3072
607       vs.push_back(0);
608       vs.push_back(1);
609       vs.push_back((ossim_uint16)(mapProj->getPcsCode()));
610 
611       vs.push_back(ossim::OPROJECTION_GEO_KEY); // 3074
612       vs.push_back(0);
613       vs.push_back(1);
614       vs.push_back((ossim_uint16)(mapProj->getPcsCode()));
615 
616       if ( mapProj->isGeographic() == false )
617       {
618          vs.push_back(ossim::OPROJ_LINEAR_UNITS_GEO_KEY); // 3076
619          vs.push_back(0);
620          vs.push_back(1);
621          vs.push_back(ossim::OLINEAR_METER);
622       }
623 
624       count = vs.size();
625       vs[3] = (count / 4) - 1;
626       writeTiffTag<ossim_uint16>( tag, type, count, &vs.front(), arrayWritePos );
627       ++tagCount;
628 
629       // geo double params tag 33550:
630       tag   = ossim::OGEO_DOUBLE_PARAMS_TAG;
631       count = 2; // ellipsoid major, minor axis
632       type  = ossim::OTIFF_DOUBLE;
633       vf.resize( count );
634       vf[0] = mapProj->getDatum()->ellipsoid()->a();
635       vf[1] = mapProj->getDatum()->ellipsoid()->b();
636       writeTiffTag<ossim_float64>( tag, type, count, &vf.front(), arrayWritePos );
637       ++tagCount;
638    }
639 
640    // Write trailing zero indicading no more IFDs.
641    ossim_uint64 offsetToNextIfd = 0;
642    m_str->write( (const char*)&offsetToNextIfd, 8 );
643 
644    // Seek back and re-write the tag count.
645    m_str->seekp( 16, std::ios_base::beg );
646    m_str->write( (const char*)&tagCount, 8 );
647 
648    status =  m_str->good();
649 
650    return status;
651 }
652 
writeMinMaxTiffTags(std::streamoff & arrayWritePos)653 bool ossimWriter::writeMinMaxTiffTags( std::streamoff& arrayWritePos )
654 {
655    bool status = true;
656 
657    // DEFAULT for OSSIM_UINT32.
658    ossim_uint16 minValue = 1;
659    ossim_uint16 maxValue = 255;
660 
661    switch( theInputConnection->getOutputScalarType() )
662    {
663       case OSSIM_UINT8:
664       {
665          break; // defaulted above
666       }
667       case OSSIM_USHORT11:
668       {
669          maxValue = 2047;
670          break;
671       }
672       case OSSIM_USHORT12:
673       {
674          maxValue = 4095;
675          break;
676       }
677       case OSSIM_USHORT13:
678       {
679          maxValue = 8191;
680          break;
681       }
682       case OSSIM_USHORT14:
683       {
684          maxValue = 16383;
685          break;
686       }
687       case OSSIM_USHORT15:
688       {
689          maxValue = 32767;
690          break;
691       }
692       case OSSIM_UINT16:
693       {
694          maxValue = 65535;
695          break;
696       }
697       default:
698          status = false;
699    }
700 
701    if ( status )
702    {
703       writeTiffTag<ossim_uint16>( ossim::OTIFFTAG_MINSAMPLEVALUE,
704                                   ossim::OTIFF_SHORT,
705                                   1, &minValue, arrayWritePos );
706       writeTiffTag<ossim_uint16>( ossim::OTIFFTAG_MAXSAMPLEVALUE,
707                                   ossim::OTIFF_SHORT,
708                                   1, &maxValue, arrayWritePos );
709    }
710 
711    return status;
712 }
713 
writeSMinSMaxTiffTags(const vector<ossim_float64> & minBands,const vector<ossim_float64> & maxBands,std::streamoff & arrayWritePos)714 bool ossimWriter::writeSMinSMaxTiffTags( const vector<ossim_float64>& minBands,
715                                          const vector<ossim_float64>& maxBands,
716                                          std::streamoff& arrayWritePos )
717 {
718    bool status = false;
719 
720    if(minBands.size() && maxBands.size())
721    {
722       ossim_float64 minValue = *std::min_element(minBands.begin(), minBands.end());
723       ossim_float64 maxValue = *std::max_element(maxBands.begin(), maxBands.end());
724 
725       switch( theInputConnection->getOutputScalarType() )
726       {
727          case OSSIM_SINT16:
728          case OSSIM_UINT32:
729          case OSSIM_FLOAT32:
730          case OSSIM_FLOAT64:
731          case OSSIM_NORMALIZED_FLOAT:
732          case OSSIM_NORMALIZED_DOUBLE:
733          {
734             ossim_float32 v = static_cast<ossim_float32>(minValue);
735             writeTiffTag<ossim_float32>( ossim::OTIFFTAG_SMINSAMPLEVALUE,
736                                          ossim::OTIFF_FLOAT,
737                                          1, &v, arrayWritePos );
738             v = static_cast<ossim_float32>(maxValue);
739             writeTiffTag<ossim_float32>( ossim::OTIFFTAG_SMAXSAMPLEVALUE,
740                                          ossim::OTIFF_FLOAT,
741                                          1, &v, arrayWritePos );
742             status = true;
743             break;
744          }
745          default:
746          {
747             break;
748          }
749       }
750    }
751    return status;
752 }
753 
754 template <class T>
writeTiffTag(ossim_uint16 tag,ossim_uint16 type,ossim_uint64 count,const T * value,std::streamoff & arrayWritePos)755 void ossimWriter::writeTiffTag(
756    ossim_uint16 tag, ossim_uint16 type, ossim_uint64 count,
757    const T* value, std::streamoff& arrayWritePos )
758 {
759    m_str->write( (const char*)&tag, 2 );
760    m_str->write( (const char*)&type, 2 );
761    m_str->write( (const char*)&count, 8 );
762 
763    ossim_uint64 bytes = sizeof( T ) * count;
764 
765    if ( bytes <= 8 )
766    {
767       m_str->write( (const char*)value, bytes );
768       if ( bytes < 8 )
769       {
770          // Fill remaining bytes with 0.
771          char c = '\0';
772          m_str->write( (const char*)&c, (8-bytes) );
773       }
774    }
775    else // Greater than 8 bytes, must write at end of file.
776    {
777       // Store the offset to array:
778       m_str->write( (const char*)&arrayWritePos, 8 );
779 
780       // Capture posistion:
781       std::streamoff currentPos = m_str->tellp();
782 
783       // Seek to end:
784       m_str->seekp( arrayWritePos, std::ios_base::beg );
785 
786       // Write:
787       m_str->write( (const char*)value, bytes );
788 
789       // Capture new offset for next array write.
790       arrayWritePos = m_str->tellp();
791 
792       // Seek back:
793       m_str->seekp( currentPos, std::ios_base::beg );
794    }
795 }
796 
writeTiffTilesBandSeparate(std::vector<ossim_uint64> & tile_offsets,std::vector<ossim_uint64> & tile_byte_counts,std::vector<ossim_float64> & minBands,std::vector<ossim_float64> & maxBands)797 bool ossimWriter::writeTiffTilesBandSeparate( std::vector<ossim_uint64>& tile_offsets,
798                                               std::vector<ossim_uint64>& tile_byte_counts,
799                                               std::vector<ossim_float64>& minBands,
800                                               std::vector<ossim_float64>& maxBands )
801 {
802    static const char* const MODULE = "ossimWriter::writeToTilesBandSeparate";
803    if ( traceDebug() ) CLOG << " Entered...\n";
804 
805    // Start the sequence at the first tile.
806    theInputConnection->setToStartOfSequence();
807 
808    // Control flags:
809    bool alignTiles    = getAlignTilesFlag();
810    bool flushTiles    = getFlushTilesFlag();
811    bool writeBlanks   = getWriteBlanksFlag();
812    bool computeMinMax = needsMinMax();
813 
814    // Block size for write:
815    const std::streamsize BLOCK_SIZE = getBlockSize();
816 
817    const ossim_int32 BANDS       = (ossim_int32)theInputConnection->getNumberOfOutputBands();
818    const ossim_int32 TILES_WIDE  = (ossim_int32)theInputConnection->getNumberOfTilesHorizontal();
819    const ossim_int32 TILES_TOTAL = (ossim_int32)theInputConnection->getNumberOfTiles();
820 
821    if (traceDebug())
822    {
823       ossimNotify(ossimNotifyLevel_DEBUG)
824          << "align tiles flag:     " << alignTiles
825          << "\nflush tiles flag:     " << flushTiles
826          << "\nwrite blanks flag:    " << writeBlanks
827          << "\ncompute min max flag: " << computeMinMax
828          << "\nwrite block size:     " << BLOCK_SIZE
829          << "\nBANDS:                " << BANDS
830          << "\nTILES_WIDE:           " << TILES_WIDE
831          << "\nTILES_TOTAL:          " << TILES_TOTAL << "\n";
832    }
833 
834    tile_offsets.resize( TILES_TOTAL*BANDS );
835    tile_byte_counts.resize( TILES_TOTAL*BANDS );
836 
837    ossimDataObjectStatus tileStatus = OSSIM_STATUS_UNKNOWN;
838    ossim_int64 ossimTileIndex    = 0;
839    ossim_int64 tiffTileIndex     = 0;
840    ossim_int64 tileSizeInBytes   = 0;
841    ossim_int64 bandOffsetInBytes = 0;
842 
843    //---
844    // Adjust the starting file position to make room for IFD tags, tile offset
845    // and tile byte counts and arrays.
846    //
847    // Assuming:
848    // IFD start = 16, end 512, gives 496 bytes for tags.
849    // Array section start = 512, end is start + (16 * tile_count * bands) + 256 bytes
850    // for geotiff array bytes.
851    //---
852    std::streamsize startPos = 512 + 16 * TILES_TOTAL * BANDS + 256;
853 
854    while ( ossimTileIndex < TILES_TOTAL )
855    {
856       ossimRefPtr<ossimImageData> id = theInputConnection->getNextTile();
857       if(!id)
858       {
859          ossimNotify(ossimNotifyLevel_WARN)
860             << MODULE << " ERROR:"
861             << "Error returned writing tiff tile:  " << ossimTileIndex
862             << "\nNULL Tile from input encountered"
863             << std::endl;
864          return false;
865       }
866 
867       tileStatus = id->getDataObjectStatus();
868 
869       if ( ossimTileIndex == 0 )
870       {
871          tileSizeInBytes = (ossim_int64)id->getSizePerBandInBytes();
872          bandOffsetInBytes = tileSizeInBytes * TILES_TOTAL;
873       }
874 
875       if ( computeMinMax )
876       {
877          if ( (tileStatus == OSSIM_FULL) || (tileStatus == OSSIM_PARTIAL) )
878          {
879             // Compute running min, max.
880             id->computeMinMaxPix(minBands, maxBands);
881          }
882       }
883 
884       // Band loop.
885       for (ossim_int32 band=0; band < BANDS; ++band)
886       {
887          tiffTileIndex = ossimTileIndex + band * TILES_TOTAL;
888 
889          if ( (writeBlanks == true) || (tileStatus == OSSIM_FULL) || (tileStatus == OSSIM_PARTIAL) )
890          {
891             // Grab a pointer to the tile for the band.
892             const char* data = (const char*)id->getBuf(band);
893 
894             // Compress data here(future maybe, i.e. jpeg, j2k...
895 
896             //---
897             // Write the tile.
898             // Note: tiles laid out, all the red tiles, all the green tiles all the
899             // blue tiles.
900             //---
901             if(data)
902             {
903                // Compute the stream position:
904                std::streampos pos = startPos + ossimTileIndex * tileSizeInBytes +
905                   band * bandOffsetInBytes;
906 
907                if ( alignTiles )
908                {
909                   // Snap to block boundary:
910                   std::streampos overflow = pos % BLOCK_SIZE;
911                   if ( overflow > 0 )
912                   {
913                      pos += BLOCK_SIZE - overflow;
914                   }
915                }
916 
917                m_str->seekp( pos );
918 
919                if ( m_str->good() )
920                {
921                   tile_offsets[ tiffTileIndex ] = (ossim_uint64)pos;
922                   tile_byte_counts[ tiffTileIndex ] = (ossim_uint64)tileSizeInBytes;
923 
924                   // Write the tile to stream:
925                   m_str->write( data, (std::streamsize)tileSizeInBytes);
926 
927                   if ( flushTiles )
928                   {
929                      m_str->flush();
930                   }
931 
932                   // Check stream:
933                   if ( m_str->fail() == true )
934                   {
935                      ossimNotify(ossimNotifyLevel_DEBUG)
936                         << MODULE << " ERROR:\nWrite error on tiff tile:  " << ossimTileIndex
937                         << std::endl;
938                      return false;
939                   }
940                }
941                else
942                {
943                   ossimNotify(ossimNotifyLevel_DEBUG)
944                      << MODULE << " ERROR:\nStream has gone bad!" << std::endl;
945                   return false;
946                }
947 
948             }
949             else
950             {
951                ossimNotify(ossimNotifyLevel_WARN)
952                   << MODULE << " ERROR:\nNull input tile:  " << ossimTileIndex
953                   << std::endl;
954                return false;
955             }
956          }
957          else
958          {
959             //---
960             // Writing sparse tiff.
961             // Set the offset and byte count to 0 to indicate blank tile.
962             //---
963             if (traceDebug())
964             {
965                ossimNotify(ossimNotifyLevel_DEBUG)
966                   << "sparse blank tile[" << tiffTileIndex << "]: " << tiffTileIndex << "\n";
967             }
968             tile_offsets[ tiffTileIndex ] = 0;
969             tile_byte_counts[ tiffTileIndex ] = 0;
970          }
971 
972       } // End of band loop.
973 
974       ++ossimTileIndex;
975 
976       if( needsAborting() )
977       {
978          setPercentComplete(100);
979          break; // Get out...
980       }
981       else if ( ossimTileIndex % TILES_WIDE )
982       {
983          // Output percent complete every row of tiles.
984          double tileNum = ossimTileIndex;
985          double numTiles = TILES_TOTAL;
986          setPercentComplete(tileNum / numTiles * 100.0);
987       }
988 
989    } // End: while ( ossimTileIndex < TILES_TOTAL )
990 
991    if ( traceDebug() ) CLOG << " Exited...\n";
992 
993    return m_str->good();
994 }
995 
setOutputStream(std::ostream & stream)996 bool ossimWriter::setOutputStream(std::ostream& stream)
997 {
998    if (m_ownsStreamFlag && m_str)
999    {
1000       delete m_str;
1001    }
1002    m_str = &stream;
1003    m_ownsStreamFlag = false;
1004    return true;
1005 }
1006 
setTileSize(const ossimIpt & tileSize)1007 void ossimWriter::setTileSize(const ossimIpt& tileSize)
1008 {
1009    if ( (tileSize.x % 16) || (tileSize.y % 16) )
1010    {
1011       if(traceDebug())
1012       {
1013          ossimNotify(ossimNotifyLevel_DEBUG)
1014                   << "ossimWriter::setTileSize ERROR:"
1015                   << "\nTile size must be a multiple of 16!"
1016                   << "\nSize remains:  " << m_outputTileSize
1017                   << std::endl;
1018       }
1019    }
1020    else
1021    {
1022       m_outputTileSize = tileSize;
1023 
1024       // For save state:
1025       m_kwl->addPair( TILE_SIZE_KW, m_outputTileSize.toString().string() );
1026    }
1027 }
1028 
getOutputTileSize() const1029 const ossimIpt& ossimWriter::getOutputTileSize() const
1030 {
1031    return m_outputTileSize;
1032 }
1033 
saveState(ossimKeywordlist & kwl,const char * prefix) const1034 bool ossimWriter::saveState( ossimKeywordlist& kwl, const char* prefix) const
1035 {
1036    // Lazy man save state...
1037    kwl.add( prefix, *(m_kwl.get()), true );
1038    return ossimImageFileWriter::saveState(kwl, prefix);
1039 }
1040 
loadState(const ossimKeywordlist & kwl,const char * prefix)1041 bool ossimWriter::loadState(const ossimKeywordlist& kwl, const char* prefix)
1042 {
1043    bool result = false;
1044    if ( ossimImageFileWriter::loadState(kwl, prefix) )
1045    {
1046       if ( theOutputImageType!="ossim_ttbs")
1047       {
1048          result = true;
1049 
1050          std::string pfx = prefix?prefix:"";
1051          std::string value;
1052 
1053          value = kwl.findKey( pfx, ALIGN_TILES_KW );
1054          if ( value.size() )
1055          {
1056             m_kwl->addPair( ALIGN_TILES_KW, value, true );
1057          }
1058 
1059          value = kwl.findKey( pfx, BLOCK_SIZE_KW );
1060          if ( value.size() )
1061          {
1062             m_kwl->addPair( BLOCK_SIZE_KW, value, true );
1063          }
1064 
1065          value = kwl.findKey( pfx, FLUSH_TILES_KW );
1066          if ( value.size() )
1067          {
1068             m_kwl->addPair( FLUSH_TILES_KW, value, true );
1069          }
1070 
1071          value = kwl.findKey( pfx, INCLUDE_BLANK_TILES_KW );
1072          if ( value.size() )
1073          {
1074             m_kwl->addPair( INCLUDE_BLANK_TILES_KW, value, true );
1075          }
1076 
1077          value = kwl.findKey( pfx, TILE_SIZE_KW );
1078          if ( value.size() )
1079          {
1080             m_outputTileSize.toPoint(value);
1081             m_kwl->addPair( TILE_SIZE_KW, m_outputTileSize.toString().string(), true );
1082          }
1083       }
1084    }
1085 
1086    return result;
1087 }
1088 
setProperty(ossimRefPtr<ossimProperty> property)1089 void ossimWriter::setProperty(ossimRefPtr<ossimProperty> property)
1090 {
1091    if( property.valid() )
1092    {
1093       // See if it's one of our properties:
1094       std::string key = property->getName().string();
1095 
1096       if ( traceDebug() )
1097       {
1098          ossimString value;
1099          property->valueToString(value);
1100 
1101          ossimNotify(ossimNotifyLevel_DEBUG)
1102             << "ossimWriter::setProperty DEBUG:"
1103             << "\nkey:   " << key
1104             << "\nvalue: " << value << "\n";
1105       }
1106 
1107       if ( ( key == ALIGN_TILES_KW ) ||
1108            ( key == BLOCK_SIZE_KW )  ||
1109            ( key == FLUSH_TILES_KW ) ||
1110            ( key == INCLUDE_BLANK_TILES_KW ) )
1111       {
1112          ossimString value;
1113          property->valueToString(value);
1114          m_kwl->addPair( key, value.string(), true );
1115       }
1116       else if ( key == TILE_SIZE_KW )
1117       {
1118          // Comes in as a single int, e.g.: 256
1119          ossimString value;
1120          property->valueToString(value);
1121          m_outputTileSize.x = value.toInt32();
1122          m_outputTileSize.y = m_outputTileSize.x;
1123 
1124          // Store in keywordlist / save state as a point, e.g.: ( 256, 256 )
1125          m_kwl->addPair( key,  m_outputTileSize.toString().string(), true );
1126       }
1127       else
1128       {
1129          ossimImageFileWriter::setProperty(property);
1130       }
1131    }
1132 }
1133 
getProperty(const ossimString & name) const1134 ossimRefPtr<ossimProperty> ossimWriter::getProperty(const ossimString& name)const
1135 {
1136    ossimRefPtr<ossimProperty> prop = 0;
1137 
1138    if ( name.string() == ALIGN_TILES_KW )
1139    {
1140       std::string value = m_kwl->findKey( ALIGN_TILES_KW );
1141       ossimRefPtr<ossimBooleanProperty> boolProp =
1142          new ossimBooleanProperty(name, ossimString(value).toBool());
1143       prop = boolProp.get();
1144    }
1145    else if( name == BLOCK_SIZE_KW )
1146    {
1147       // Property a single int, e.g.: 4096
1148       ossim_int64 blockSize = getBlockSize();
1149       ossimRefPtr<ossimStringProperty> stringProp =
1150          new ossimStringProperty(name, ossimString::toString(blockSize), false); // editable flag
1151       prop = stringProp.get();
1152    }
1153    else if ( name.string() == FLUSH_TILES_KW )
1154    {
1155       std::string value = m_kwl->findKey( FLUSH_TILES_KW );
1156       ossimRefPtr<ossimBooleanProperty> boolProp =
1157          new ossimBooleanProperty(name, ossimString(value).toBool());
1158       prop = boolProp.get();
1159    }
1160    else if ( name.string() == INCLUDE_BLANK_TILES_KW )
1161    {
1162       std::string value = m_kwl->findKey( INCLUDE_BLANK_TILES_KW );
1163       ossimRefPtr<ossimBooleanProperty> boolProp =
1164          new ossimBooleanProperty(name, ossimString(value).toBool());
1165       prop = boolProp.get();
1166    }
1167    else if( name == TILE_SIZE_KW )
1168    {
1169       // Property a single int, e.g.: 256
1170       ossimRefPtr<ossimStringProperty> stringProp =
1171          new ossimStringProperty(name, ossimString::toString(m_outputTileSize.x), false); // editable flag
1172       stringProp->setReadOnlyFlag(false);
1173       stringProp->setChangeType(ossimProperty::ossimPropertyChangeType_AFFECTS_OTHERS);
1174       stringProp->addConstraint(ossimString("16"));
1175       stringProp->addConstraint(ossimString("32"));
1176       stringProp->addConstraint(ossimString("64"));
1177       stringProp->addConstraint(ossimString("128"));
1178       stringProp->addConstraint(ossimString("256"));
1179       stringProp->addConstraint(ossimString("512"));
1180       stringProp->addConstraint(ossimString("1024"));
1181       stringProp->addConstraint(ossimString("2048"));
1182       prop = stringProp.get();
1183    }
1184    else
1185    {
1186       prop = ossimImageFileWriter::getProperty(name);
1187    }
1188    return prop;
1189 }
1190 
getPropertyNames(std::vector<ossimString> & propertyNames) const1191 void ossimWriter::getPropertyNames(std::vector<ossimString>& propertyNames) const
1192 {
1193    propertyNames.push_back(ossimString(ALIGN_TILES_KW));
1194    propertyNames.push_back(ossimString(BLOCK_SIZE_KW));
1195    propertyNames.push_back(ossimString(FLUSH_TILES_KW));
1196    propertyNames.push_back(ossimString(INCLUDE_BLANK_TILES_KW));
1197    propertyNames.push_back(ossimString(TILE_SIZE_KW));
1198    ossimImageFileWriter::getPropertyNames(propertyNames);
1199 }
1200 
getTiffSampleFormat() const1201 ossim_uint16 ossimWriter::getTiffSampleFormat() const
1202 {
1203    ossim_uint16 result = 0;
1204    switch( theInputConnection->getOutputScalarType() )
1205    {
1206       case OSSIM_UINT8:
1207       case OSSIM_USHORT11:
1208       case OSSIM_USHORT12:
1209       case OSSIM_USHORT13:
1210       case OSSIM_USHORT14:
1211       case OSSIM_USHORT15:
1212       case OSSIM_UINT16:
1213       case OSSIM_UINT32:
1214          result = ossim::OSAMPLEFORMAT_UINT;
1215          break;
1216 
1217       case OSSIM_SINT16:
1218          result = ossim::OSAMPLEFORMAT_INT;
1219          break;
1220 
1221       case OSSIM_FLOAT32:
1222       case OSSIM_FLOAT64:
1223       case OSSIM_NORMALIZED_FLOAT:
1224       case OSSIM_NORMALIZED_DOUBLE:
1225          result = ossim::OSAMPLEFORMAT_IEEEFP;
1226          break;
1227 
1228       default:
1229          break;
1230    }
1231 
1232    return result;
1233 }
1234 
isTiled() const1235 bool ossimWriter::isTiled() const
1236 {
1237    return ( theOutputImageType == "ossim_ttbs" );
1238 }
1239 
getAlignTilesFlag() const1240 bool ossimWriter::getAlignTilesFlag() const
1241 {
1242    bool result = true; // default
1243    std::string value = m_kwl->findKey( ALIGN_TILES_KW );
1244    if ( value.size() )
1245    {
1246       result = ossimString(value).toBool();
1247    }
1248    return result;
1249 }
1250 
getBlockSize() const1251 ossim_int64 ossimWriter::getBlockSize() const
1252 {
1253    ossim_int64 result = 4096; // default
1254    std::string value = m_kwl->findKey( BLOCK_SIZE_KW );
1255    if ( value.size() )
1256    {
1257       result = ossimString(value).toInt64();
1258 
1259       // Disallow anything not on 1024 boundary.
1260       if ( result % 1024 )
1261       {
1262          result = 4096;
1263          ossimNotify(ossimNotifyLevel_DEBUG)
1264                   << "ossimWriter::getBlockSize ERROR:"
1265                   << "\nBlock size MUST be a multiple of 1024!"
1266                   << "\nBlock size remains:  " << result
1267                   << std::endl;
1268       }
1269    }
1270    return result;
1271 }
1272 
getFlushTilesFlag() const1273 bool ossimWriter::getFlushTilesFlag() const
1274 {
1275    bool result = true; // default
1276    std::string value = m_kwl->findKey( FLUSH_TILES_KW );
1277    if ( value.size() )
1278    {
1279       result = ossimString(value).toBool();
1280    }
1281    return result;
1282 }
1283 
getWriteBlanksFlag() const1284 bool ossimWriter::getWriteBlanksFlag() const
1285 {
1286    bool result = true; // default
1287    std::string value = m_kwl->findKey( INCLUDE_BLANK_TILES_KW );
1288    if ( value.size() )
1289    {
1290       result = ossimString(value).toBool();
1291    }
1292    return result;
1293 }
1294 
needsMinMax() const1295 bool ossimWriter::needsMinMax() const
1296 {
1297    bool result = false;
1298    switch( theInputConnection->getOutputScalarType() )
1299    {
1300       case OSSIM_SINT16:
1301       case OSSIM_UINT32:
1302       case OSSIM_FLOAT32:
1303       case OSSIM_FLOAT64:
1304       case OSSIM_NORMALIZED_FLOAT:
1305       case OSSIM_NORMALIZED_DOUBLE:
1306       {
1307          result = true;
1308          break;
1309       }
1310       default:
1311       {
1312          break;
1313       }
1314    }
1315    return result;
1316 }
1317