/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfDeepTiledInputFile.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////////// |
2 | | // |
3 | | // Copyright (c) 2011, 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 DeepTiledInputFile |
38 | | // |
39 | | //----------------------------------------------------------------------------- |
40 | | |
41 | | #include <ImfDeepTiledInputFile.h> |
42 | | #include <ImfTileDescriptionAttribute.h> |
43 | | #include <ImfChannelList.h> |
44 | | #include <ImfMisc.h> |
45 | | #include <ImfTiledMisc.h> |
46 | | #include <ImfStdIO.h> |
47 | | #include <ImfCompressor.h> |
48 | | #include "ImathBox.h" |
49 | | #include <ImfXdr.h> |
50 | | #include <ImfConvert.h> |
51 | | #include <ImfVersion.h> |
52 | | #include <ImfTileOffsets.h> |
53 | | #include <ImfThreading.h> |
54 | | #include <ImfPartType.h> |
55 | | #include <ImfMultiPartInputFile.h> |
56 | | #include "IlmThreadPool.h" |
57 | | #include "IlmThreadSemaphore.h" |
58 | | #include "IlmThreadMutex.h" |
59 | | #include "ImfInputStreamMutex.h" |
60 | | #include "ImfInputPartData.h" |
61 | | #include "ImathVec.h" |
62 | | #include "Iex.h" |
63 | | #include <string> |
64 | | #include <vector> |
65 | | #include <algorithm> |
66 | | #include <assert.h> |
67 | | #include <limits> |
68 | | |
69 | | #include "ImfNamespace.h" |
70 | | |
71 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
72 | | |
73 | | using IMATH_NAMESPACE::Box2i; |
74 | | using IMATH_NAMESPACE::V2i; |
75 | | using std::string; |
76 | | using std::vector; |
77 | | using std::min; |
78 | | using std::max; |
79 | | using ILMTHREAD_NAMESPACE::Mutex; |
80 | | using ILMTHREAD_NAMESPACE::Lock; |
81 | | using ILMTHREAD_NAMESPACE::Semaphore; |
82 | | using ILMTHREAD_NAMESPACE::Task; |
83 | | using ILMTHREAD_NAMESPACE::TaskGroup; |
84 | | using ILMTHREAD_NAMESPACE::ThreadPool; |
85 | | |
86 | | namespace { |
87 | | |
88 | | struct TInSliceInfo |
89 | | { |
90 | | PixelType typeInFrameBuffer; |
91 | | PixelType typeInFile; |
92 | | char* pointerArrayBase; |
93 | | size_t xStride; |
94 | | size_t yStride; |
95 | | ptrdiff_t sampleStride; |
96 | | bool fill; |
97 | | bool skip; |
98 | | double fillValue; |
99 | | int xTileCoords; |
100 | | int yTileCoords; |
101 | | |
102 | | TInSliceInfo (PixelType typeInFrameBuffer = HALF, |
103 | | char * base = NULL, |
104 | | PixelType typeInFile = HALF, |
105 | | size_t xStride = 0, |
106 | | size_t yStride = 0, |
107 | | ptrdiff_t sampleStride = 0, |
108 | | bool fill = false, |
109 | | bool skip = false, |
110 | | double fillValue = 0.0, |
111 | | int xTileCoords = 0, |
112 | | int yTileCoords = 0); |
113 | | }; |
114 | | |
115 | | |
116 | | TInSliceInfo::TInSliceInfo (PixelType tifb, |
117 | | char * b, |
118 | | PixelType tifl, |
119 | | size_t xs, size_t ys, |
120 | | ptrdiff_t spst, |
121 | | bool f, bool s, |
122 | | double fv, |
123 | | int xtc, |
124 | | int ytc) |
125 | | : |
126 | | typeInFrameBuffer (tifb), |
127 | | typeInFile (tifl), |
128 | | pointerArrayBase (b), |
129 | | xStride (xs), |
130 | | yStride (ys), |
131 | | sampleStride (spst), |
132 | | fill (f), |
133 | | skip (s), |
134 | | fillValue (fv), |
135 | | xTileCoords (xtc), |
136 | | yTileCoords (ytc) |
137 | 0 | { |
138 | | // empty |
139 | 0 | } |
140 | | |
141 | | |
142 | | struct TileBuffer |
143 | | { |
144 | | Array2D<unsigned int> sampleCount; |
145 | | const char * uncompressedData; |
146 | | char * buffer; |
147 | | Int64 dataSize; |
148 | | Int64 uncompressedDataSize; |
149 | | Compressor * compressor; |
150 | | Compressor::Format format; |
151 | | int dx; |
152 | | int dy; |
153 | | int lx; |
154 | | int ly; |
155 | | bool hasException; |
156 | | string exception; |
157 | | |
158 | | TileBuffer (); |
159 | | ~TileBuffer (); |
160 | | |
161 | 0 | inline void wait () {_sem.wait();} |
162 | 0 | inline void post () {_sem.post();} |
163 | | |
164 | | protected: |
165 | | |
166 | | Semaphore _sem; |
167 | | }; |
168 | | |
169 | | |
170 | | TileBuffer::TileBuffer (): |
171 | | uncompressedData (0), |
172 | | buffer (0), |
173 | | dataSize (0), |
174 | | compressor (0), |
175 | | format (defaultFormat (compressor)), |
176 | | dx (-1), |
177 | | dy (-1), |
178 | | lx (-1), |
179 | | ly (-1), |
180 | | hasException (false), |
181 | | exception (), |
182 | | _sem (1) |
183 | 0 | { |
184 | | // empty |
185 | 0 | } |
186 | | |
187 | | |
188 | | TileBuffer::~TileBuffer () |
189 | 0 | { |
190 | 0 | delete compressor; |
191 | 0 | } |
192 | | |
193 | | } // namespace |
194 | | |
195 | | |
196 | | class MultiPartInputFile; |
197 | | |
198 | | |
199 | | // |
200 | | // struct TiledInputFile::Data stores things that will be |
201 | | // needed between calls to readTile() |
202 | | // |
203 | | |
204 | | struct DeepTiledInputFile::Data: public Mutex |
205 | | { |
206 | | Header header; // the image header |
207 | | TileDescription tileDesc; // describes the tile layout |
208 | | int version; // file's version |
209 | | DeepFrameBuffer frameBuffer; // framebuffer to write into |
210 | | LineOrder lineOrder; // the file's lineorder |
211 | | int minX; // data window's min x coord |
212 | | int maxX; // data window's max x coord |
213 | | int minY; // data window's min y coord |
214 | | int maxY; // data window's max x coord |
215 | | |
216 | | int numXLevels; // number of x levels |
217 | | int numYLevels; // number of y levels |
218 | | int * numXTiles; // number of x tiles at a level |
219 | | int * numYTiles; // number of y tiles at a level |
220 | | |
221 | | TileOffsets tileOffsets; // stores offsets in file for |
222 | | // each tile |
223 | | |
224 | | bool fileIsComplete; // True if no tiles are missing |
225 | | // in the file |
226 | | |
227 | | vector<TInSliceInfo*> slices; // info about channels in file |
228 | | |
229 | | // ourselves? or does someone |
230 | | // else do it? |
231 | | |
232 | | int partNumber; // part number |
233 | | |
234 | | bool multiPartBackwardSupport; // if we are reading a multipart file |
235 | | // using OpenEXR 1.7 API |
236 | | |
237 | | int numThreads; // number of threads |
238 | | |
239 | | MultiPartInputFile* multiPartFile; // the MultiPartInputFile used to |
240 | | // support backward compatibility |
241 | | |
242 | | vector<TileBuffer*> tileBuffers; // each holds a single tile |
243 | | |
244 | | bool memoryMapped; // if the stream is memory mapped |
245 | | |
246 | | char* sampleCountSliceBase; // pointer to the start of |
247 | | // the sample count array |
248 | | ptrdiff_t sampleCountXStride; // x stride of the sample count array |
249 | | ptrdiff_t sampleCountYStride; // y stride of the sample count array |
250 | | |
251 | | int sampleCountXTileCoords; // the value of xTileCoords from the |
252 | | // sample count slice |
253 | | int sampleCountYTileCoords; // the value of yTileCoords from the |
254 | | // sample count slice |
255 | | |
256 | | Array<char> sampleCountTableBuffer; // the buffer for sample count table |
257 | | |
258 | | Compressor* sampleCountTableComp; // the decompressor for sample count table |
259 | | |
260 | | Int64 maxSampleCountTableSize; // the max size in bytes for a pixel |
261 | | // sample count table |
262 | | int combinedSampleSize; // total size of all channels combined to check sampletable size |
263 | | |
264 | | InputStreamMutex * _streamData; |
265 | | bool _deleteStream; // should we delete the stream |
266 | | Data (int numThreads); |
267 | | ~Data (); |
268 | | |
269 | | inline TileBuffer * getTileBuffer (int number); |
270 | | // hash function from tile indices |
271 | | // into our vector of tile buffers |
272 | | |
273 | | int& getSampleCount(int x, int y); |
274 | | // get the number of samples |
275 | | // in each pixel |
276 | | }; |
277 | | |
278 | | |
279 | | DeepTiledInputFile::Data::Data (int numThreads): |
280 | | numXTiles (0), |
281 | | numYTiles (0), |
282 | | partNumber (-1), |
283 | | multiPartBackwardSupport(false), |
284 | | numThreads(numThreads), |
285 | | memoryMapped(false), |
286 | | _streamData(NULL), |
287 | | _deleteStream(false) |
288 | 0 | { |
289 | | // |
290 | | // We need at least one tileBuffer, but if threading is used, |
291 | | // to keep n threads busy we need 2*n tileBuffers |
292 | | // |
293 | |
|
294 | 0 | tileBuffers.resize (max (1, 2 * numThreads)); |
295 | 0 | } |
296 | | |
297 | | |
298 | | DeepTiledInputFile::Data::~Data () |
299 | 0 | { |
300 | 0 | delete [] numXTiles; |
301 | 0 | delete [] numYTiles; |
302 | |
|
303 | 0 | for (size_t i = 0; i < tileBuffers.size(); i++) |
304 | 0 | delete tileBuffers[i]; |
305 | |
|
306 | 0 | if (multiPartBackwardSupport) |
307 | 0 | delete multiPartFile; |
308 | |
|
309 | 0 | for (size_t i = 0; i < slices.size(); i++) |
310 | 0 | delete slices[i]; |
311 | 0 | } |
312 | | |
313 | | |
314 | | TileBuffer* |
315 | | DeepTiledInputFile::Data::getTileBuffer (int number) |
316 | 0 | { |
317 | 0 | return tileBuffers[number % tileBuffers.size()]; |
318 | 0 | } |
319 | | |
320 | | |
321 | | int& |
322 | | DeepTiledInputFile::Data::getSampleCount(int x, int y) |
323 | 0 | { |
324 | 0 | return sampleCount(sampleCountSliceBase, |
325 | 0 | sampleCountXStride, |
326 | 0 | sampleCountYStride, |
327 | 0 | x, y); |
328 | 0 | } |
329 | | |
330 | | |
331 | | namespace { |
332 | | |
333 | | void |
334 | | readTileData (InputStreamMutex *streamData, |
335 | | DeepTiledInputFile::Data *ifd, |
336 | | int dx, int dy, |
337 | | int lx, int ly, |
338 | | char *&buffer, |
339 | | Int64 &dataSize, |
340 | | Int64 &unpackedDataSize) |
341 | 0 | { |
342 | | // |
343 | | // Read a single tile block from the file and into the array pointed |
344 | | // to by buffer. If the file is memory-mapped, then we change where |
345 | | // buffer points instead of writing into the array (hence buffer needs |
346 | | // to be a reference to a char *). |
347 | | // |
348 | | |
349 | | // |
350 | | // Look up the location for this tile in the Index and |
351 | | // seek to that position if necessary |
352 | | // |
353 | |
|
354 | 0 | Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly); |
355 | |
|
356 | 0 | if (tileOffset == 0) |
357 | 0 | { |
358 | 0 | THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " << |
359 | 0 | lx << ", " << ly << ") is missing."); |
360 | 0 | } |
361 | | |
362 | | // |
363 | | // In a multi-part file, the next chunk does not need to |
364 | | // belong to the same part, so we have to compare the |
365 | | // offset here. |
366 | | // |
367 | | |
368 | 0 | if ( !isMultiPart(ifd->version) ) |
369 | 0 | { |
370 | 0 | if (streamData->currentPosition != tileOffset) |
371 | 0 | streamData->is->seekg(tileOffset); |
372 | 0 | } |
373 | 0 | else |
374 | 0 | { |
375 | | // |
376 | | // In a multi-part file, the file pointer may be moved by other |
377 | | // parts, so we have to ask tellg() where we are. |
378 | | // |
379 | 0 | if (streamData->is->tellg() != tileOffset) |
380 | 0 | streamData->is->seekg (tileOffset); |
381 | 0 | } |
382 | | |
383 | | // |
384 | | // Read the first few bytes of the tile (the header). |
385 | | // Verify that the tile coordinates and the level number |
386 | | // are correct. |
387 | | // |
388 | |
|
389 | 0 | int tileXCoord, tileYCoord, levelX, levelY; |
390 | |
|
391 | 0 | if (isMultiPart(ifd->version)) |
392 | 0 | { |
393 | 0 | int partNumber; |
394 | 0 | Xdr::read <StreamIO> (*streamData->is, partNumber); |
395 | 0 | if (partNumber != ifd->partNumber) |
396 | 0 | { |
397 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber |
398 | 0 | << ", should be " << ifd->partNumber << "."); |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | 0 | Xdr::read <StreamIO> (*streamData->is, tileXCoord); |
403 | 0 | Xdr::read <StreamIO> (*streamData->is, tileYCoord); |
404 | 0 | Xdr::read <StreamIO> (*streamData->is, levelX); |
405 | 0 | Xdr::read <StreamIO> (*streamData->is, levelY); |
406 | |
|
407 | 0 | Int64 tableSize; |
408 | 0 | Xdr::read <StreamIO> (*streamData->is, tableSize); |
409 | |
|
410 | 0 | Xdr::read <StreamIO> (*streamData->is, dataSize); |
411 | 0 | Xdr::read <StreamIO> (*streamData->is, unpackedDataSize); |
412 | | |
413 | | |
414 | | // |
415 | | // Skip the pixel sample count table because we have read this data. |
416 | | // |
417 | |
|
418 | 0 | Xdr::skip <StreamIO> (*streamData->is, tableSize); |
419 | | |
420 | |
|
421 | 0 | if (tileXCoord != dx) |
422 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate."); |
423 | | |
424 | 0 | if (tileYCoord != dy) |
425 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate."); |
426 | | |
427 | 0 | if (levelX != lx) |
428 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate."); |
429 | | |
430 | 0 | if (levelY != ly) |
431 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate."); |
432 | | |
433 | | // |
434 | | // Read the pixel data. |
435 | | // |
436 | | |
437 | 0 | if (streamData->is->isMemoryMapped ()) |
438 | 0 | buffer = streamData->is->readMemoryMapped (dataSize); |
439 | 0 | else |
440 | 0 | { |
441 | | // (TODO) check if the packed data size is too big? |
442 | | // (TODO) better memory management here. Don't delete buffer everytime. |
443 | 0 | if (buffer != 0) delete[] buffer; |
444 | 0 | buffer = new char[dataSize]; |
445 | 0 | streamData->is->read (buffer, dataSize); |
446 | 0 | } |
447 | | |
448 | | // |
449 | | // Keep track of which tile is the next one in |
450 | | // the file, so that we can avoid redundant seekg() |
451 | | // operations (seekg() can be fairly expensive). |
452 | | // |
453 | |
|
454 | 0 | streamData->currentPosition = tileOffset + 4 * Xdr::size<int>() + |
455 | 0 | 3 * Xdr::size<Int64>() + |
456 | 0 | tableSize + |
457 | 0 | dataSize; |
458 | 0 | } |
459 | | |
460 | | |
461 | | void |
462 | | readNextTileData (InputStreamMutex *streamData, |
463 | | DeepTiledInputFile::Data *ifd, |
464 | | int &dx, int &dy, |
465 | | int &lx, int &ly, |
466 | | char * & buffer, |
467 | | Int64 &dataSize, |
468 | | Int64 &unpackedDataSize) |
469 | 0 | { |
470 | 0 | // |
471 | 0 | // Read the next tile block from the file |
472 | 0 | // |
473 | 0 |
|
474 | 0 | // |
475 | 0 | // Read the first few bytes of the tile (the header). |
476 | 0 | // |
477 | 0 |
|
478 | 0 | Xdr::read <StreamIO> (*streamData->is, dx); |
479 | 0 | Xdr::read <StreamIO> (*streamData->is, dy); |
480 | 0 | Xdr::read <StreamIO> (*streamData->is, lx); |
481 | 0 | Xdr::read <StreamIO> (*streamData->is, ly); |
482 | 0 |
|
483 | 0 | Int64 tableSize; |
484 | 0 | Xdr::read <StreamIO> (*streamData->is, tableSize); |
485 | 0 |
|
486 | 0 | Xdr::read <StreamIO> (*streamData->is, dataSize); |
487 | 0 | Xdr::read <StreamIO> (*streamData->is, unpackedDataSize); |
488 | 0 |
|
489 | 0 | // |
490 | 0 | // Skip the pixel sample count table because we have read this data. |
491 | 0 | // |
492 | 0 |
|
493 | 0 | Xdr::skip <StreamIO> (*streamData->is, tableSize); |
494 | 0 |
|
495 | 0 | // |
496 | 0 | // Read the pixel data. |
497 | 0 | // |
498 | 0 |
|
499 | 0 | streamData->is->read (buffer, dataSize); |
500 | 0 |
|
501 | 0 | // |
502 | 0 | // Keep track of which tile is the next one in |
503 | 0 | // the file, so that we can avoid redundant seekg() |
504 | 0 | // operations (seekg() can be fairly expensive). |
505 | 0 | // |
506 | 0 |
|
507 | 0 | streamData->currentPosition += 4 * Xdr::size<int>() + |
508 | 0 | 3 * Xdr::size<Int64>() + |
509 | 0 | tableSize + |
510 | 0 | dataSize; |
511 | 0 | } |
512 | | |
513 | | |
514 | | // |
515 | | // A TileBufferTask encapsulates the task of uncompressing |
516 | | // a single tile and copying it into the frame buffer. |
517 | | // |
518 | | |
519 | | class TileBufferTask : public Task |
520 | | { |
521 | | public: |
522 | | |
523 | | TileBufferTask (TaskGroup *group, |
524 | | DeepTiledInputFile::Data *ifd, |
525 | | TileBuffer *tileBuffer); |
526 | | |
527 | | virtual ~TileBufferTask (); |
528 | | |
529 | | virtual void execute (); |
530 | | |
531 | | private: |
532 | | |
533 | | DeepTiledInputFile::Data * _ifd; |
534 | | TileBuffer * _tileBuffer; |
535 | | }; |
536 | | |
537 | | |
538 | | TileBufferTask::TileBufferTask |
539 | | (TaskGroup *group, |
540 | | DeepTiledInputFile::Data *ifd, |
541 | | TileBuffer *tileBuffer) |
542 | | : |
543 | | Task (group), |
544 | | _ifd (ifd), |
545 | | _tileBuffer (tileBuffer) |
546 | 0 | { |
547 | | // empty |
548 | 0 | } |
549 | | |
550 | | |
551 | | TileBufferTask::~TileBufferTask () |
552 | 0 | { |
553 | | // |
554 | | // Signal that the tile buffer is now free |
555 | | // |
556 | |
|
557 | 0 | _tileBuffer->post (); |
558 | 0 | } |
559 | | |
560 | | |
561 | | void |
562 | | TileBufferTask::execute () |
563 | 0 | { |
564 | 0 | try |
565 | 0 | { |
566 | | // |
567 | | // Calculate information about the tile |
568 | | // |
569 | |
|
570 | 0 | Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile ( |
571 | 0 | _ifd->tileDesc, |
572 | 0 | _ifd->minX, _ifd->maxX, |
573 | 0 | _ifd->minY, _ifd->maxY, |
574 | 0 | _tileBuffer->dx, |
575 | 0 | _tileBuffer->dy, |
576 | 0 | _tileBuffer->lx, |
577 | 0 | _tileBuffer->ly); |
578 | | |
579 | | // |
580 | | // Get the size of the tile. |
581 | | // |
582 | |
|
583 | 0 | Array<unsigned int> numPixelsPerScanLine; |
584 | 0 | numPixelsPerScanLine.resizeErase(tileRange.max.y - tileRange.min.y + 1); |
585 | |
|
586 | 0 | int sizeOfTile = 0; |
587 | 0 | int maxBytesPerTileLine = 0; |
588 | |
|
589 | 0 | for (int y = tileRange.min.y; y <= tileRange.max.y; y++) |
590 | 0 | { |
591 | 0 | numPixelsPerScanLine[y - tileRange.min.y] = 0; |
592 | |
|
593 | 0 | int bytesPerLine = 0; |
594 | |
|
595 | 0 | for (int x = tileRange.min.x; x <= tileRange.max.x; x++) |
596 | 0 | { |
597 | 0 | int xOffset = _ifd->sampleCountXTileCoords * tileRange.min.x; |
598 | 0 | int yOffset = _ifd->sampleCountYTileCoords * tileRange.min.y; |
599 | |
|
600 | 0 | int count = _ifd->getSampleCount(x - xOffset, y - yOffset); |
601 | 0 | for (unsigned int c = 0; c < _ifd->slices.size(); ++c) |
602 | 0 | { |
603 | 0 | sizeOfTile += count * pixelTypeSize(_ifd->slices[c]->typeInFile); |
604 | 0 | bytesPerLine += count * pixelTypeSize(_ifd->slices[c]->typeInFile); |
605 | 0 | } |
606 | 0 | numPixelsPerScanLine[y - tileRange.min.y] += count; |
607 | 0 | } |
608 | |
|
609 | 0 | if (bytesPerLine > maxBytesPerTileLine) |
610 | 0 | maxBytesPerTileLine = bytesPerLine; |
611 | 0 | } |
612 | | |
613 | | // (TODO) don't do this every time. |
614 | 0 | if (_tileBuffer->compressor != 0) |
615 | 0 | delete _tileBuffer->compressor; |
616 | 0 | _tileBuffer->compressor = newTileCompressor |
617 | 0 | (_ifd->header.compression(), |
618 | 0 | maxBytesPerTileLine, |
619 | 0 | _ifd->tileDesc.ySize, |
620 | 0 | _ifd->header); |
621 | | |
622 | | // |
623 | | // Uncompress the data, if necessary |
624 | | // |
625 | |
|
626 | 0 | if (_tileBuffer->compressor && _tileBuffer->dataSize < Int64(sizeOfTile)) |
627 | 0 | { |
628 | 0 | _tileBuffer->format = _tileBuffer->compressor->format(); |
629 | |
|
630 | 0 | _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile |
631 | 0 | (_tileBuffer->buffer, _tileBuffer->dataSize, |
632 | 0 | tileRange, _tileBuffer->uncompressedData); |
633 | 0 | } |
634 | 0 | else |
635 | 0 | { |
636 | | // |
637 | | // If the line is uncompressed, it's in XDR format, |
638 | | // regardless of the compressor's output format. |
639 | | // |
640 | |
|
641 | 0 | _tileBuffer->format = Compressor::XDR; |
642 | 0 | _tileBuffer->uncompressedData = _tileBuffer->buffer; |
643 | 0 | } |
644 | | |
645 | | // |
646 | | // Convert the tile of pixel data back from the machine-independent |
647 | | // representation, and store the result in the frame buffer. |
648 | | // |
649 | |
|
650 | 0 | const char *readPtr = _tileBuffer->uncompressedData; |
651 | | // points to where we |
652 | | // read from in the |
653 | | // tile block |
654 | | |
655 | | // |
656 | | // Iterate over the scan lines in the tile. |
657 | | // |
658 | |
|
659 | 0 | for (int y = tileRange.min.y; y <= tileRange.max.y; ++y) |
660 | 0 | { |
661 | | // |
662 | | // Iterate over all image channels. |
663 | | // |
664 | |
|
665 | 0 | for (unsigned int i = 0; i < _ifd->slices.size(); ++i) |
666 | 0 | { |
667 | 0 | TInSliceInfo &slice = *_ifd->slices[i]; |
668 | | |
669 | | // |
670 | | // These offsets are used to facilitate both |
671 | | // absolute and tile-relative pixel coordinates. |
672 | | // |
673 | |
|
674 | 0 | int xOffsetForData = (slice.xTileCoords == 0) ? 0 : tileRange.min.x; |
675 | 0 | int yOffsetForData = (slice.yTileCoords == 0) ? 0 : tileRange.min.y; |
676 | 0 | int xOffsetForSampleCount = |
677 | 0 | (_ifd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x; |
678 | 0 | int yOffsetForSampleCount = |
679 | 0 | (_ifd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y; |
680 | | |
681 | | // |
682 | | // Fill the frame buffer with pixel data. |
683 | | // |
684 | |
|
685 | 0 | if (slice.skip) |
686 | 0 | { |
687 | | // |
688 | | // The file contains data for this channel, but |
689 | | // the frame buffer contains no slice for this channel. |
690 | | // |
691 | |
|
692 | 0 | skipChannel (readPtr, slice.typeInFile, |
693 | 0 | numPixelsPerScanLine[y - tileRange.min.y]); |
694 | 0 | } |
695 | 0 | else |
696 | 0 | { |
697 | | // |
698 | | // The frame buffer contains a slice for this channel. |
699 | | // |
700 | |
|
701 | 0 | copyIntoDeepFrameBuffer (readPtr, slice.pointerArrayBase, |
702 | 0 | _ifd->sampleCountSliceBase, |
703 | 0 | _ifd->sampleCountXStride, |
704 | 0 | _ifd->sampleCountYStride, |
705 | 0 | y, |
706 | 0 | tileRange.min.x, |
707 | 0 | tileRange.max.x, |
708 | 0 | xOffsetForSampleCount, yOffsetForSampleCount, |
709 | 0 | xOffsetForData, yOffsetForData, |
710 | 0 | slice.sampleStride, |
711 | 0 | slice.xStride, |
712 | 0 | slice.yStride, |
713 | 0 | slice.fill, |
714 | 0 | slice.fillValue, _tileBuffer->format, |
715 | 0 | slice.typeInFrameBuffer, |
716 | 0 | slice.typeInFile); |
717 | 0 | } |
718 | 0 | } |
719 | 0 | } |
720 | 0 | } |
721 | 0 | catch (std::exception &e) |
722 | 0 | { |
723 | 0 | if (!_tileBuffer->hasException) |
724 | 0 | { |
725 | 0 | _tileBuffer->exception = e.what (); |
726 | 0 | _tileBuffer->hasException = true; |
727 | 0 | } |
728 | 0 | } |
729 | 0 | catch (...) |
730 | 0 | { |
731 | 0 | if (!_tileBuffer->hasException) |
732 | 0 | { |
733 | 0 | _tileBuffer->exception = "unrecognized exception"; |
734 | 0 | _tileBuffer->hasException = true; |
735 | 0 | } |
736 | 0 | } |
737 | 0 | } |
738 | | |
739 | | |
740 | | TileBufferTask * |
741 | | newTileBufferTask |
742 | | (TaskGroup *group, |
743 | | DeepTiledInputFile::Data *ifd, |
744 | | int number, |
745 | | int dx, int dy, |
746 | | int lx, int ly) |
747 | 0 | { |
748 | | // |
749 | | // Wait for a tile buffer to become available, |
750 | | // fill the buffer with raw data from the file, |
751 | | // and create a new TileBufferTask whose execute() |
752 | | // method will uncompress the tile and copy the |
753 | | // tile's pixels into the frame buffer. |
754 | | // |
755 | |
|
756 | 0 | TileBuffer *tileBuffer = ifd->getTileBuffer (number); |
757 | |
|
758 | 0 | try |
759 | 0 | { |
760 | 0 | tileBuffer->wait(); |
761 | |
|
762 | 0 | tileBuffer->dx = dx; |
763 | 0 | tileBuffer->dy = dy; |
764 | 0 | tileBuffer->lx = lx; |
765 | 0 | tileBuffer->ly = ly; |
766 | |
|
767 | 0 | tileBuffer->uncompressedData = 0; |
768 | |
|
769 | 0 | readTileData (ifd->_streamData, ifd, dx, dy, lx, ly, |
770 | 0 | tileBuffer->buffer, |
771 | 0 | tileBuffer->dataSize, |
772 | 0 | tileBuffer->uncompressedDataSize); |
773 | 0 | } |
774 | 0 | catch (...) |
775 | 0 | { |
776 | | // |
777 | | // Reading from the file caused an exception. |
778 | | // Signal that the tile buffer is free, and |
779 | | // re-throw the exception. |
780 | | // |
781 | |
|
782 | 0 | tileBuffer->post(); |
783 | 0 | throw; |
784 | 0 | } |
785 | | |
786 | 0 | return new TileBufferTask (group, ifd, tileBuffer); |
787 | 0 | } |
788 | | |
789 | | |
790 | | } // namespace |
791 | | |
792 | | |
793 | | DeepTiledInputFile::DeepTiledInputFile (const char fileName[], int numThreads): |
794 | | _data (new Data (numThreads)) |
795 | 0 | { |
796 | 0 | _data->_deleteStream=true; |
797 | | // |
798 | | // This constructor is called when a user |
799 | | // explicitly wants to read a tiled file. |
800 | | // |
801 | |
|
802 | 0 | IStream* is = 0; |
803 | 0 | try |
804 | 0 | { |
805 | 0 | is = new StdIFStream (fileName); |
806 | 0 | readMagicNumberAndVersionField(*is, _data->version); |
807 | | |
808 | | // |
809 | | // Compatibility to read multpart file. |
810 | | // |
811 | 0 | if (isMultiPart(_data->version)) |
812 | 0 | { |
813 | 0 | compatibilityInitialize(*is); |
814 | 0 | } |
815 | 0 | else |
816 | 0 | { |
817 | 0 | _data->_streamData = new InputStreamMutex(); |
818 | 0 | _data->_streamData->is = is; |
819 | 0 | _data->header.readFrom (*_data->_streamData->is, _data->version); |
820 | 0 | initialize(); |
821 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true); |
822 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
823 | 0 | } |
824 | 0 | } |
825 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
826 | 0 | { |
827 | 0 | if (is) delete is; |
828 | 0 | if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData; |
829 | 0 | if (_data) delete _data; |
830 | |
|
831 | 0 | REPLACE_EXC (e, "Cannot open image file " |
832 | 0 | "\"" << fileName << "\". " << e); |
833 | 0 | throw; |
834 | 0 | } |
835 | 0 | catch (...) |
836 | 0 | { |
837 | 0 | if (is) delete is; |
838 | 0 | if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData; |
839 | 0 | if (_data) delete _data; |
840 | |
|
841 | 0 | throw; |
842 | 0 | } |
843 | 0 | } |
844 | | |
845 | | |
846 | | DeepTiledInputFile::DeepTiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads): |
847 | | _data (new Data (numThreads)) |
848 | 0 | { |
849 | 0 | _data->_streamData=0; |
850 | 0 | _data->_deleteStream=false; |
851 | | |
852 | | // |
853 | | // This constructor is called when a user |
854 | | // explicitly wants to read a tiled file. |
855 | | // |
856 | |
|
857 | 0 | try |
858 | 0 | { |
859 | 0 | readMagicNumberAndVersionField(is, _data->version); |
860 | | |
861 | | // |
862 | | // Backward compatibility to read multpart file. |
863 | | // |
864 | 0 | if (isMultiPart(_data->version)) |
865 | 0 | { |
866 | 0 | compatibilityInitialize(is); |
867 | 0 | } |
868 | 0 | else |
869 | 0 | { |
870 | 0 | _data->_streamData = new InputStreamMutex(); |
871 | 0 | _data->_streamData->is = &is; |
872 | 0 | _data->header.readFrom (*_data->_streamData->is, _data->version); |
873 | 0 | initialize(); |
874 | | // file is guaranteed not to be multipart, but is deep |
875 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete, false,true); |
876 | 0 | _data->memoryMapped = _data->_streamData->is->isMemoryMapped(); |
877 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
878 | 0 | } |
879 | 0 | } |
880 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
881 | 0 | { |
882 | 0 | if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData; |
883 | 0 | if (_data) delete _data; |
884 | |
|
885 | 0 | REPLACE_EXC (e, "Cannot open image file " |
886 | 0 | "\"" << is.fileName() << "\". " << e); |
887 | 0 | throw; |
888 | 0 | } |
889 | 0 | catch (...) |
890 | 0 | { |
891 | 0 | if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData; |
892 | 0 | if (_data) delete _data; |
893 | |
|
894 | 0 | throw; |
895 | 0 | } |
896 | 0 | } |
897 | | |
898 | | |
899 | | DeepTiledInputFile::DeepTiledInputFile (const Header &header, |
900 | | OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is, |
901 | | int version, |
902 | | int numThreads) : |
903 | | _data (new Data (numThreads)) |
904 | | |
905 | 0 | { |
906 | 0 | _data->_streamData->is = is; |
907 | 0 | _data->_deleteStream=false; |
908 | | |
909 | | // |
910 | | // This constructor called by class Imf::InputFile |
911 | | // when a user wants to just read an image file, and |
912 | | // doesn't care or know if the file is tiled. |
913 | | // No need to have backward compatibility here, because |
914 | | // we have the header. |
915 | | // |
916 | |
|
917 | 0 | _data->header = header; |
918 | 0 | _data->version = version; |
919 | 0 | initialize(); |
920 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true); |
921 | 0 | _data->memoryMapped = is->isMemoryMapped(); |
922 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
923 | 0 | } |
924 | | |
925 | | |
926 | | DeepTiledInputFile::DeepTiledInputFile (InputPartData* part) : |
927 | | _data (new Data (part->numThreads)) |
928 | 0 | { |
929 | 0 | _data->_deleteStream=false; |
930 | 0 | multiPartInitialize(part); |
931 | 0 | } |
932 | | |
933 | | |
934 | | void |
935 | | DeepTiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is) |
936 | 0 | { |
937 | 0 | is.seekg(0); |
938 | | // |
939 | | // Construct a MultiPartInputFile, initialize TiledInputFile |
940 | | // with the part 0 data. |
941 | | // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later. |
942 | | // |
943 | 0 | _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads); |
944 | 0 | _data->multiPartBackwardSupport = true; |
945 | 0 | InputPartData* part = _data->multiPartFile->getPart(0); |
946 | |
|
947 | 0 | multiPartInitialize(part); |
948 | 0 | } |
949 | | |
950 | | |
951 | | void |
952 | | DeepTiledInputFile::multiPartInitialize(InputPartData* part) |
953 | 0 | { |
954 | 0 | if (isTiled(part->header.type()) == false) |
955 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Can't build a DeepTiledInputFile from a part of type " << part->header.type()); |
956 | | |
957 | 0 | _data->_streamData = part->mutex; |
958 | 0 | _data->header = part->header; |
959 | 0 | _data->version = part->version; |
960 | 0 | _data->partNumber = part->partNumber; |
961 | 0 | _data->memoryMapped = _data->_streamData->is->isMemoryMapped(); |
962 | 0 | initialize(); |
963 | 0 | _data->tileOffsets.readFrom(part->chunkOffsets , _data->fileIsComplete); |
964 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
965 | 0 | } |
966 | | |
967 | | |
968 | | void |
969 | | DeepTiledInputFile::initialize () |
970 | 0 | { |
971 | 0 | if (_data->partNumber == -1) |
972 | 0 | if (_data->header.type() != DEEPTILE) |
973 | 0 | throw IEX_NAMESPACE::ArgExc ("Expected a deep tiled file but the file is not deep tiled."); |
974 | 0 | if(_data->header.version()!=1) |
975 | 0 | { |
976 | 0 | THROW(IEX_NAMESPACE::ArgExc, "Version " << _data->header.version() << " not supported for deeptiled images in this version of the library"); |
977 | 0 | } |
978 | | |
979 | 0 | _data->header.sanityCheck (true); |
980 | |
|
981 | 0 | _data->tileDesc = _data->header.tileDescription(); |
982 | 0 | _data->lineOrder = _data->header.lineOrder(); |
983 | | |
984 | | // |
985 | | // Save the dataWindow information |
986 | | // |
987 | |
|
988 | 0 | const Box2i &dataWindow = _data->header.dataWindow(); |
989 | 0 | _data->minX = dataWindow.min.x; |
990 | 0 | _data->maxX = dataWindow.max.x; |
991 | 0 | _data->minY = dataWindow.min.y; |
992 | 0 | _data->maxY = dataWindow.max.y; |
993 | | |
994 | | // |
995 | | // Precompute level and tile information to speed up utility functions |
996 | | // |
997 | |
|
998 | 0 | precalculateTileInfo (_data->tileDesc, |
999 | 0 | _data->minX, _data->maxX, |
1000 | 0 | _data->minY, _data->maxY, |
1001 | 0 | _data->numXTiles, _data->numYTiles, |
1002 | 0 | _data->numXLevels, _data->numYLevels); |
1003 | | |
1004 | | // |
1005 | | // Create all the TileBuffers and allocate their internal buffers |
1006 | | // |
1007 | |
|
1008 | 0 | _data->tileOffsets = TileOffsets (_data->tileDesc.mode, |
1009 | 0 | _data->numXLevels, |
1010 | 0 | _data->numYLevels, |
1011 | 0 | _data->numXTiles, |
1012 | 0 | _data->numYTiles); |
1013 | |
|
1014 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); i++) |
1015 | 0 | _data->tileBuffers[i] = new TileBuffer (); |
1016 | |
|
1017 | 0 | _data->maxSampleCountTableSize = _data->tileDesc.ySize * |
1018 | 0 | _data->tileDesc.xSize * |
1019 | 0 | sizeof(int); |
1020 | |
|
1021 | 0 | _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize); |
1022 | |
|
1023 | 0 | _data->sampleCountTableComp = newCompressor(_data->header.compression(), |
1024 | 0 | _data->maxSampleCountTableSize, |
1025 | 0 | _data->header); |
1026 | | |
1027 | | |
1028 | 0 | const ChannelList & c=_data->header.channels(); |
1029 | 0 | _data->combinedSampleSize=0; |
1030 | 0 | for(ChannelList::ConstIterator i=c.begin();i!=c.end();i++) |
1031 | 0 | { |
1032 | 0 | switch( i.channel().type ) |
1033 | 0 | { |
1034 | 0 | case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF : |
1035 | 0 | _data->combinedSampleSize+=Xdr::size<half>(); |
1036 | 0 | break; |
1037 | 0 | case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT : |
1038 | 0 | _data->combinedSampleSize+=Xdr::size<float>(); |
1039 | 0 | break; |
1040 | 0 | case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT : |
1041 | 0 | _data->combinedSampleSize+=Xdr::size<unsigned int>(); |
1042 | 0 | break; |
1043 | 0 | default : |
1044 | 0 | THROW(IEX_NAMESPACE::ArgExc, "Bad type for channel " << i.name() << " initializing deepscanline reader"); |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | 0 | } |
1049 | | |
1050 | | |
1051 | | DeepTiledInputFile::~DeepTiledInputFile () |
1052 | 0 | { |
1053 | 0 | if (!_data->memoryMapped) |
1054 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); i++) |
1055 | 0 | if (_data->tileBuffers[i]->buffer != 0) |
1056 | 0 | delete [] _data->tileBuffers[i]->buffer; |
1057 | |
|
1058 | 0 | if (_data->_deleteStream) |
1059 | 0 | delete _data->_streamData->is; |
1060 | | |
1061 | | // |
1062 | | // (TODO) we should have a way to tell if the stream data is owned by this file or |
1063 | | // by a parent multipart file. |
1064 | | // |
1065 | |
|
1066 | 0 | if (_data->partNumber == -1) |
1067 | 0 | delete _data->_streamData; |
1068 | |
|
1069 | 0 | delete _data; |
1070 | 0 | } |
1071 | | |
1072 | | |
1073 | | const char * |
1074 | | DeepTiledInputFile::fileName () const |
1075 | 0 | { |
1076 | 0 | return _data->_streamData->is->fileName(); |
1077 | 0 | } |
1078 | | |
1079 | | |
1080 | | const Header & |
1081 | | DeepTiledInputFile::header () const |
1082 | 0 | { |
1083 | 0 | return _data->header; |
1084 | 0 | } |
1085 | | |
1086 | | |
1087 | | int |
1088 | | DeepTiledInputFile::version () const |
1089 | 0 | { |
1090 | 0 | return _data->version; |
1091 | 0 | } |
1092 | | |
1093 | | |
1094 | | void |
1095 | | DeepTiledInputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer) |
1096 | 0 | { |
1097 | 0 | Lock lock (*_data->_streamData); |
1098 | | |
1099 | | // |
1100 | | // Set the frame buffer |
1101 | | // |
1102 | | |
1103 | | // |
1104 | | // Check if the new frame buffer descriptor is |
1105 | | // compatible with the image file header. |
1106 | | // |
1107 | |
|
1108 | 0 | const ChannelList &channels = _data->header.channels(); |
1109 | |
|
1110 | 0 | for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin(); |
1111 | 0 | j != frameBuffer.end(); |
1112 | 0 | ++j) |
1113 | 0 | { |
1114 | 0 | ChannelList::ConstIterator i = channels.find (j.name()); |
1115 | |
|
1116 | 0 | if (i == channels.end()) |
1117 | 0 | continue; |
1118 | | |
1119 | 0 | if (i.channel().xSampling != j.slice().xSampling || |
1120 | 0 | i.channel().ySampling != j.slice().ySampling) |
1121 | 0 | THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors " |
1122 | 0 | "of \"" << i.name() << "\" channel " |
1123 | 0 | "of input file \"" << fileName() << "\" are " |
1124 | 0 | "not compatible with the frame buffer's " |
1125 | 0 | "subsampling factors."); |
1126 | 0 | } |
1127 | | |
1128 | | // |
1129 | | // Store the pixel sample count table. |
1130 | | // (TODO) Support for different sampling rates? |
1131 | | // |
1132 | | |
1133 | 0 | const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice(); |
1134 | 0 | if (sampleCountSlice.base == 0) |
1135 | 0 | { |
1136 | 0 | throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice."); |
1137 | 0 | } |
1138 | 0 | else |
1139 | 0 | { |
1140 | 0 | _data->sampleCountSliceBase = sampleCountSlice.base; |
1141 | 0 | _data->sampleCountXStride = sampleCountSlice.xStride; |
1142 | 0 | _data->sampleCountYStride = sampleCountSlice.yStride; |
1143 | 0 | _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords; |
1144 | 0 | _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords; |
1145 | 0 | } |
1146 | | |
1147 | | // |
1148 | | // Initialize the slice table for readPixels(). |
1149 | | // |
1150 | | |
1151 | 0 | vector<TInSliceInfo*> slices; |
1152 | 0 | ChannelList::ConstIterator i = channels.begin(); |
1153 | |
|
1154 | 0 | for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin(); |
1155 | 0 | j != frameBuffer.end(); |
1156 | 0 | ++j) |
1157 | 0 | { |
1158 | 0 | while (i != channels.end() && strcmp (i.name(), j.name()) < 0) |
1159 | 0 | { |
1160 | | // |
1161 | | // Channel i is present in the file but not |
1162 | | // in the frame buffer; data for channel i |
1163 | | // will be skipped during readPixels(). |
1164 | | // |
1165 | |
|
1166 | 0 | slices.push_back (new TInSliceInfo (i.channel().type, |
1167 | 0 | NULL, |
1168 | 0 | i.channel().type, |
1169 | 0 | 0, // xStride |
1170 | 0 | 0, // yStride |
1171 | 0 | 0, // sampleStride |
1172 | 0 | false, // fill |
1173 | 0 | true, // skip |
1174 | 0 | 0.0)); // fillValue |
1175 | 0 | ++i; |
1176 | 0 | } |
1177 | |
|
1178 | 0 | bool fill = false; |
1179 | |
|
1180 | 0 | if (i == channels.end() || strcmp (i.name(), j.name()) > 0) |
1181 | 0 | { |
1182 | | // |
1183 | | // Channel i is present in the frame buffer, but not in the file. |
1184 | | // In the frame buffer, slice j will be filled with a default value. |
1185 | | // |
1186 | |
|
1187 | 0 | fill = true; |
1188 | 0 | } |
1189 | |
|
1190 | 0 | slices.push_back (new TInSliceInfo (j.slice().type, |
1191 | 0 | j.slice().base, |
1192 | 0 | fill? j.slice().type: i.channel().type, |
1193 | 0 | j.slice().xStride, |
1194 | 0 | j.slice().yStride, |
1195 | 0 | j.slice().sampleStride, |
1196 | 0 | fill, |
1197 | 0 | false, // skip |
1198 | 0 | j.slice().fillValue, |
1199 | 0 | (j.slice().xTileCoords)? 1: 0, |
1200 | 0 | (j.slice().yTileCoords)? 1: 0)); |
1201 | | |
1202 | |
|
1203 | 0 | if (i != channels.end() && !fill) |
1204 | 0 | ++i; |
1205 | 0 | } |
1206 | | |
1207 | | // (TODO) inspect the following code. It's additional to the scanline input file. |
1208 | | // Is this needed? |
1209 | |
|
1210 | 0 | while (i != channels.end()) |
1211 | 0 | { |
1212 | | // |
1213 | | // Channel i is present in the file but not |
1214 | | // in the frame buffer; data for channel i |
1215 | | // will be skipped during readPixels(). |
1216 | | // |
1217 | |
|
1218 | 0 | slices.push_back (new TInSliceInfo (i.channel().type, |
1219 | 0 | NULL, |
1220 | 0 | i.channel().type, |
1221 | 0 | 0, // xStride |
1222 | 0 | 0, // yStride |
1223 | 0 | 0, // sampleStride |
1224 | 0 | false, // fill |
1225 | 0 | true, // skip |
1226 | 0 | 0.0)); // fillValue |
1227 | 0 | ++i; |
1228 | 0 | } |
1229 | | |
1230 | | // |
1231 | | // Store the new frame buffer. |
1232 | | // |
1233 | |
|
1234 | 0 | _data->frameBuffer = frameBuffer; |
1235 | |
|
1236 | 0 | for (size_t i = 0; i < _data->slices.size(); i++) |
1237 | 0 | delete _data->slices[i]; |
1238 | 0 | _data->slices = slices; |
1239 | 0 | } |
1240 | | |
1241 | | |
1242 | | const DeepFrameBuffer & |
1243 | | DeepTiledInputFile::frameBuffer () const |
1244 | 0 | { |
1245 | 0 | Lock lock (*_data->_streamData); |
1246 | 0 | return _data->frameBuffer; |
1247 | 0 | } |
1248 | | |
1249 | | |
1250 | | bool |
1251 | | DeepTiledInputFile::isComplete () const |
1252 | 0 | { |
1253 | 0 | return _data->fileIsComplete; |
1254 | 0 | } |
1255 | | |
1256 | | |
1257 | | void |
1258 | | DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly) |
1259 | 0 | { |
1260 | | // |
1261 | | // Read a range of tiles from the file into the framebuffer |
1262 | | // |
1263 | |
|
1264 | 0 | try |
1265 | 0 | { |
1266 | 0 | Lock lock (*_data->_streamData); |
1267 | |
|
1268 | 0 | if (_data->slices.size() == 0) |
1269 | 0 | throw IEX_NAMESPACE::ArgExc ("No frame buffer specified " |
1270 | 0 | "as pixel data destination."); |
1271 | | |
1272 | 0 | if (!isValidLevel (lx, ly)) |
1273 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1274 | 0 | "Level coordinate " |
1275 | 0 | "(" << lx << ", " << ly << ") " |
1276 | 0 | "is invalid."); |
1277 | | |
1278 | | // |
1279 | | // Determine the first and last tile coordinates in both dimensions. |
1280 | | // We always attempt to read the range of tiles in the order that |
1281 | | // they are stored in the file. |
1282 | | // |
1283 | | |
1284 | 0 | if (dx1 > dx2) |
1285 | 0 | std::swap (dx1, dx2); |
1286 | |
|
1287 | 0 | if (dy1 > dy2) |
1288 | 0 | std::swap (dy1, dy2); |
1289 | |
|
1290 | 0 | int dyStart = dy1; |
1291 | 0 | int dyStop = dy2 + 1; |
1292 | 0 | int dY = 1; |
1293 | |
|
1294 | 0 | if (_data->lineOrder == DECREASING_Y) |
1295 | 0 | { |
1296 | 0 | dyStart = dy2; |
1297 | 0 | dyStop = dy1 - 1; |
1298 | 0 | dY = -1; |
1299 | 0 | } |
1300 | | |
1301 | | // |
1302 | | // Create a task group for all tile buffer tasks. When the |
1303 | | // task group goes out of scope, the destructor waits until |
1304 | | // all tasks are complete. |
1305 | | // |
1306 | |
|
1307 | 0 | { |
1308 | 0 | TaskGroup taskGroup; |
1309 | 0 | int tileNumber = 0; |
1310 | |
|
1311 | 0 | for (int dy = dyStart; dy != dyStop; dy += dY) |
1312 | 0 | { |
1313 | 0 | for (int dx = dx1; dx <= dx2; dx++) |
1314 | 0 | { |
1315 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1316 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1317 | 0 | "Tile (" << dx << ", " << dy << ", " << |
1318 | 0 | lx << "," << ly << ") is not a valid tile."); |
1319 | | |
1320 | 0 | ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup, |
1321 | 0 | _data, |
1322 | 0 | tileNumber++, |
1323 | 0 | dx, dy, |
1324 | 0 | lx, ly)); |
1325 | 0 | } |
1326 | 0 | } |
1327 | | |
1328 | | // |
1329 | | // finish all tasks |
1330 | | // |
1331 | 0 | } |
1332 | | |
1333 | | // |
1334 | | // Exeption handling: |
1335 | | // |
1336 | | // TileBufferTask::execute() may have encountered exceptions, but |
1337 | | // those exceptions occurred in another thread, not in the thread |
1338 | | // that is executing this call to TiledInputFile::readTiles(). |
1339 | | // TileBufferTask::execute() has caught all exceptions and stored |
1340 | | // the exceptions' what() strings in the tile buffers. |
1341 | | // Now we check if any tile buffer contains a stored exception; if |
1342 | | // this is the case then we re-throw the exception in this thread. |
1343 | | // (It is possible that multiple tile buffers contain stored |
1344 | | // exceptions. We re-throw the first exception we find and |
1345 | | // ignore all others.) |
1346 | | // |
1347 | | |
1348 | 0 | const string *exception = 0; |
1349 | |
|
1350 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); ++i) |
1351 | 0 | { |
1352 | 0 | TileBuffer *tileBuffer = _data->tileBuffers[i]; |
1353 | |
|
1354 | 0 | if (tileBuffer->hasException && !exception) |
1355 | 0 | exception = &tileBuffer->exception; |
1356 | |
|
1357 | 0 | tileBuffer->hasException = false; |
1358 | 0 | } |
1359 | |
|
1360 | 0 | if (exception) |
1361 | 0 | throw IEX_NAMESPACE::IoExc (*exception); |
1362 | 0 | } |
1363 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1364 | 0 | { |
1365 | 0 | REPLACE_EXC (e, "Error reading pixel data from image " |
1366 | 0 | "file \"" << fileName() << "\". " << e); |
1367 | 0 | throw; |
1368 | 0 | } |
1369 | 0 | } |
1370 | | |
1371 | | |
1372 | | void |
1373 | | DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l) |
1374 | 0 | { |
1375 | 0 | readTiles (dx1, dx2, dy1, dy2, l, l); |
1376 | 0 | } |
1377 | | |
1378 | | |
1379 | | void |
1380 | | DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly) |
1381 | 0 | { |
1382 | 0 | readTiles (dx, dx, dy, dy, lx, ly); |
1383 | 0 | } |
1384 | | |
1385 | | |
1386 | | void |
1387 | | DeepTiledInputFile::readTile (int dx, int dy, int l) |
1388 | 0 | { |
1389 | 0 | readTile (dx, dy, l, l); |
1390 | 0 | } |
1391 | | |
1392 | | |
1393 | | void |
1394 | | DeepTiledInputFile::rawTileData (int &dx, int &dy, |
1395 | | int &lx, int &ly, |
1396 | | char * pixelData, |
1397 | | Int64 &pixelDataSize) const |
1398 | 0 | { |
1399 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1400 | 0 | throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside " |
1401 | 0 | "the image file's data window."); |
1402 | | |
1403 | 0 | Int64 tileOffset = _data->tileOffsets (dx, dy, lx, ly); |
1404 | | |
1405 | 0 | if(tileOffset == 0) |
1406 | 0 | { |
1407 | 0 | THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " << |
1408 | 0 | lx << ", " << ly << ") is missing."); |
1409 | 0 | } |
1410 | | |
1411 | 0 | Lock lock(*_data->_streamData); |
1412 | | |
1413 | 0 | if (_data->_streamData->is->tellg() != tileOffset) |
1414 | 0 | _data->_streamData->is->seekg (tileOffset); |
1415 | | |
1416 | | |
1417 | | // |
1418 | | // Read the first few bytes of the tile (the header). |
1419 | | // Verify that the tile coordinates and the level number |
1420 | | // are correct. |
1421 | | // |
1422 | | |
1423 | 0 | int tileXCoord, tileYCoord, levelX, levelY; |
1424 | | |
1425 | 0 | if (isMultiPart(_data->version)) |
1426 | 0 | { |
1427 | 0 | int partNumber; |
1428 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, partNumber); |
1429 | 0 | if (partNumber != _data->partNumber) |
1430 | 0 | { |
1431 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber |
1432 | 0 | << ", should be " << _data->partNumber << "."); |
1433 | 0 | } |
1434 | 0 | } |
1435 | | |
1436 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, tileXCoord); |
1437 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, tileYCoord); |
1438 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, levelX); |
1439 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, levelY); |
1440 | | |
1441 | 0 | Int64 sampleCountTableSize; |
1442 | 0 | Int64 packedDataSize; |
1443 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, sampleCountTableSize); |
1444 | | |
1445 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, packedDataSize); |
1446 | | |
1447 | | |
1448 | | |
1449 | 0 | if (tileXCoord != dx) |
1450 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate."); |
1451 | | |
1452 | 0 | if (tileYCoord != dy) |
1453 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate."); |
1454 | | |
1455 | 0 | if (levelX != lx) |
1456 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate."); |
1457 | | |
1458 | 0 | if (levelY != ly) |
1459 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate."); |
1460 | | |
1461 | | |
1462 | | // total requirement for reading all the data |
1463 | | |
1464 | 0 | Int64 totalSizeRequired=40+sampleCountTableSize+packedDataSize; |
1465 | | |
1466 | 0 | bool big_enough = totalSizeRequired<=pixelDataSize; |
1467 | | |
1468 | 0 | pixelDataSize = totalSizeRequired; |
1469 | | |
1470 | | // was the block we were given big enough? |
1471 | 0 | if(!big_enough || pixelData==NULL) |
1472 | 0 | { |
1473 | | // special case: seek stream back to start if we are at the beginning (regular reading pixels assumes it doesn't need to seek |
1474 | | // in single part files) |
1475 | 0 | if(!isMultiPart(_data->version)) |
1476 | 0 | { |
1477 | 0 | _data->_streamData->is->seekg(_data->_streamData->currentPosition); |
1478 | 0 | } |
1479 | | // leave lock here - bail before reading more data |
1480 | 0 | return; |
1481 | 0 | } |
1482 | | |
1483 | | // copy the values we have read into the output block |
1484 | 0 | *(int *) (pixelData+0) = dx; |
1485 | 0 | *(int *) (pixelData+4) = dy; |
1486 | 0 | *(int *) (pixelData+8) = levelX; |
1487 | 0 | *(int *) (pixelData+12) = levelY; |
1488 | 0 | *(Int64 *) (pixelData+16) =sampleCountTableSize; |
1489 | 0 | *(Int64 *) (pixelData+24) = packedDataSize; |
1490 | | |
1491 | | // didn't read the unpackedsize - do that now |
1492 | 0 | Xdr::read<StreamIO> (*_data->_streamData->is, *(Int64 *) (pixelData+32)); |
1493 | | |
1494 | | // read the actual data |
1495 | 0 | _data->_streamData->is->read(pixelData+40, sampleCountTableSize+packedDataSize); |
1496 | | |
1497 | | |
1498 | 0 | if(!isMultiPart(_data->version)) |
1499 | 0 | { |
1500 | 0 | _data->_streamData->currentPosition+=sampleCountTableSize+packedDataSize+40; |
1501 | 0 | } |
1502 | | |
1503 | | // leave lock here |
1504 | | |
1505 | | |
1506 | 0 | } |
1507 | | |
1508 | | |
1509 | | unsigned int |
1510 | | DeepTiledInputFile::tileXSize () const |
1511 | 0 | { |
1512 | 0 | return _data->tileDesc.xSize; |
1513 | 0 | } |
1514 | | |
1515 | | |
1516 | | unsigned int |
1517 | | DeepTiledInputFile::tileYSize () const |
1518 | 0 | { |
1519 | 0 | return _data->tileDesc.ySize; |
1520 | 0 | } |
1521 | | |
1522 | | |
1523 | | LevelMode |
1524 | | DeepTiledInputFile::levelMode () const |
1525 | 0 | { |
1526 | 0 | return _data->tileDesc.mode; |
1527 | 0 | } |
1528 | | |
1529 | | |
1530 | | LevelRoundingMode |
1531 | | DeepTiledInputFile::levelRoundingMode () const |
1532 | 0 | { |
1533 | 0 | return _data->tileDesc.roundingMode; |
1534 | 0 | } |
1535 | | |
1536 | | |
1537 | | int |
1538 | | DeepTiledInputFile::numLevels () const |
1539 | 0 | { |
1540 | 0 | if (levelMode() == RIPMAP_LEVELS) |
1541 | 0 | THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image " |
1542 | 0 | "file \"" << fileName() << "\" " |
1543 | 0 | "(numLevels() is not defined for files " |
1544 | 0 | "with RIPMAP level mode)."); |
1545 | | |
1546 | 0 | return _data->numXLevels; |
1547 | 0 | } |
1548 | | |
1549 | | |
1550 | | int |
1551 | | DeepTiledInputFile::numXLevels () const |
1552 | 0 | { |
1553 | 0 | return _data->numXLevels; |
1554 | 0 | } |
1555 | | |
1556 | | |
1557 | | int |
1558 | | DeepTiledInputFile::numYLevels () const |
1559 | 0 | { |
1560 | 0 | return _data->numYLevels; |
1561 | 0 | } |
1562 | | |
1563 | | |
1564 | | bool |
1565 | | DeepTiledInputFile::isValidLevel (int lx, int ly) const |
1566 | 0 | { |
1567 | 0 | if (lx < 0 || ly < 0) |
1568 | 0 | return false; |
1569 | | |
1570 | 0 | if (levelMode() == MIPMAP_LEVELS && lx != ly) |
1571 | 0 | return false; |
1572 | | |
1573 | 0 | if (lx >= numXLevels() || ly >= numYLevels()) |
1574 | 0 | return false; |
1575 | | |
1576 | 0 | return true; |
1577 | 0 | } |
1578 | | |
1579 | | |
1580 | | int |
1581 | | DeepTiledInputFile::levelWidth (int lx) const |
1582 | 0 | { |
1583 | 0 | try |
1584 | 0 | { |
1585 | 0 | return levelSize (_data->minX, _data->maxX, lx, |
1586 | 0 | _data->tileDesc.roundingMode); |
1587 | 0 | } |
1588 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1589 | 0 | { |
1590 | 0 | REPLACE_EXC (e, "Error calling levelWidth() on image " |
1591 | 0 | "file \"" << fileName() << "\". " << e); |
1592 | 0 | throw; |
1593 | 0 | } |
1594 | 0 | } |
1595 | | |
1596 | | |
1597 | | int |
1598 | | DeepTiledInputFile::levelHeight (int ly) const |
1599 | 0 | { |
1600 | 0 | try |
1601 | 0 | { |
1602 | 0 | return levelSize (_data->minY, _data->maxY, ly, |
1603 | 0 | _data->tileDesc.roundingMode); |
1604 | 0 | } |
1605 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1606 | 0 | { |
1607 | 0 | REPLACE_EXC (e, "Error calling levelHeight() on image " |
1608 | 0 | "file \"" << fileName() << "\". " << e); |
1609 | 0 | throw; |
1610 | 0 | } |
1611 | 0 | } |
1612 | | |
1613 | | |
1614 | | int |
1615 | | DeepTiledInputFile::numXTiles (int lx) const |
1616 | 0 | { |
1617 | 0 | if (lx < 0 || lx >= _data->numXLevels) |
1618 | 0 | { |
1619 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image " |
1620 | 0 | "file \"" << _data->_streamData->is->fileName() << "\" " |
1621 | 0 | "(Argument is not in valid range)."); |
1622 | |
|
1623 | 0 | } |
1624 | | |
1625 | 0 | return _data->numXTiles[lx]; |
1626 | 0 | } |
1627 | | |
1628 | | |
1629 | | int |
1630 | | DeepTiledInputFile::numYTiles (int ly) const |
1631 | 0 | { |
1632 | 0 | if (ly < 0 || ly >= _data->numYLevels) |
1633 | 0 | { |
1634 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image " |
1635 | 0 | "file \"" << _data->_streamData->is->fileName() << "\" " |
1636 | 0 | "(Argument is not in valid range)."); |
1637 | 0 | } |
1638 | | |
1639 | 0 | return _data->numYTiles[ly]; |
1640 | 0 | } |
1641 | | |
1642 | | |
1643 | | Box2i |
1644 | | DeepTiledInputFile::dataWindowForLevel (int l) const |
1645 | 0 | { |
1646 | 0 | return dataWindowForLevel (l, l); |
1647 | 0 | } |
1648 | | |
1649 | | |
1650 | | Box2i |
1651 | | DeepTiledInputFile::dataWindowForLevel (int lx, int ly) const |
1652 | 0 | { |
1653 | 0 | try |
1654 | 0 | { |
1655 | 0 | return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel ( |
1656 | 0 | _data->tileDesc, |
1657 | 0 | _data->minX, _data->maxX, |
1658 | 0 | _data->minY, _data->maxY, |
1659 | 0 | lx, ly); |
1660 | 0 | } |
1661 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1662 | 0 | { |
1663 | 0 | REPLACE_EXC (e, "Error calling dataWindowForLevel() on image " |
1664 | 0 | "file \"" << fileName() << "\". " << e); |
1665 | 0 | throw; |
1666 | 0 | } |
1667 | 0 | } |
1668 | | |
1669 | | |
1670 | | Box2i |
1671 | | DeepTiledInputFile::dataWindowForTile (int dx, int dy, int l) const |
1672 | 0 | { |
1673 | 0 | return dataWindowForTile (dx, dy, l, l); |
1674 | 0 | } |
1675 | | |
1676 | | |
1677 | | Box2i |
1678 | | DeepTiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const |
1679 | 0 | { |
1680 | 0 | try |
1681 | 0 | { |
1682 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1683 | 0 | throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range."); |
1684 | | |
1685 | 0 | return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile ( |
1686 | 0 | _data->tileDesc, |
1687 | 0 | _data->minX, _data->maxX, |
1688 | 0 | _data->minY, _data->maxY, |
1689 | 0 | dx, dy, lx, ly); |
1690 | 0 | } |
1691 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1692 | 0 | { |
1693 | 0 | REPLACE_EXC (e, "Error calling dataWindowForTile() on image " |
1694 | 0 | "file \"" << fileName() << "\". " << e); |
1695 | 0 | throw; |
1696 | 0 | } |
1697 | 0 | } |
1698 | | |
1699 | | |
1700 | | bool |
1701 | | DeepTiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const |
1702 | 0 | { |
1703 | 0 | return ((lx < _data->numXLevels && lx >= 0) && |
1704 | 0 | (ly < _data->numYLevels && ly >= 0) && |
1705 | 0 | (dx < _data->numXTiles[lx] && dx >= 0) && |
1706 | 0 | (dy < _data->numYTiles[ly] && dy >= 0)); |
1707 | 0 | } |
1708 | | |
1709 | | |
1710 | | void |
1711 | | DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2, |
1712 | | int dy1, int dy2, |
1713 | | int lx, int ly) |
1714 | 0 | { |
1715 | 0 | Int64 savedFilePos = 0; |
1716 | |
|
1717 | 0 | try |
1718 | 0 | { |
1719 | 0 | Lock lock (*_data->_streamData); |
1720 | |
|
1721 | 0 | savedFilePos = _data->_streamData->is->tellg(); |
1722 | | |
1723 | | |
1724 | 0 | if (!isValidLevel (lx, ly)) |
1725 | 0 | { |
1726 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1727 | 0 | "Level coordinate " |
1728 | 0 | "(" << lx << ", " << ly << ") " |
1729 | 0 | "is invalid."); |
1730 | 0 | } |
1731 | | |
1732 | 0 | if (dx1 > dx2) |
1733 | 0 | std::swap (dx1, dx2); |
1734 | |
|
1735 | 0 | if (dy1 > dy2) |
1736 | 0 | std::swap (dy1, dy2); |
1737 | |
|
1738 | 0 | int dyStart = dy1; |
1739 | 0 | int dyStop = dy2 + 1; |
1740 | 0 | int dY = 1; |
1741 | |
|
1742 | 0 | if (_data->lineOrder == DECREASING_Y) |
1743 | 0 | { |
1744 | 0 | dyStart = dy2; |
1745 | 0 | dyStop = dy1 - 1; |
1746 | 0 | dY = -1; |
1747 | 0 | } |
1748 | | |
1749 | | // (TODO) Check if we have read the sample counts for those tiles, |
1750 | | // if we have, no need to read again. |
1751 | 0 | for (int dy = dyStart; dy != dyStop; dy += dY) |
1752 | 0 | { |
1753 | 0 | for (int dx = dx1; dx <= dx2; dx++) |
1754 | 0 | { |
1755 | | |
1756 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1757 | 0 | { |
1758 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1759 | 0 | "Tile (" << dx << ", " << dy << ", " << |
1760 | 0 | lx << "," << ly << ") is not a valid tile."); |
1761 | 0 | } |
1762 | | |
1763 | 0 | Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile ( |
1764 | 0 | _data->tileDesc, |
1765 | 0 | _data->minX, _data->maxX, |
1766 | 0 | _data->minY, _data->maxY, |
1767 | 0 | dx, dy, lx, ly); |
1768 | |
|
1769 | 0 | int xOffset = _data->sampleCountXTileCoords * tileRange.min.x; |
1770 | 0 | int yOffset = _data->sampleCountYTileCoords * tileRange.min.y; |
1771 | | |
1772 | | // |
1773 | | // Skip and check the tile coordinates. |
1774 | | // |
1775 | |
|
1776 | 0 | _data->_streamData->is->seekg(_data->tileOffsets(dx, dy, lx, ly)); |
1777 | |
|
1778 | 0 | if (isMultiPart(_data->version)) |
1779 | 0 | { |
1780 | 0 | int partNumber; |
1781 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, partNumber); |
1782 | |
|
1783 | 0 | if (partNumber != _data->partNumber) |
1784 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected part number."); |
1785 | 0 | } |
1786 | | |
1787 | 0 | int xInFile, yInFile, lxInFile, lyInFile; |
1788 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, xInFile); |
1789 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, yInFile); |
1790 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, lxInFile); |
1791 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, lyInFile); |
1792 | |
|
1793 | 0 | if (xInFile != dx) |
1794 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate."); |
1795 | | |
1796 | 0 | if (yInFile != dy) |
1797 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate."); |
1798 | | |
1799 | 0 | if (lxInFile != lx) |
1800 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate."); |
1801 | | |
1802 | 0 | if (lyInFile != ly) |
1803 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate."); |
1804 | | |
1805 | 0 | Int64 tableSize, dataSize, unpackedDataSize; |
1806 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, tableSize); |
1807 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, dataSize); |
1808 | 0 | Xdr::read <StreamIO> (*_data->_streamData->is, unpackedDataSize); |
1809 | | |
1810 | | |
1811 | 0 | if(tableSize>_data->maxSampleCountTableSize) |
1812 | 0 | { |
1813 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Bad sampleCountTableDataSize read from tile "<< dx << ',' << dy << ',' << lx << ',' << ly << ": expected " << _data->maxSampleCountTableSize << " or less, got "<< tableSize); |
1814 | 0 | } |
1815 | | |
1816 | | |
1817 | | // |
1818 | | // We make a check on the data size requirements here. |
1819 | | // Whilst we wish to store 64bit sizes on disk, not all the compressors |
1820 | | // have been made to work with such data sizes and are still limited to |
1821 | | // using signed 32 bit (int) for the data size. As such, this version |
1822 | | // insists that we validate that the data size does not exceed the data |
1823 | | // type max limit. |
1824 | | // @TODO refactor the compressor code to ensure full 64-bit support. |
1825 | | // |
1826 | | |
1827 | 0 | Int64 compressorMaxDataSize = Int64(std::numeric_limits<int>::max()); |
1828 | 0 | if (dataSize > compressorMaxDataSize || |
1829 | 0 | unpackedDataSize > compressorMaxDataSize || |
1830 | 0 | tableSize > compressorMaxDataSize) |
1831 | 0 | { |
1832 | 0 | THROW (IEX_NAMESPACE::ArgExc, "This version of the library does not" |
1833 | 0 | << "support the allocation of data with size > " |
1834 | 0 | << compressorMaxDataSize |
1835 | 0 | << " file table size :" << tableSize |
1836 | 0 | << " file unpacked size :" << unpackedDataSize |
1837 | 0 | << " file packed size :" << dataSize << ".\n"); |
1838 | 0 | } |
1839 | | |
1840 | | // |
1841 | | // Read and uncompress the pixel sample count table. |
1842 | | // |
1843 | | |
1844 | 0 | _data->_streamData->is->read(_data->sampleCountTableBuffer, tableSize); |
1845 | |
|
1846 | 0 | const char* readPtr; |
1847 | |
|
1848 | 0 | if (tableSize < _data->maxSampleCountTableSize) |
1849 | 0 | { |
1850 | 0 | if(!_data->sampleCountTableComp) |
1851 | 0 | { |
1852 | 0 | THROW(IEX_NAMESPACE::ArgExc,"Deep scanline data corrupt at tile " << dx << ',' << dy << ',' << lx << ',' << ly << " (sampleCountTableDataSize error)"); |
1853 | 0 | } |
1854 | 0 | _data->sampleCountTableComp->uncompress(_data->sampleCountTableBuffer, |
1855 | 0 | tableSize, |
1856 | 0 | tileRange.min.y, |
1857 | 0 | readPtr); |
1858 | 0 | } |
1859 | 0 | else |
1860 | 0 | readPtr = _data->sampleCountTableBuffer; |
1861 | | |
1862 | 0 | size_t cumulative_total_samples =0; |
1863 | 0 | int lastAccumulatedCount; |
1864 | 0 | for (int j = tileRange.min.y; j <= tileRange.max.y; j++) |
1865 | 0 | { |
1866 | 0 | lastAccumulatedCount = 0; |
1867 | 0 | for (int i = tileRange.min.x; i <= tileRange.max.x; i++) |
1868 | 0 | { |
1869 | 0 | int accumulatedCount; |
1870 | 0 | Xdr::read <CharPtrIO> (readPtr, accumulatedCount); |
1871 | | |
1872 | 0 | if (accumulatedCount < lastAccumulatedCount) |
1873 | 0 | { |
1874 | 0 | THROW(IEX_NAMESPACE::ArgExc,"Deep tile sampleCount data corrupt at tile " |
1875 | 0 | << dx << ',' << dy << ',' << lx << ',' << ly << " (negative sample count detected)"); |
1876 | 0 | } |
1877 | | |
1878 | 0 | int count = accumulatedCount - lastAccumulatedCount; |
1879 | 0 | lastAccumulatedCount = accumulatedCount; |
1880 | | |
1881 | 0 | _data->getSampleCount(i - xOffset, j - yOffset) =count; |
1882 | 0 | } |
1883 | 0 | cumulative_total_samples += lastAccumulatedCount; |
1884 | 0 | } |
1885 | | |
1886 | 0 | if(cumulative_total_samples * _data->combinedSampleSize > unpackedDataSize) |
1887 | 0 | { |
1888 | 0 | THROW(IEX_NAMESPACE::ArgExc,"Deep scanline sampleCount data corrupt at tile " |
1889 | 0 | << dx << ',' << dy << ',' << lx << ',' << ly |
1890 | 0 | << ": pixel data only contains " << unpackedDataSize |
1891 | 0 | << " bytes of data but table references at least " |
1892 | 0 | << cumulative_total_samples*_data->combinedSampleSize << " bytes of sample data" ); |
1893 | 0 | } |
1894 | | |
1895 | 0 | } |
1896 | 0 | } |
1897 | | |
1898 | 0 | _data->_streamData->is->seekg(savedFilePos); |
1899 | 0 | } |
1900 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1901 | 0 | { |
1902 | 0 | REPLACE_EXC (e, "Error reading sample count data from image " |
1903 | 0 | "file \"" << fileName() << "\". " << e); |
1904 | |
|
1905 | 0 | _data->_streamData->is->seekg(savedFilePos); |
1906 | |
|
1907 | 0 | throw; |
1908 | 0 | } |
1909 | 0 | } |
1910 | | |
1911 | | |
1912 | | void |
1913 | | DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int l) |
1914 | 0 | { |
1915 | 0 | readPixelSampleCount (dx, dy, l, l); |
1916 | 0 | } |
1917 | | |
1918 | | |
1919 | | void |
1920 | | DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int lx, int ly) |
1921 | 0 | { |
1922 | 0 | readPixelSampleCounts (dx, dx, dy, dy, lx, ly); |
1923 | 0 | } |
1924 | | |
1925 | | |
1926 | | void |
1927 | | DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2, |
1928 | | int dy1, int dy2, |
1929 | | int l) |
1930 | 0 | { |
1931 | 0 | readPixelSampleCounts (dx1, dx2, dy1, dy2, l, l); |
1932 | 0 | } |
1933 | | |
1934 | | |
1935 | | size_t |
1936 | | DeepTiledInputFile::totalTiles() const |
1937 | 0 | { |
1938 | | // |
1939 | | // Calculate the total number of tiles in the file |
1940 | | // |
1941 | | |
1942 | 0 | int numAllTiles = 0; |
1943 | | |
1944 | 0 | switch (levelMode ()) |
1945 | 0 | { |
1946 | 0 | case ONE_LEVEL: |
1947 | 0 | case MIPMAP_LEVELS: |
1948 | | |
1949 | 0 | for (int i_l = 0; i_l < numLevels (); ++i_l) |
1950 | 0 | numAllTiles += numXTiles (i_l) * numYTiles (i_l); |
1951 | | |
1952 | 0 | break; |
1953 | | |
1954 | 0 | case RIPMAP_LEVELS: |
1955 | | |
1956 | 0 | for (int i_ly = 0; i_ly < numYLevels (); ++i_ly) |
1957 | 0 | for (int i_lx = 0; i_lx < numXLevels (); ++i_lx) |
1958 | 0 | numAllTiles += numXTiles (i_lx) * numYTiles (i_ly); |
1959 | | |
1960 | 0 | break; |
1961 | | |
1962 | 0 | default: |
1963 | | |
1964 | 0 | throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format."); |
1965 | 0 | } |
1966 | 0 | return numAllTiles; |
1967 | 0 | } |
1968 | | |
1969 | | |
1970 | | |
1971 | | |
1972 | | void |
1973 | | DeepTiledInputFile::getTileOrder(int dx[],int dy[],int lx[],int ly[]) const |
1974 | 0 | { |
1975 | 0 | return _data->tileOffsets.getTileOrder(dx,dy,lx,ly); |
1976 | | |
1977 | 0 | } |
1978 | | |
1979 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |