1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4 // Digital Ltd. LLC
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are
10 // met:
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 // * Neither the name of Industrial Light & Magic nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35 //-----------------------------------------------------------------------------
36 //
37 // class TiledOutputFile
38 //
39 //-----------------------------------------------------------------------------
40
41 #include <ImfTiledOutputFile.h>
42 #include <ImfTiledInputFile.h>
43 #include <ImfInputFile.h>
44 #include <ImfTileDescriptionAttribute.h>
45 #include <ImfPreviewImageAttribute.h>
46 #include <ImfChannelList.h>
47 #include <ImfMisc.h>
48 #include <ImfTiledMisc.h>
49 #include <ImfStdIO.h>
50 #include <ImfCompressor.h>
51 #include "ImathBox.h"
52 #include <ImfArray.h>
53 #include <ImfXdr.h>
54 #include <ImfVersion.h>
55 #include <ImfTileOffsets.h>
56 #include <ImfThreading.h>
57 #include "IlmThreadPool.h"
58 #include "IlmThreadSemaphore.h"
59 #include "IlmThreadMutex.h"
60 #include "Iex.h"
61 #include <string>
62 #include <vector>
63 #include <fstream>
64 #include <assert.h>
65 #include <map>
66 #include <algorithm> // for std::max()
67
68
69 namespace Imf {
70
71 using Imath::Box2i;
72 using Imath::V2i;
73 using std::string;
74 using std::vector;
75 using std::ofstream;
76 using std::map;
77 using std::min;
78 using std::max;
79 using std::swap;
80 using IlmThread::Mutex;
81 using IlmThread::Lock;
82 using IlmThread::Semaphore;
83 using IlmThread::Task;
84 using IlmThread::TaskGroup;
85 using IlmThread::ThreadPool;
86
87 namespace {
88
89 struct TOutSliceInfo
90 {
91 PixelType type;
92 const char * base;
93 size_t xStride;
94 size_t yStride;
95 bool zero;
96 int xTileCoords;
97 int yTileCoords;
98
99 TOutSliceInfo (PixelType type = HALF,
100 const char *base = 0,
101 size_t xStride = 0,
102 size_t yStride = 0,
103 bool zero = false,
104 int xTileCoords = 0,
105 int yTileCoords = 0);
106 };
107
108
TOutSliceInfo(PixelType t,const char * b,size_t xs,size_t ys,bool z,int xtc,int ytc)109 TOutSliceInfo::TOutSliceInfo (PixelType t,
110 const char *b,
111 size_t xs, size_t ys,
112 bool z,
113 int xtc,
114 int ytc)
115 :
116 type (t),
117 base (b),
118 xStride (xs),
119 yStride (ys),
120 zero (z),
121 xTileCoords (xtc),
122 yTileCoords (ytc)
123 {
124 // empty
125 }
126
127
128 struct TileCoord
129 {
130 int dx;
131 int dy;
132 int lx;
133 int ly;
134
135
TileCoordImf::__anon467016530111::TileCoord136 TileCoord (int xTile = 0, int yTile = 0,
137 int xLevel = 0, int yLevel = 0)
138 :
139 dx (xTile), dy (yTile),
140 lx (xLevel), ly (yLevel)
141 {
142 // empty
143 }
144
145
146 bool
operator <Imf::__anon467016530111::TileCoord147 operator < (const TileCoord &other) const
148 {
149 return (ly < other.ly) ||
150 (ly == other.ly && lx < other.lx) ||
151 ((ly == other.ly && lx == other.lx) &&
152 ((dy < other.dy) || (dy == other.dy && dx < other.dx)));
153 }
154
155
156 bool
operator ==Imf::__anon467016530111::TileCoord157 operator == (const TileCoord &other) const
158 {
159 return lx == other.lx &&
160 ly == other.ly &&
161 dx == other.dx &&
162 dy == other.dy;
163 }
164 };
165
166
167 struct BufferedTile
168 {
169 char * pixelData;
170 int pixelDataSize;
171
BufferedTileImf::__anon467016530111::BufferedTile172 BufferedTile (const char *data, int size):
173 pixelData (0),
174 pixelDataSize(size)
175 {
176 pixelData = new char[pixelDataSize];
177 memcpy (pixelData, data, pixelDataSize);
178 }
179
~BufferedTileImf::__anon467016530111::BufferedTile180 ~BufferedTile()
181 {
182 delete [] pixelData;
183 }
184 };
185
186
187 typedef map <TileCoord, BufferedTile *> TileMap;
188
189
190 struct TileBuffer
191 {
192 Array<char> buffer;
193 const char * dataPtr;
194 int dataSize;
195 Compressor * compressor;
196 TileCoord tileCoord;
197 bool hasException;
198 string exception;
199
200 TileBuffer (Compressor *comp);
201 ~TileBuffer ();
202
waitImf::__anon467016530111::TileBuffer203 inline void wait () {_sem.wait();}
postImf::__anon467016530111::TileBuffer204 inline void post () {_sem.post();}
205
206 protected:
207
208 Semaphore _sem;
209 };
210
211
TileBuffer(Compressor * comp)212 TileBuffer::TileBuffer (Compressor *comp):
213 dataPtr (0),
214 dataSize (0),
215 compressor (comp),
216 hasException (false),
217 exception (),
218 _sem (1)
219 {
220 // empty
221 }
222
223
~TileBuffer()224 TileBuffer::~TileBuffer ()
225 {
226 delete compressor;
227 }
228
229
230 } // namespace
231
232
233 struct TiledOutputFile::Data: public Mutex
234 {
235 Header header; // the image header
236 int version; // file format version
237 TileDescription tileDesc; // describes the tile layout
238 FrameBuffer frameBuffer; // framebuffer to write into
239 Int64 previewPosition;
240 LineOrder lineOrder; // the file's lineorder
241 int minX; // data window's min x coord
242 int maxX; // data window's max x coord
243 int minY; // data window's min y coord
244 int maxY; // data window's max x coord
245
246 int numXLevels; // number of x levels
247 int numYLevels; // number of y levels
248 int * numXTiles; // number of x tiles at a level
249 int * numYTiles; // number of y tiles at a level
250
251 TileOffsets tileOffsets; // stores offsets in file for
252 // each tile
253
254 Compressor::Format format; // compressor's data format
255 vector<TOutSliceInfo> slices; // info about channels in file
256 OStream * os; // file stream to write to
257 bool deleteStream;
258
259 size_t maxBytesPerTileLine; // combined size of a tile line
260 // over all channels
261
262
263 vector<TileBuffer*> tileBuffers;
264 size_t tileBufferSize; // size of a tile buffer
265
266 Int64 tileOffsetsPosition; // position of the tile index
267 Int64 currentPosition; // current position in the file
268
269 TileMap tileMap;
270 TileCoord nextTileToWrite;
271
272 Data (bool del, int numThreads);
273 ~Data ();
274
275 inline TileBuffer * getTileBuffer (int number);
276 // hash function from tile
277 // buffer coords into our
278 // vector of tile buffers
279
280 TileCoord nextTileCoord (const TileCoord &a);
281 };
282
283
Data(bool del,int numThreads)284 TiledOutputFile::Data::Data (bool del, int numThreads):
285 numXTiles(0),
286 numYTiles(0),
287 os (0),
288 deleteStream (del),
289 tileOffsetsPosition (0)
290 {
291 //
292 // We need at least one tileBuffer, but if threading is used,
293 // to keep n threads busy we need 2*n tileBuffers
294 //
295
296 tileBuffers.resize (max (1, 2 * numThreads));
297 }
298
299
~Data()300 TiledOutputFile::Data::~Data ()
301 {
302 delete [] numXTiles;
303 delete [] numYTiles;
304
305 if (deleteStream)
306 delete os;
307
308 //
309 // Delete all the tile buffers, if any still happen to exist
310 //
311
312 for (TileMap::iterator i = tileMap.begin(); i != tileMap.end(); ++i)
313 delete i->second;
314
315 for (size_t i = 0; i < tileBuffers.size(); i++)
316 delete tileBuffers[i];
317 }
318
319
320 TileBuffer*
getTileBuffer(int number)321 TiledOutputFile::Data::getTileBuffer (int number)
322 {
323 return tileBuffers[number % tileBuffers.size()];
324 }
325
326
327 TileCoord
nextTileCoord(const TileCoord & a)328 TiledOutputFile::Data::nextTileCoord (const TileCoord &a)
329 {
330 TileCoord b = a;
331
332 if (lineOrder == INCREASING_Y)
333 {
334 b.dx++;
335
336 if (b.dx >= numXTiles[b.lx])
337 {
338 b.dx = 0;
339 b.dy++;
340
341 if (b.dy >= numYTiles[b.ly])
342 {
343 //
344 // the next tile is in the next level
345 //
346
347 b.dy = 0;
348
349 switch (tileDesc.mode)
350 {
351 case ONE_LEVEL:
352 case MIPMAP_LEVELS:
353
354 b.lx++;
355 b.ly++;
356 break;
357
358 case RIPMAP_LEVELS:
359
360 b.lx++;
361
362 if (b.lx >= numXLevels)
363 {
364 b.lx = 0;
365 b.ly++;
366
367 #ifdef DEBUG
368 assert (b.ly <= numYLevels);
369 #endif
370 }
371 break;
372 }
373 }
374 }
375 }
376 else if (lineOrder == DECREASING_Y)
377 {
378 b.dx++;
379
380 if (b.dx >= numXTiles[b.lx])
381 {
382 b.dx = 0;
383 b.dy--;
384
385 if (b.dy < 0)
386 {
387 //
388 // the next tile is in the next level
389 //
390
391 switch (tileDesc.mode)
392 {
393 case ONE_LEVEL:
394 case MIPMAP_LEVELS:
395
396 b.lx++;
397 b.ly++;
398 break;
399
400 case RIPMAP_LEVELS:
401
402 b.lx++;
403
404 if (b.lx >= numXLevels)
405 {
406 b.lx = 0;
407 b.ly++;
408
409 #ifdef DEBUG
410 assert (b.ly <= numYLevels);
411 #endif
412 }
413 break;
414 }
415
416 if (b.ly < numYLevels)
417 b.dy = numYTiles[b.ly] - 1;
418 }
419 }
420 }
421
422 return b;
423 }
424
425
426 namespace {
427
428 void
writeTileData(TiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],int pixelDataSize)429 writeTileData (TiledOutputFile::Data *ofd,
430 int dx, int dy,
431 int lx, int ly,
432 const char pixelData[],
433 int pixelDataSize)
434 {
435 //
436 // Store a block of pixel data in the output file, and try
437 // to keep track of the current writing position the file,
438 // without calling tellp() (tellp() can be fairly expensive).
439 //
440
441 Int64 currentPosition = ofd->currentPosition;
442 ofd->currentPosition = 0;
443
444 if (currentPosition == 0)
445 currentPosition = ofd->os->tellp();
446
447 ofd->tileOffsets (dx, dy, lx, ly) = currentPosition;
448
449 #ifdef DEBUG
450 assert (ofd->os->tellp() == currentPosition);
451 #endif
452
453 //
454 // Write the tile header.
455 //
456
457 Xdr::write <StreamIO> (*ofd->os, dx);
458 Xdr::write <StreamIO> (*ofd->os, dy);
459 Xdr::write <StreamIO> (*ofd->os, lx);
460 Xdr::write <StreamIO> (*ofd->os, ly);
461 Xdr::write <StreamIO> (*ofd->os, pixelDataSize);
462
463 ofd->os->write (pixelData, pixelDataSize);
464
465 //
466 // Keep current position in the file so that we can avoid
467 // redundant seekg() operations (seekg() can be fairly expensive).
468 //
469
470 ofd->currentPosition = currentPosition +
471 5 * Xdr::size<int>() +
472 pixelDataSize;
473 }
474
475
476
477 void
bufferedTileWrite(TiledOutputFile::Data * ofd,int dx,int dy,int lx,int ly,const char pixelData[],int pixelDataSize)478 bufferedTileWrite (TiledOutputFile::Data *ofd,
479 int dx, int dy,
480 int lx, int ly,
481 const char pixelData[],
482 int pixelDataSize)
483 {
484 //
485 // Check if a tile with coordinates (dx,dy,lx,ly) has already been written.
486 //
487
488 if (ofd->tileOffsets (dx, dy, lx, ly))
489 {
490 THROW (Iex::ArgExc,
491 "Attempt to write tile "
492 "(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
493 "more than once.");
494 }
495
496 //
497 // If tiles can be written in random order, then don't buffer anything.
498 //
499
500 if (ofd->lineOrder == RANDOM_Y)
501 {
502 writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
503 return;
504 }
505
506 //
507 // If the tiles cannot be written in random order, then check if a
508 // tile with coordinates (dx,dy,lx,ly) has already been buffered.
509 //
510
511 TileCoord currentTile = TileCoord(dx, dy, lx, ly);
512
513 if (ofd->tileMap.find (currentTile) != ofd->tileMap.end())
514 {
515 THROW (Iex::ArgExc,
516 "Attempt to write tile "
517 "(" << dx << ", " << dy << ", " << lx << "," << ly << ") "
518 "more than once.");
519 }
520
521 //
522 // If all the tiles before this one have already been written to the file,
523 // then write this tile immediately and check if we have buffered tiles
524 // that can be written after this tile.
525 //
526 // Otherwise, buffer the tile so it can be written to file later.
527 //
528
529 if (ofd->nextTileToWrite == currentTile)
530 {
531 writeTileData (ofd, dx, dy, lx, ly, pixelData, pixelDataSize);
532 ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
533
534 TileMap::iterator i = ofd->tileMap.find (ofd->nextTileToWrite);
535
536 //
537 // Step through the tiles and write all successive buffered tiles after
538 // the current one.
539 //
540
541 while(i != ofd->tileMap.end())
542 {
543 //
544 // Write the tile, and then delete the tile's buffered data
545 //
546
547 writeTileData (ofd,
548 i->first.dx, i->first.dy,
549 i->first.lx, i->first.ly,
550 i->second->pixelData,
551 i->second->pixelDataSize);
552
553 delete i->second;
554 ofd->tileMap.erase (i);
555
556 //
557 // Proceed to the next tile
558 //
559
560 ofd->nextTileToWrite = ofd->nextTileCoord (ofd->nextTileToWrite);
561 i = ofd->tileMap.find (ofd->nextTileToWrite);
562 }
563 }
564 else
565 {
566 //
567 // Create a new BufferedTile, copy the pixelData into it, and
568 // insert it into the tileMap.
569 //
570
571 ofd->tileMap[currentTile] =
572 new BufferedTile ((const char *)pixelData, pixelDataSize);
573 }
574 }
575
576
577 void
convertToXdr(TiledOutputFile::Data * ofd,Array<char> & tileBuffer,int numScanLines,int numPixelsPerScanLine)578 convertToXdr (TiledOutputFile::Data *ofd,
579 Array<char>& tileBuffer,
580 int numScanLines,
581 int numPixelsPerScanLine)
582 {
583 //
584 // Convert the contents of a TiledOutputFile's tileBuffer from the
585 // machine's native representation to Xdr format. This function is called
586 // by writeTile(), below, if the compressor wanted its input pixel data
587 // in the machine's native format, but then failed to compress the data
588 // (most compressors will expand rather than compress random input data).
589 //
590 // Note that this routine assumes that the machine's native representation
591 // of the pixel data has the same size as the Xdr representation. This
592 // makes it possible to convert the pixel data in place, without an
593 // intermediate temporary buffer.
594 //
595
596 //
597 // Set these to point to the start of the tile.
598 // We will write to toPtr, and read from fromPtr.
599 //
600
601 char *writePtr = tileBuffer;
602 const char *readPtr = writePtr;
603
604 //
605 // Iterate over all scan lines in the tile.
606 //
607
608 for (int y = 0; y < numScanLines; ++y)
609 {
610 //
611 // Iterate over all slices in the file.
612 //
613
614 for (unsigned int i = 0; i < ofd->slices.size(); ++i)
615 {
616 const TOutSliceInfo &slice = ofd->slices[i];
617
618 //
619 // Convert the samples in place.
620 //
621
622 convertInPlace (writePtr, readPtr, slice.type,
623 numPixelsPerScanLine);
624 }
625 }
626
627 #ifdef DEBUG
628
629 assert (writePtr == readPtr);
630
631 #endif
632 }
633
634
635 //
636 // A TileBufferTask encapsulates the task of copying a tile from
637 // the user's framebuffer into a LineBuffer and compressing the data
638 // if necessary.
639 //
640
641 class TileBufferTask: public Task
642 {
643 public:
644
645 TileBufferTask (TaskGroup *group,
646 TiledOutputFile::Data *ofd,
647 int number,
648 int dx, int dy,
649 int lx, int ly);
650
651 virtual ~TileBufferTask ();
652
653 virtual void execute ();
654
655 private:
656
657 TiledOutputFile::Data * _ofd;
658 TileBuffer * _tileBuffer;
659 };
660
661
TileBufferTask(TaskGroup * group,TiledOutputFile::Data * ofd,int number,int dx,int dy,int lx,int ly)662 TileBufferTask::TileBufferTask
663 (TaskGroup *group,
664 TiledOutputFile::Data *ofd,
665 int number,
666 int dx, int dy,
667 int lx, int ly)
668 :
669 Task (group),
670 _ofd (ofd),
671 _tileBuffer (_ofd->getTileBuffer (number))
672 {
673 //
674 // Wait for the tileBuffer to become available
675 //
676
677 _tileBuffer->wait ();
678 _tileBuffer->tileCoord = TileCoord (dx, dy, lx, ly);
679 }
680
681
~TileBufferTask()682 TileBufferTask::~TileBufferTask ()
683 {
684 //
685 // Signal that the tile buffer is now free
686 //
687
688 _tileBuffer->post ();
689 }
690
691
692 void
execute()693 TileBufferTask::execute ()
694 {
695 try
696 {
697 //
698 // First copy the pixel data from the frame buffer
699 // into the tile buffer
700 //
701 // Convert one tile's worth of pixel data to
702 // a machine-independent representation, and store
703 // the result in _tileBuffer->buffer.
704 //
705
706 char *writePtr = _tileBuffer->buffer;
707
708 Box2i tileRange = Imf::dataWindowForTile (_ofd->tileDesc,
709 _ofd->minX, _ofd->maxX,
710 _ofd->minY, _ofd->maxY,
711 _tileBuffer->tileCoord.dx,
712 _tileBuffer->tileCoord.dy,
713 _tileBuffer->tileCoord.lx,
714 _tileBuffer->tileCoord.ly);
715
716 int numScanLines = tileRange.max.y - tileRange.min.y + 1;
717 int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
718
719 //
720 // Iterate over the scan lines in the tile.
721 //
722
723 for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
724 {
725 //
726 // Iterate over all image channels.
727 //
728
729 for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
730 {
731 const TOutSliceInfo &slice = _ofd->slices[i];
732
733 //
734 // These offsets are used to facilitate both absolute
735 // and tile-relative pixel coordinates.
736 //
737
738 int xOffset = slice.xTileCoords * tileRange.min.x;
739 int yOffset = slice.yTileCoords * tileRange.min.y;
740
741 //
742 // Fill the tile buffer with pixel data.
743 //
744
745 if (slice.zero)
746 {
747 //
748 // The frame buffer contains no data for this channel.
749 // Store zeroes in _data->tileBuffer.
750 //
751
752 fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
753 numPixelsPerScanLine);
754 }
755 else
756 {
757 //
758 // The frame buffer contains data for this channel.
759 //
760
761 const char *readPtr = slice.base +
762 (y - yOffset) * slice.yStride +
763 (tileRange.min.x - xOffset) *
764 slice.xStride;
765
766 const char *endPtr = readPtr +
767 (numPixelsPerScanLine - 1) *
768 slice.xStride;
769
770 copyFromFrameBuffer (writePtr, readPtr, endPtr,
771 slice.xStride, _ofd->format,
772 slice.type);
773 }
774 }
775 }
776
777 //
778 // Compress the contents of the tileBuffer,
779 // and store the compressed data in the output file.
780 //
781
782 _tileBuffer->dataSize = writePtr - _tileBuffer->buffer;
783 _tileBuffer->dataPtr = _tileBuffer->buffer;
784
785 if (_tileBuffer->compressor)
786 {
787 const char *compPtr;
788
789 int compSize = _tileBuffer->compressor->compressTile
790 (_tileBuffer->dataPtr,
791 _tileBuffer->dataSize,
792 tileRange, compPtr);
793
794 if (compSize < _tileBuffer->dataSize)
795 {
796 _tileBuffer->dataSize = compSize;
797 _tileBuffer->dataPtr = compPtr;
798 }
799 else if (_ofd->format == Compressor::NATIVE)
800 {
801 //
802 // The data did not shrink during compression, but
803 // we cannot write to the file using native format,
804 // so we need to convert the lineBuffer to Xdr.
805 //
806
807 convertToXdr (_ofd, _tileBuffer->buffer, numScanLines,
808 numPixelsPerScanLine);
809 }
810 }
811 }
812 catch (std::exception &e)
813 {
814 if (!_tileBuffer->hasException)
815 {
816 _tileBuffer->exception = e.what ();
817 _tileBuffer->hasException = true;
818 }
819 }
820 catch (...)
821 {
822 if (!_tileBuffer->hasException)
823 {
824 _tileBuffer->exception = "unrecognized exception";
825 _tileBuffer->hasException = true;
826 }
827 }
828 }
829
830 } // namespace
831
832
TiledOutputFile(const char fileName[],const Header & header,int numThreads)833 TiledOutputFile::TiledOutputFile
834 (const char fileName[],
835 const Header &header,
836 int numThreads)
837 :
838 _data (new Data (true, numThreads))
839 {
840 try
841 {
842 header.sanityCheck (true);
843 _data->os = new StdOFStream (fileName);
844 initialize (header);
845 }
846 catch (Iex::BaseExc &e)
847 {
848 delete _data;
849
850 REPLACE_EXC (e, "Cannot open image file "
851 "\"" << fileName << "\". " << e);
852 throw;
853 }
854 catch (...)
855 {
856 delete _data;
857 throw;
858 }
859 }
860
861
TiledOutputFile(OStream & os,const Header & header,int numThreads)862 TiledOutputFile::TiledOutputFile
863 (OStream &os,
864 const Header &header,
865 int numThreads)
866 :
867 _data (new Data (false, numThreads))
868 {
869 try
870 {
871 header.sanityCheck(true);
872 _data->os = &os;
873 initialize (header);
874 }
875 catch (Iex::BaseExc &e)
876 {
877 delete _data;
878
879 REPLACE_EXC (e, "Cannot open image file "
880 "\"" << os.fileName() << "\". " << e);
881 throw;
882 }
883 catch (...)
884 {
885 delete _data;
886 throw;
887 }
888 }
889
890
891 void
initialize(const Header & header)892 TiledOutputFile::initialize (const Header &header)
893 {
894 _data->header = header;
895 _data->lineOrder = _data->header.lineOrder();
896
897 //
898 // Check that the file is indeed tiled
899 //
900
901 _data->tileDesc = _data->header.tileDescription();
902
903 //
904 // Save the dataWindow information
905 //
906
907 const Box2i &dataWindow = _data->header.dataWindow();
908 _data->minX = dataWindow.min.x;
909 _data->maxX = dataWindow.max.x;
910 _data->minY = dataWindow.min.y;
911 _data->maxY = dataWindow.max.y;
912
913 //
914 // Precompute level and tile information to speed up utility functions
915 //
916
917 precalculateTileInfo (_data->tileDesc,
918 _data->minX, _data->maxX,
919 _data->minY, _data->maxY,
920 _data->numXTiles, _data->numYTiles,
921 _data->numXLevels, _data->numYLevels);
922
923 //
924 // Determine the first tile coordinate that we will be writing
925 // if the file is not RANDOM_Y.
926 //
927
928 _data->nextTileToWrite = (_data->lineOrder == INCREASING_Y)?
929 TileCoord (0, 0, 0, 0):
930 TileCoord (0, _data->numYTiles[0] - 1, 0, 0);
931
932 _data->maxBytesPerTileLine =
933 calculateBytesPerPixel (_data->header) * _data->tileDesc.xSize;
934
935 _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
936
937 //
938 // Create all the TileBuffers and allocate their internal buffers
939 //
940
941 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
942 {
943 _data->tileBuffers[i] = new TileBuffer (newTileCompressor
944 (_data->header.compression(),
945 _data->maxBytesPerTileLine,
946 _data->tileDesc.ySize,
947 _data->header));
948
949 _data->tileBuffers[i]->buffer.resizeErase(_data->tileBufferSize);
950 }
951
952 _data->format = defaultFormat (_data->tileBuffers[0]->compressor);
953
954 _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
955 _data->numXLevels,
956 _data->numYLevels,
957 _data->numXTiles,
958 _data->numYTiles);
959
960 _data->previewPosition = _data->header.writeTo (*_data->os, true);
961
962 _data->tileOffsetsPosition = _data->tileOffsets.writeTo (*_data->os);
963 _data->currentPosition = _data->os->tellp();
964 }
965
966
~TiledOutputFile()967 TiledOutputFile::~TiledOutputFile ()
968 {
969 if (_data)
970 {
971 {
972 if (_data->tileOffsetsPosition > 0)
973 {
974 try
975 {
976 _data->os->seekp (_data->tileOffsetsPosition);
977 _data->tileOffsets.writeTo (*_data->os);
978 }
979 catch (...)
980 {
981 //
982 // We cannot safely throw any exceptions from here.
983 // This destructor may have been called because the
984 // stack is currently being unwound for another
985 // exception.
986 //
987 }
988 }
989 }
990
991 delete _data;
992 }
993 }
994
995
996 const char *
fileName() const997 TiledOutputFile::fileName () const
998 {
999 return _data->os->fileName();
1000 }
1001
1002
1003 const Header &
header() const1004 TiledOutputFile::header () const
1005 {
1006 return _data->header;
1007 }
1008
1009
1010 void
setFrameBuffer(const FrameBuffer & frameBuffer)1011 TiledOutputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1012 {
1013 Lock lock (*_data);
1014
1015 //
1016 // Check if the new frame buffer descriptor
1017 // is compatible with the image file header.
1018 //
1019
1020 const ChannelList &channels = _data->header.channels();
1021
1022 for (ChannelList::ConstIterator i = channels.begin();
1023 i != channels.end();
1024 ++i)
1025 {
1026 FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1027
1028 if (j == frameBuffer.end())
1029 continue;
1030
1031 if (i.channel().type != j.slice().type)
1032 THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
1033 "of output file \"" << fileName() << "\" is "
1034 "not compatible with the frame buffer's "
1035 "pixel type.");
1036
1037 if (j.slice().xSampling != 1 || j.slice().ySampling != 1)
1038 THROW (Iex::ArgExc, "All channels in a tiled file must have"
1039 "sampling (1,1).");
1040 }
1041
1042 //
1043 // Initialize slice table for writePixels().
1044 //
1045
1046 vector<TOutSliceInfo> slices;
1047
1048 for (ChannelList::ConstIterator i = channels.begin();
1049 i != channels.end();
1050 ++i)
1051 {
1052 FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
1053
1054 if (j == frameBuffer.end())
1055 {
1056 //
1057 // Channel i is not present in the frame buffer.
1058 // In the file, channel i will contain only zeroes.
1059 //
1060
1061 slices.push_back (TOutSliceInfo (i.channel().type,
1062 0, // base
1063 0, // xStride,
1064 0, // yStride,
1065 true)); // zero
1066 }
1067 else
1068 {
1069 //
1070 // Channel i is present in the frame buffer.
1071 //
1072
1073 slices.push_back (TOutSliceInfo (j.slice().type,
1074 j.slice().base,
1075 j.slice().xStride,
1076 j.slice().yStride,
1077 false, // zero
1078 (j.slice().xTileCoords)? 1: 0,
1079 (j.slice().yTileCoords)? 1: 0));
1080 }
1081 }
1082
1083 //
1084 // Store the new frame buffer.
1085 //
1086
1087 _data->frameBuffer = frameBuffer;
1088 _data->slices = slices;
1089 }
1090
1091
1092 const FrameBuffer &
frameBuffer() const1093 TiledOutputFile::frameBuffer () const
1094 {
1095 Lock lock (*_data);
1096 return _data->frameBuffer;
1097 }
1098
1099
1100 void
writeTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1101 TiledOutputFile::writeTiles (int dx1, int dx2, int dy1, int dy2,
1102 int lx, int ly)
1103 {
1104 try
1105 {
1106 Lock lock (*_data);
1107
1108 if (_data->slices.size() == 0)
1109 throw Iex::ArgExc ("No frame buffer specified "
1110 "as pixel data source.");
1111
1112 if (!isValidTile (dx1, dy1, lx, ly) || !isValidTile (dx2, dy2, lx, ly))
1113 throw Iex::ArgExc ("Tile coordinates are invalid.");
1114
1115 //
1116 // Determine the first and last tile coordinates in both dimensions
1117 // based on the file's lineOrder
1118 //
1119
1120 if (dx1 > dx2)
1121 swap (dx1, dx2);
1122
1123 if (dy1 > dy2)
1124 swap (dy1, dy2);
1125
1126 int dyStart = dy1;
1127 int dyStop = dy2 + 1;
1128 int dY = 1;
1129
1130 if (_data->lineOrder == DECREASING_Y)
1131 {
1132 dyStart = dy2;
1133 dyStop = dy1 - 1;
1134 dY = -1;
1135 }
1136
1137 int numTiles = (dx2 - dx1 + 1) * (dy2 - dy1 + 1);
1138 int numTasks = min ((int)_data->tileBuffers.size(), numTiles);
1139
1140 //
1141 // Create a task group for all tile buffer tasks. When the
1142 // task group goes out of scope, the destructor waits until
1143 // all tasks are complete.
1144 //
1145
1146 {
1147 TaskGroup taskGroup;
1148
1149 //
1150 // Add in the initial compression tasks to the thread pool
1151 //
1152
1153 int nextCompBuffer = 0;
1154 int dxComp = dx1;
1155 int dyComp = dyStart;
1156
1157 while (nextCompBuffer < numTasks)
1158 {
1159 ThreadPool::addGlobalTask (new TileBufferTask (&taskGroup,
1160 _data,
1161 nextCompBuffer++,
1162 dxComp, dyComp,
1163 lx, ly));
1164 dxComp++;
1165
1166 if (dxComp > dx2)
1167 {
1168 dxComp = dx1;
1169 dyComp += dY;
1170 }
1171 }
1172
1173 //
1174 // Write the compressed buffers and add in more compression
1175 // tasks until done
1176 //
1177
1178 int nextWriteBuffer = 0;
1179 int dxWrite = dx1;
1180 int dyWrite = dyStart;
1181
1182 while (nextWriteBuffer < numTiles)
1183 {
1184 //
1185 // Wait until the nextWriteBuffer is ready to be written
1186 //
1187
1188 TileBuffer* writeBuffer =
1189 _data->getTileBuffer (nextWriteBuffer);
1190
1191 writeBuffer->wait();
1192
1193 //
1194 // Write the tilebuffer
1195 //
1196
1197 bufferedTileWrite (_data, dxWrite, dyWrite, lx, ly,
1198 writeBuffer->dataPtr,
1199 writeBuffer->dataSize);
1200
1201 //
1202 // Release the lock on nextWriteBuffer
1203 //
1204
1205 writeBuffer->post();
1206
1207 //
1208 // If there are no more tileBuffers to compress, then
1209 // only continue to write out remaining tileBuffers,
1210 // otherwise keep adding compression tasks.
1211 //
1212
1213 if (nextCompBuffer < numTiles)
1214 {
1215 //
1216 // add nextCompBuffer as a compression Task
1217 //
1218
1219 ThreadPool::addGlobalTask
1220 (new TileBufferTask (&taskGroup,
1221 _data,
1222 nextCompBuffer,
1223 dxComp, dyComp,
1224 lx, ly));
1225 }
1226
1227 nextWriteBuffer++;
1228 dxWrite++;
1229
1230 if (dxWrite > dx2)
1231 {
1232 dxWrite = dx1;
1233 dyWrite += dY;
1234 }
1235
1236 nextCompBuffer++;
1237 dxComp++;
1238
1239 if (dxComp > dx2)
1240 {
1241 dxComp = dx1;
1242 dyComp += dY;
1243 }
1244 }
1245
1246 //
1247 // finish all tasks
1248 //
1249 }
1250
1251 //
1252 // Exeption handling:
1253 //
1254 // TileBufferTask::execute() may have encountered exceptions, but
1255 // those exceptions occurred in another thread, not in the thread
1256 // that is executing this call to TiledOutputFile::writeTiles().
1257 // TileBufferTask::execute() has caught all exceptions and stored
1258 // the exceptions' what() strings in the tile buffers.
1259 // Now we check if any tile buffer contains a stored exception; if
1260 // this is the case then we re-throw the exception in this thread.
1261 // (It is possible that multiple tile buffers contain stored
1262 // exceptions. We re-throw the first exception we find and
1263 // ignore all others.)
1264 //
1265
1266 const string *exception = 0;
1267
1268 for (int i = 0; i < _data->tileBuffers.size(); ++i)
1269 {
1270 TileBuffer *tileBuffer = _data->tileBuffers[i];
1271
1272 if (tileBuffer->hasException && !exception)
1273 exception = &tileBuffer->exception;
1274
1275 tileBuffer->hasException = false;
1276 }
1277
1278 if (exception)
1279 throw Iex::IoExc (*exception);
1280 }
1281 catch (Iex::BaseExc &e)
1282 {
1283 REPLACE_EXC (e, "Failed to write pixel data to image "
1284 "file \"" << fileName() << "\". " << e);
1285 throw;
1286 }
1287 }
1288
1289
1290 void
writeTiles(int dx1,int dxMax,int dyMin,int dyMax,int l)1291 TiledOutputFile::writeTiles (int dx1, int dxMax, int dyMin, int dyMax, int l)
1292 {
1293 writeTiles (dx1, dxMax, dyMin, dyMax, l, l);
1294 }
1295
1296
1297 void
writeTile(int dx,int dy,int lx,int ly)1298 TiledOutputFile::writeTile (int dx, int dy, int lx, int ly)
1299 {
1300 writeTiles (dx, dx, dy, dy, lx, ly);
1301 }
1302
1303
1304 void
writeTile(int dx,int dy,int l)1305 TiledOutputFile::writeTile (int dx, int dy, int l)
1306 {
1307 writeTile(dx, dy, l, l);
1308 }
1309
1310
1311 void
copyPixels(TiledInputFile & in)1312 TiledOutputFile::copyPixels (TiledInputFile &in)
1313 {
1314 Lock lock (*_data);
1315
1316 //
1317 // Check if this file's and and the InputFile's
1318 // headers are compatible.
1319 //
1320
1321 const Header &hdr = _data->header;
1322 const Header &inHdr = in.header();
1323
1324 if (!hdr.hasTileDescription() || !inHdr.hasTileDescription())
1325 THROW (Iex::ArgExc, "Cannot perform a quick pixel copy from image "
1326 "file \"" << in.fileName() << "\" to image "
1327 "file \"" << fileName() << "\". The "
1328 "output file is tiled, but the input file is not. "
1329 "Try using OutputFile::copyPixels() instead.");
1330
1331 if (!(hdr.tileDescription() == inHdr.tileDescription()))
1332 THROW (Iex::ArgExc, "Quick pixel copy from image "
1333 "file \"" << in.fileName() << "\" to image "
1334 "file \"" << fileName() << "\" failed. "
1335 "The files have different tile descriptions.");
1336
1337 if (!(hdr.dataWindow() == inHdr.dataWindow()))
1338 THROW (Iex::ArgExc, "Cannot copy pixels from image "
1339 "file \"" << in.fileName() << "\" to image "
1340 "file \"" << fileName() << "\". The "
1341 "files have different data windows.");
1342
1343 if (!(hdr.lineOrder() == inHdr.lineOrder()))
1344 THROW (Iex::ArgExc, "Quick pixel copy from image "
1345 "file \"" << in.fileName() << "\" to image "
1346 "file \"" << fileName() << "\" failed. "
1347 "The files have different line orders.");
1348
1349 if (!(hdr.compression() == inHdr.compression()))
1350 THROW (Iex::ArgExc, "Quick pixel copy from image "
1351 "file \"" << in.fileName() << "\" to image "
1352 "file \"" << fileName() << "\" failed. "
1353 "The files use different compression methods.");
1354
1355 if (!(hdr.channels() == inHdr.channels()))
1356 THROW (Iex::ArgExc, "Quick pixel copy from image "
1357 "file \"" << in.fileName() << "\" to image "
1358 "file \"" << fileName() << "\" "
1359 "failed. The files have different channel "
1360 "lists.");
1361
1362 //
1363 // Verify that no pixel data have been written to this file yet.
1364 //
1365
1366 if (!_data->tileOffsets.isEmpty())
1367 THROW (Iex::LogicExc, "Quick pixel copy from image "
1368 "file \"" << in.fileName() << "\" to image "
1369 "file \"" << _data->os->fileName() << "\" "
1370 "failed. \"" << fileName() << "\" "
1371 "already contains pixel data.");
1372
1373 //
1374 // Calculate the total number of tiles in the file
1375 //
1376
1377 int numAllTiles = 0;
1378
1379 switch (levelMode ())
1380 {
1381 case ONE_LEVEL:
1382 case MIPMAP_LEVELS:
1383
1384 for (size_t i_l = 0; i_l < numLevels (); ++i_l)
1385 numAllTiles += numXTiles (i_l) * numYTiles (i_l);
1386
1387 break;
1388
1389 case RIPMAP_LEVELS:
1390
1391 for (size_t i_ly = 0; i_ly < numYLevels (); ++i_ly)
1392 for (size_t i_lx = 0; i_lx < numXLevels (); ++i_lx)
1393 numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
1394
1395 break;
1396
1397 default:
1398
1399 throw Iex::ArgExc ("Unknown LevelMode format.");
1400 }
1401
1402 for (int i = 0; i < numAllTiles; ++i)
1403 {
1404 const char *pixelData;
1405 int pixelDataSize;
1406
1407 int dx = _data->nextTileToWrite.dx;
1408 int dy = _data->nextTileToWrite.dy;
1409 int lx = _data->nextTileToWrite.lx;
1410 int ly = _data->nextTileToWrite.ly;
1411
1412 in.rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);
1413 writeTileData (_data, dx, dy, lx, ly, pixelData, pixelDataSize);
1414 }
1415 }
1416
1417
1418 void
copyPixels(InputFile & in)1419 TiledOutputFile::copyPixels (InputFile &in)
1420 {
1421 copyPixels (*in.tFile());
1422 }
1423
1424
1425 unsigned int
tileXSize() const1426 TiledOutputFile::tileXSize () const
1427 {
1428 return _data->tileDesc.xSize;
1429 }
1430
1431
1432 unsigned int
tileYSize() const1433 TiledOutputFile::tileYSize () const
1434 {
1435 return _data->tileDesc.ySize;
1436 }
1437
1438
1439 LevelMode
levelMode() const1440 TiledOutputFile::levelMode () const
1441 {
1442 return _data->tileDesc.mode;
1443 }
1444
1445
1446 LevelRoundingMode
levelRoundingMode() const1447 TiledOutputFile::levelRoundingMode () const
1448 {
1449 return _data->tileDesc.roundingMode;
1450 }
1451
1452
1453 int
numLevels() const1454 TiledOutputFile::numLevels () const
1455 {
1456 if (levelMode() == RIPMAP_LEVELS)
1457 THROW (Iex::LogicExc, "Error calling numLevels() on image "
1458 "file \"" << fileName() << "\" "
1459 "(numLevels() is not defined for RIPMAPs).");
1460 return _data->numXLevels;
1461 }
1462
1463
1464 int
numXLevels() const1465 TiledOutputFile::numXLevels () const
1466 {
1467 return _data->numXLevels;
1468 }
1469
1470
1471 int
numYLevels() const1472 TiledOutputFile::numYLevels () const
1473 {
1474 return _data->numYLevels;
1475 }
1476
1477
1478 bool
isValidLevel(int lx,int ly) const1479 TiledOutputFile::isValidLevel (int lx, int ly) const
1480 {
1481 if (lx < 0 || ly < 0)
1482 return false;
1483
1484 if (levelMode() == MIPMAP_LEVELS && lx != ly)
1485 return false;
1486
1487 if (lx >= numXLevels() || ly >= numYLevels())
1488 return false;
1489
1490 return true;
1491 }
1492
1493
1494 int
levelWidth(int lx) const1495 TiledOutputFile::levelWidth (int lx) const
1496 {
1497 try
1498 {
1499 int retVal = levelSize (_data->minX, _data->maxX, lx,
1500 _data->tileDesc.roundingMode);
1501
1502 return retVal;
1503 }
1504 catch (Iex::BaseExc &e)
1505 {
1506 REPLACE_EXC (e, "Error calling levelWidth() on image "
1507 "file \"" << fileName() << "\". " << e);
1508 throw;
1509 }
1510 }
1511
1512
1513 int
levelHeight(int ly) const1514 TiledOutputFile::levelHeight (int ly) const
1515 {
1516 try
1517 {
1518 return levelSize (_data->minY, _data->maxY, ly,
1519 _data->tileDesc.roundingMode);
1520 }
1521 catch (Iex::BaseExc &e)
1522 {
1523 REPLACE_EXC (e, "Error calling levelHeight() on image "
1524 "file \"" << fileName() << "\". " << e);
1525 throw;
1526 }
1527 }
1528
1529
1530 int
numXTiles(int lx) const1531 TiledOutputFile::numXTiles (int lx) const
1532 {
1533 if (lx < 0 || lx >= _data->numXLevels)
1534 THROW (Iex::LogicExc, "Error calling numXTiles() on image "
1535 "file \"" << _data->os->fileName() << "\" "
1536 "(Argument is not in valid range).");
1537
1538 return _data->numXTiles[lx];
1539 }
1540
1541
1542 int
numYTiles(int ly) const1543 TiledOutputFile::numYTiles (int ly) const
1544 {
1545 if (ly < 0 || ly >= _data->numYLevels)
1546 THROW (Iex::LogicExc, "Error calling numXTiles() on image "
1547 "file \"" << _data->os->fileName() << "\" "
1548 "(Argument is not in valid range).");
1549
1550 return _data->numYTiles[ly];
1551 }
1552
1553
1554 Box2i
dataWindowForLevel(int l) const1555 TiledOutputFile::dataWindowForLevel (int l) const
1556 {
1557 return dataWindowForLevel (l, l);
1558 }
1559
1560
1561 Box2i
dataWindowForLevel(int lx,int ly) const1562 TiledOutputFile::dataWindowForLevel (int lx, int ly) const
1563 {
1564 try
1565 {
1566 return Imf::dataWindowForLevel (_data->tileDesc,
1567 _data->minX, _data->maxX,
1568 _data->minY, _data->maxY,
1569 lx, ly);
1570 }
1571 catch (Iex::BaseExc &e)
1572 {
1573 REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1574 "file \"" << fileName() << "\". " << e);
1575 throw;
1576 }
1577 }
1578
1579
1580 Box2i
dataWindowForTile(int dx,int dy,int l) const1581 TiledOutputFile::dataWindowForTile (int dx, int dy, int l) const
1582 {
1583 return dataWindowForTile (dx, dy, l, l);
1584 }
1585
1586
1587 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1588 TiledOutputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1589 {
1590 try
1591 {
1592 if (!isValidTile (dx, dy, lx, ly))
1593 throw Iex::ArgExc ("Arguments not in valid range.");
1594
1595 return Imf::dataWindowForTile (_data->tileDesc,
1596 _data->minX, _data->maxX,
1597 _data->minY, _data->maxY,
1598 dx, dy,
1599 lx, ly);
1600 }
1601 catch (Iex::BaseExc &e)
1602 {
1603 REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1604 "file \"" << fileName() << "\". " << e);
1605 throw;
1606 }
1607 }
1608
1609
1610 bool
isValidTile(int dx,int dy,int lx,int ly) const1611 TiledOutputFile::isValidTile (int dx, int dy, int lx, int ly) const
1612 {
1613 return ((lx < _data->numXLevels && lx >= 0) &&
1614 (ly < _data->numYLevels && ly >= 0) &&
1615 (dx < _data->numXTiles[lx] && dx >= 0) &&
1616 (dy < _data->numYTiles[ly] && dy >= 0));
1617 }
1618
1619
1620 void
updatePreviewImage(const PreviewRgba newPixels[])1621 TiledOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
1622 {
1623 Lock lock (*_data);
1624
1625 if (_data->previewPosition <= 0)
1626 THROW (Iex::LogicExc, "Cannot update preview image pixels. "
1627 "File \"" << fileName() << "\" does not "
1628 "contain a preview image.");
1629
1630 //
1631 // Store the new pixels in the header's preview image attribute.
1632 //
1633
1634 PreviewImageAttribute &pia =
1635 _data->header.typedAttribute <PreviewImageAttribute> ("preview");
1636
1637 PreviewImage &pi = pia.value();
1638 PreviewRgba *pixels = pi.pixels();
1639 int numPixels = pi.width() * pi.height();
1640
1641 for (int i = 0; i < numPixels; ++i)
1642 pixels[i] = newPixels[i];
1643
1644 //
1645 // Save the current file position, jump to the position in
1646 // the file where the preview image starts, store the new
1647 // preview image, and jump back to the saved file position.
1648 //
1649
1650 Int64 savedPosition = _data->os->tellp();
1651
1652 try
1653 {
1654 _data->os->seekp (_data->previewPosition);
1655 pia.writeValueTo (*_data->os, _data->version);
1656 _data->os->seekp (savedPosition);
1657 }
1658 catch (Iex::BaseExc &e)
1659 {
1660 REPLACE_EXC (e, "Cannot update preview image pixels for "
1661 "file \"" << fileName() << "\". " << e);
1662 throw;
1663 }
1664 }
1665
1666
1667 void
breakTile(int dx,int dy,int lx,int ly,int offset,int length,char c)1668 TiledOutputFile::breakTile
1669 (int dx, int dy,
1670 int lx, int ly,
1671 int offset,
1672 int length,
1673 char c)
1674 {
1675 Lock lock (*_data);
1676
1677 Int64 position = _data->tileOffsets (dx, dy, lx, ly);
1678
1679 if (!position)
1680 THROW (Iex::ArgExc,
1681 "Cannot overwrite tile "
1682 "(" << dx << ", " << dy << ", " << lx << "," << ly << "). "
1683 "The tile has not yet been stored in "
1684 "file \"" << fileName() << "\".");
1685
1686 _data->currentPosition = 0;
1687 _data->os->seekp (position + offset);
1688
1689 for (int i = 0; i < length; ++i)
1690 _data->os->write (&c, 1);
1691 }
1692
1693 } // namespace Imf
1694