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