/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfTiledInputFile.cpp
Line | Count | Source (jump to first uncovered line) |
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 TiledInputFile |
38 | | // |
39 | | //----------------------------------------------------------------------------- |
40 | | |
41 | | #include "ImfTiledInputFile.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 "ImfXdr.h" |
49 | | #include "ImfConvert.h" |
50 | | #include "ImfVersion.h" |
51 | | #include "ImfTileOffsets.h" |
52 | | #include "ImfThreading.h" |
53 | | #include "ImfPartType.h" |
54 | | #include "ImfMultiPartInputFile.h" |
55 | | #include "ImfInputStreamMutex.h" |
56 | | #include "IlmThreadPool.h" |
57 | | #include "IlmThreadSemaphore.h" |
58 | | #include "IlmThreadMutex.h" |
59 | | #include "ImathVec.h" |
60 | | #include "Iex.h" |
61 | | #include <string> |
62 | | #include <vector> |
63 | | #include <algorithm> |
64 | | #include <assert.h> |
65 | | #include "ImfInputPartData.h" |
66 | | #include "ImfNamespace.h" |
67 | | |
68 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
69 | | |
70 | | using IMATH_NAMESPACE::Box2i; |
71 | | using IMATH_NAMESPACE::V2i; |
72 | | using std::string; |
73 | | using std::vector; |
74 | | using std::min; |
75 | | using std::max; |
76 | | using ILMTHREAD_NAMESPACE::Mutex; |
77 | | using ILMTHREAD_NAMESPACE::Lock; |
78 | | using ILMTHREAD_NAMESPACE::Semaphore; |
79 | | using ILMTHREAD_NAMESPACE::Task; |
80 | | using ILMTHREAD_NAMESPACE::TaskGroup; |
81 | | using ILMTHREAD_NAMESPACE::ThreadPool; |
82 | | |
83 | | namespace { |
84 | | |
85 | | struct TInSliceInfo |
86 | | { |
87 | | PixelType typeInFrameBuffer; |
88 | | PixelType typeInFile; |
89 | | char * base; |
90 | | size_t xStride; |
91 | | size_t yStride; |
92 | | bool fill; |
93 | | bool skip; |
94 | | double fillValue; |
95 | | int xTileCoords; |
96 | | int yTileCoords; |
97 | | |
98 | | TInSliceInfo (PixelType typeInFrameBuffer = HALF, |
99 | | PixelType typeInFile = HALF, |
100 | | char *base = 0, |
101 | | size_t xStride = 0, |
102 | | size_t yStride = 0, |
103 | | bool fill = false, |
104 | | bool skip = false, |
105 | | double fillValue = 0.0, |
106 | | int xTileCoords = 0, |
107 | | int yTileCoords = 0); |
108 | | }; |
109 | | |
110 | | |
111 | | TInSliceInfo::TInSliceInfo (PixelType tifb, |
112 | | PixelType tifl, |
113 | | char *b, |
114 | | size_t xs, size_t ys, |
115 | | bool f, bool s, |
116 | | double fv, |
117 | | int xtc, |
118 | | int ytc) |
119 | | : |
120 | | typeInFrameBuffer (tifb), |
121 | | typeInFile (tifl), |
122 | | base (b), |
123 | | xStride (xs), |
124 | | yStride (ys), |
125 | | fill (f), |
126 | | skip (s), |
127 | | fillValue (fv), |
128 | | xTileCoords (xtc), |
129 | | yTileCoords (ytc) |
130 | 0 | { |
131 | | // empty |
132 | 0 | } |
133 | | |
134 | | |
135 | | struct TileBuffer |
136 | | { |
137 | | const char * uncompressedData; |
138 | | char * buffer; |
139 | | int dataSize; |
140 | | Compressor * compressor; |
141 | | Compressor::Format format; |
142 | | int dx; |
143 | | int dy; |
144 | | int lx; |
145 | | int ly; |
146 | | bool hasException; |
147 | | string exception; |
148 | | |
149 | | TileBuffer (Compressor * const comp); |
150 | | ~TileBuffer (); |
151 | | |
152 | 0 | inline void wait () {_sem.wait();} |
153 | 0 | inline void post () {_sem.post();} |
154 | | |
155 | | protected: |
156 | | |
157 | | Semaphore _sem; |
158 | | }; |
159 | | |
160 | | |
161 | | TileBuffer::TileBuffer (Compressor *comp): |
162 | | uncompressedData (0), |
163 | | buffer (0), |
164 | | dataSize (0), |
165 | | compressor (comp), |
166 | | format (defaultFormat (compressor)), |
167 | | dx (-1), |
168 | | dy (-1), |
169 | | lx (-1), |
170 | | ly (-1), |
171 | | hasException (false), |
172 | | exception (), |
173 | | _sem (1) |
174 | 0 | { |
175 | | // empty |
176 | 0 | } |
177 | | |
178 | | |
179 | | TileBuffer::~TileBuffer () |
180 | 0 | { |
181 | 0 | delete compressor; |
182 | 0 | } |
183 | | |
184 | | } // namespace |
185 | | |
186 | | |
187 | | class MultiPartInputFile; |
188 | | |
189 | | |
190 | | // |
191 | | // struct TiledInputFile::Data stores things that will be |
192 | | // needed between calls to readTile() |
193 | | // |
194 | | |
195 | | struct TiledInputFile::Data: public Mutex |
196 | | { |
197 | | Header header; // the image header |
198 | | TileDescription tileDesc; // describes the tile layout |
199 | | int version; // file's version |
200 | | FrameBuffer frameBuffer; // framebuffer to write into |
201 | | LineOrder lineOrder; // the file's lineorder |
202 | | int minX; // data window's min x coord |
203 | | int maxX; // data window's max x coord |
204 | | int minY; // data window's min y coord |
205 | | int maxY; // data window's max x coord |
206 | | |
207 | | int numXLevels; // number of x levels |
208 | | int numYLevels; // number of y levels |
209 | | int * numXTiles; // number of x tiles at a level |
210 | | int * numYTiles; // number of y tiles at a level |
211 | | |
212 | | TileOffsets tileOffsets; // stores offsets in file for |
213 | | // each tile |
214 | | |
215 | | bool fileIsComplete; // True if no tiles are missing |
216 | | // in the file |
217 | | |
218 | | vector<TInSliceInfo> slices; // info about channels in file |
219 | | |
220 | | size_t bytesPerPixel; // size of an uncompressed pixel |
221 | | |
222 | | size_t maxBytesPerTileLine; // combined size of a line |
223 | | // over all channels |
224 | | |
225 | | int partNumber; // part number |
226 | | |
227 | | bool multiPartBackwardSupport; // if we are reading a multipart file |
228 | | // using OpenEXR 1.7 API |
229 | | |
230 | | int numThreads; // number of threads |
231 | | |
232 | | MultiPartInputFile* multiPartFile; // the MultiPartInputFile used to |
233 | | // support backward compatibility |
234 | | |
235 | | vector<TileBuffer*> tileBuffers; // each holds a single tile |
236 | | size_t tileBufferSize; // size of the tile buffers |
237 | | |
238 | | bool memoryMapped; // if the stream is memory mapped |
239 | | |
240 | | InputStreamMutex * _streamData; |
241 | | bool _deleteStream; |
242 | | |
243 | | Data (int numThreads); |
244 | | ~Data (); |
245 | | |
246 | | inline TileBuffer * getTileBuffer (int number); |
247 | | // hash function from tile indices |
248 | | // into our vector of tile buffers |
249 | | }; |
250 | | |
251 | | |
252 | | TiledInputFile::Data::Data (int numThreads): |
253 | | numXTiles (0), |
254 | | numYTiles (0), |
255 | | partNumber (-1), |
256 | | multiPartBackwardSupport(false), |
257 | | numThreads(numThreads), |
258 | | memoryMapped(false), |
259 | | _streamData(NULL), |
260 | | _deleteStream(false) |
261 | 0 | { |
262 | | // |
263 | | // We need at least one tileBuffer, but if threading is used, |
264 | | // to keep n threads busy we need 2*n tileBuffers |
265 | | // |
266 | |
|
267 | 0 | tileBuffers.resize (max (1, 2 * numThreads)); |
268 | 0 | } |
269 | | |
270 | | |
271 | | TiledInputFile::Data::~Data () |
272 | 0 | { |
273 | 0 | delete [] numXTiles; |
274 | 0 | delete [] numYTiles; |
275 | |
|
276 | 0 | for (size_t i = 0; i < tileBuffers.size(); i++) |
277 | 0 | delete tileBuffers[i]; |
278 | |
|
279 | 0 | if (multiPartBackwardSupport) |
280 | 0 | delete multiPartFile; |
281 | 0 | } |
282 | | |
283 | | |
284 | | TileBuffer* |
285 | | TiledInputFile::Data::getTileBuffer (int number) |
286 | 0 | { |
287 | 0 | return tileBuffers[number % tileBuffers.size()]; |
288 | 0 | } |
289 | | |
290 | | |
291 | | namespace { |
292 | | |
293 | | void |
294 | | readTileData (InputStreamMutex *streamData, |
295 | | TiledInputFile::Data *ifd, |
296 | | int dx, int dy, |
297 | | int lx, int ly, |
298 | | char *&buffer, |
299 | | int &dataSize) |
300 | 0 | { |
301 | | // |
302 | | // Read a single tile block from the file and into the array pointed |
303 | | // to by buffer. If the file is memory-mapped, then we change where |
304 | | // buffer points instead of writing into the array (hence buffer needs |
305 | | // to be a reference to a char *). |
306 | | // |
307 | | |
308 | | // |
309 | | // Look up the location for this tile in the Index and |
310 | | // seek to that position if necessary |
311 | | // |
312 | | |
313 | 0 | Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly); |
314 | |
|
315 | 0 | if (tileOffset == 0) |
316 | 0 | { |
317 | 0 | THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " << |
318 | 0 | lx << ", " << ly << ") is missing."); |
319 | 0 | } |
320 | | |
321 | | |
322 | | // |
323 | | // In a multi-part file, the next chunk does not need to |
324 | | // belong to the same part, so we have to compare the |
325 | | // offset here. |
326 | | // |
327 | | |
328 | 0 | if (!isMultiPart(ifd->version)) |
329 | 0 | { |
330 | 0 | if (streamData->currentPosition != tileOffset) |
331 | 0 | streamData->is->seekg (tileOffset); |
332 | 0 | } |
333 | 0 | else |
334 | 0 | { |
335 | | // |
336 | | // In a multi-part file, the file pointer may be moved by other |
337 | | // parts, so we have to ask tellg() where we are. |
338 | | // |
339 | 0 | if (streamData->is->tellg() != tileOffset) |
340 | 0 | streamData->is->seekg (tileOffset); |
341 | 0 | } |
342 | | |
343 | | // |
344 | | // Read the first few bytes of the tile (the header). |
345 | | // Verify that the tile coordinates and the level number |
346 | | // are correct. |
347 | | // |
348 | | |
349 | 0 | int tileXCoord, tileYCoord, levelX, levelY; |
350 | |
|
351 | 0 | if (isMultiPart(ifd->version)) |
352 | 0 | { |
353 | 0 | int partNumber; |
354 | 0 | Xdr::read <StreamIO> (*streamData->is, partNumber); |
355 | 0 | if (partNumber != ifd->partNumber) |
356 | 0 | { |
357 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber |
358 | 0 | << ", should be " << ifd->partNumber << "."); |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileXCoord); |
363 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileYCoord); |
364 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelX); |
365 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelY); |
366 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize); |
367 | |
|
368 | 0 | if (tileXCoord != dx) |
369 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate."); |
370 | | |
371 | 0 | if (tileYCoord != dy) |
372 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate."); |
373 | | |
374 | 0 | if (levelX != lx) |
375 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate."); |
376 | | |
377 | 0 | if (levelY != ly) |
378 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate."); |
379 | | |
380 | 0 | if (dataSize > (int) ifd->tileBufferSize) |
381 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile block length."); |
382 | | |
383 | | // |
384 | | // Read the pixel data. |
385 | | // |
386 | | |
387 | 0 | if (streamData->is->isMemoryMapped ()) |
388 | 0 | buffer = streamData->is->readMemoryMapped (dataSize); |
389 | 0 | else |
390 | 0 | streamData->is->read (buffer, dataSize); |
391 | | |
392 | | // |
393 | | // Keep track of which tile is the next one in |
394 | | // the file, so that we can avoid redundant seekg() |
395 | | // operations (seekg() can be fairly expensive). |
396 | | // |
397 | | |
398 | 0 | streamData->currentPosition = tileOffset + 5 * Xdr::size<int>() + dataSize; |
399 | 0 | } |
400 | | |
401 | | |
402 | | void |
403 | | readNextTileData (InputStreamMutex *streamData, |
404 | | TiledInputFile::Data *ifd, |
405 | | int &dx, int &dy, |
406 | | int &lx, int &ly, |
407 | | char * & buffer, |
408 | | int &dataSize) |
409 | 0 | { |
410 | | // |
411 | | // Read the next tile block from the file |
412 | | // |
413 | |
|
414 | 0 | if(isMultiPart(ifd->version)) |
415 | 0 | { |
416 | 0 | int part; |
417 | 0 | Xdr::read <StreamIO> (*streamData->is, part); |
418 | 0 | if(part!=ifd->partNumber) |
419 | 0 | { |
420 | 0 | throw IEX_NAMESPACE::InputExc("Unexpected part number in readNextTileData"); |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | // |
425 | | // Read the first few bytes of the tile (the header). |
426 | | // |
427 | | |
428 | 0 | Xdr::read <StreamIO> (*streamData->is, dx); |
429 | 0 | Xdr::read <StreamIO> (*streamData->is, dy); |
430 | 0 | Xdr::read <StreamIO> (*streamData->is, lx); |
431 | 0 | Xdr::read <StreamIO> (*streamData->is, ly); |
432 | 0 | Xdr::read <StreamIO> (*streamData->is, dataSize); |
433 | |
|
434 | 0 | if (dataSize > (int) ifd->tileBufferSize) |
435 | 0 | throw IEX_NAMESPACE::InputExc ("Unexpected tile block length."); |
436 | | |
437 | | // |
438 | | // Read the pixel data. |
439 | | // |
440 | | |
441 | 0 | streamData->is->read (buffer, dataSize); |
442 | | |
443 | | // |
444 | | // Keep track of which tile is the next one in |
445 | | // the file, so that we can avoid redundant seekg() |
446 | | // operations (seekg() can be fairly expensive). |
447 | | // |
448 | |
|
449 | 0 | streamData->currentPosition += 5 * Xdr::size<int>() + dataSize; |
450 | 0 | } |
451 | | |
452 | | |
453 | | // |
454 | | // A TileBufferTask encapsulates the task of uncompressing |
455 | | // a single tile and copying it into the frame buffer. |
456 | | // |
457 | | |
458 | | class TileBufferTask : public Task |
459 | | { |
460 | | public: |
461 | | |
462 | | TileBufferTask (TaskGroup *group, |
463 | | TiledInputFile::Data *ifd, |
464 | | TileBuffer *tileBuffer); |
465 | | |
466 | | virtual ~TileBufferTask (); |
467 | | |
468 | | virtual void execute (); |
469 | | |
470 | | private: |
471 | | |
472 | | TiledInputFile::Data * _ifd; |
473 | | TileBuffer * _tileBuffer; |
474 | | }; |
475 | | |
476 | | |
477 | | TileBufferTask::TileBufferTask |
478 | | (TaskGroup *group, |
479 | | TiledInputFile::Data *ifd, |
480 | | TileBuffer *tileBuffer) |
481 | | : |
482 | | Task (group), |
483 | | _ifd (ifd), |
484 | | _tileBuffer (tileBuffer) |
485 | 0 | { |
486 | | // empty |
487 | 0 | } |
488 | | |
489 | | |
490 | | TileBufferTask::~TileBufferTask () |
491 | 0 | { |
492 | | // |
493 | | // Signal that the tile buffer is now free |
494 | | // |
495 | |
|
496 | 0 | _tileBuffer->post (); |
497 | 0 | } |
498 | | |
499 | | |
500 | | void |
501 | | TileBufferTask::execute () |
502 | 0 | { |
503 | 0 | try |
504 | 0 | { |
505 | | // |
506 | | // Calculate information about the tile |
507 | | // |
508 | | |
509 | 0 | Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile ( |
510 | 0 | _ifd->tileDesc, |
511 | 0 | _ifd->minX, _ifd->maxX, |
512 | 0 | _ifd->minY, _ifd->maxY, |
513 | 0 | _tileBuffer->dx, |
514 | 0 | _tileBuffer->dy, |
515 | 0 | _tileBuffer->lx, |
516 | 0 | _tileBuffer->ly); |
517 | |
|
518 | 0 | int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1; |
519 | | |
520 | 0 | int numPixelsInTile = numPixelsPerScanLine * |
521 | 0 | (tileRange.max.y - tileRange.min.y + 1); |
522 | | |
523 | 0 | int sizeOfTile = _ifd->bytesPerPixel * numPixelsInTile; |
524 | | |
525 | | |
526 | | // |
527 | | // Uncompress the data, if necessary |
528 | | // |
529 | | |
530 | 0 | if (_tileBuffer->compressor && _tileBuffer->dataSize < sizeOfTile) |
531 | 0 | { |
532 | 0 | _tileBuffer->format = _tileBuffer->compressor->format(); |
533 | |
|
534 | 0 | _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile |
535 | 0 | (_tileBuffer->buffer, _tileBuffer->dataSize, |
536 | 0 | tileRange, _tileBuffer->uncompressedData); |
537 | 0 | } |
538 | 0 | else |
539 | 0 | { |
540 | | // |
541 | | // If the line is uncompressed, it's in XDR format, |
542 | | // regardless of the compressor's output format. |
543 | | // |
544 | | |
545 | 0 | _tileBuffer->format = Compressor::XDR; |
546 | 0 | _tileBuffer->uncompressedData = _tileBuffer->buffer; |
547 | 0 | } |
548 | | |
549 | | // |
550 | | // Convert the tile of pixel data back from the machine-independent |
551 | | // representation, and store the result in the frame buffer. |
552 | | // |
553 | | |
554 | 0 | const char *readPtr = _tileBuffer->uncompressedData; |
555 | | // points to where we |
556 | | // read from in the |
557 | | // tile block |
558 | | |
559 | | // |
560 | | // Iterate over the scan lines in the tile. |
561 | | // |
562 | | |
563 | 0 | for (int y = tileRange.min.y; y <= tileRange.max.y; ++y) |
564 | 0 | { |
565 | | // |
566 | | // Iterate over all image channels. |
567 | | // |
568 | | |
569 | 0 | for (unsigned int i = 0; i < _ifd->slices.size(); ++i) |
570 | 0 | { |
571 | 0 | const TInSliceInfo &slice = _ifd->slices[i]; |
572 | | |
573 | | // |
574 | | // These offsets are used to facilitate both |
575 | | // absolute and tile-relative pixel coordinates. |
576 | | // |
577 | | |
578 | 0 | int xOffset = slice.xTileCoords * tileRange.min.x; |
579 | 0 | int yOffset = slice.yTileCoords * tileRange.min.y; |
580 | | |
581 | | // |
582 | | // Fill the frame buffer with pixel data. |
583 | | // |
584 | | |
585 | 0 | if (slice.skip) |
586 | 0 | { |
587 | | // |
588 | | // The file contains data for this channel, but |
589 | | // the frame buffer contains no slice for this channel. |
590 | | // |
591 | | |
592 | 0 | skipChannel (readPtr, slice.typeInFile, |
593 | 0 | numPixelsPerScanLine); |
594 | 0 | } |
595 | 0 | else |
596 | 0 | { |
597 | | // |
598 | | // The frame buffer contains a slice for this channel. |
599 | | // |
600 | | |
601 | 0 | char *writePtr = slice.base + |
602 | 0 | (y - yOffset) * slice.yStride + |
603 | 0 | (tileRange.min.x - xOffset) * |
604 | 0 | slice.xStride; |
605 | |
|
606 | 0 | char *endPtr = writePtr + |
607 | 0 | (numPixelsPerScanLine - 1) * slice.xStride; |
608 | | |
609 | 0 | copyIntoFrameBuffer (readPtr, writePtr, endPtr, |
610 | 0 | slice.xStride, |
611 | 0 | slice.fill, slice.fillValue, |
612 | 0 | _tileBuffer->format, |
613 | 0 | slice.typeInFrameBuffer, |
614 | 0 | slice.typeInFile); |
615 | 0 | } |
616 | 0 | } |
617 | 0 | } |
618 | 0 | } |
619 | 0 | catch (std::exception &e) |
620 | 0 | { |
621 | 0 | if (!_tileBuffer->hasException) |
622 | 0 | { |
623 | 0 | _tileBuffer->exception = e.what (); |
624 | 0 | _tileBuffer->hasException = true; |
625 | 0 | } |
626 | 0 | } |
627 | 0 | catch (...) |
628 | 0 | { |
629 | 0 | if (!_tileBuffer->hasException) |
630 | 0 | { |
631 | 0 | _tileBuffer->exception = "unrecognized exception"; |
632 | 0 | _tileBuffer->hasException = true; |
633 | 0 | } |
634 | 0 | } |
635 | 0 | } |
636 | | |
637 | | |
638 | | TileBufferTask * |
639 | | newTileBufferTask |
640 | | (TaskGroup *group, |
641 | | InputStreamMutex *streamData, |
642 | | TiledInputFile::Data *ifd, |
643 | | int number, |
644 | | int dx, int dy, |
645 | | int lx, int ly) |
646 | 0 | { |
647 | | // |
648 | | // Wait for a tile buffer to become available, |
649 | | // fill the buffer with raw data from the file, |
650 | | // and create a new TileBufferTask whose execute() |
651 | | // method will uncompress the tile and copy the |
652 | | // tile's pixels into the frame buffer. |
653 | | // |
654 | |
|
655 | 0 | TileBuffer *tileBuffer = ifd->getTileBuffer (number); |
656 | |
|
657 | 0 | try |
658 | 0 | { |
659 | 0 | tileBuffer->wait(); |
660 | | |
661 | 0 | tileBuffer->dx = dx; |
662 | 0 | tileBuffer->dy = dy; |
663 | 0 | tileBuffer->lx = lx; |
664 | 0 | tileBuffer->ly = ly; |
665 | |
|
666 | 0 | tileBuffer->uncompressedData = 0; |
667 | |
|
668 | 0 | readTileData (streamData, ifd, dx, dy, lx, ly, |
669 | 0 | tileBuffer->buffer, |
670 | 0 | tileBuffer->dataSize); |
671 | 0 | } |
672 | 0 | catch (...) |
673 | 0 | { |
674 | | // |
675 | | // Reading from the file caused an exception. |
676 | | // Signal that the tile buffer is free, and |
677 | | // re-throw the exception. |
678 | | // |
679 | |
|
680 | 0 | tileBuffer->post(); |
681 | 0 | throw; |
682 | 0 | } |
683 | | |
684 | 0 | return new TileBufferTask (group, ifd, tileBuffer); |
685 | 0 | } |
686 | | |
687 | | |
688 | | } // namespace |
689 | | |
690 | | |
691 | | TiledInputFile::TiledInputFile (const char fileName[], int numThreads): |
692 | | _data (new Data (numThreads)) |
693 | 0 | { |
694 | 0 | _data->_streamData=NULL; |
695 | 0 | _data->_deleteStream=true; |
696 | | |
697 | | // |
698 | | // This constructor is called when a user |
699 | | // explicitly wants to read a tiled file. |
700 | | // |
701 | | |
702 | |
|
703 | 0 | IStream* is = 0; |
704 | 0 | try |
705 | 0 | { |
706 | 0 | is = new StdIFStream (fileName); |
707 | 0 | readMagicNumberAndVersionField(*is, _data->version); |
708 | | |
709 | | // |
710 | | // Backward compatibility to read multpart file. |
711 | | // |
712 | 0 | if (isMultiPart(_data->version)) |
713 | 0 | { |
714 | 0 | compatibilityInitialize(*is); |
715 | 0 | return; |
716 | 0 | } |
717 | | |
718 | 0 | _data->_streamData = new InputStreamMutex(); |
719 | 0 | _data->_streamData->is = is; |
720 | 0 | _data->header.readFrom (*_data->_streamData->is, _data->version); |
721 | 0 | initialize(); |
722 | | //read tile offsets - we are not multipart or deep |
723 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false); |
724 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
725 | 0 | } |
726 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
727 | 0 | { |
728 | 0 | if (_data->_streamData != 0) |
729 | 0 | { |
730 | 0 | if (_data->_streamData->is != 0) |
731 | 0 | { |
732 | 0 | delete _data->_streamData->is; |
733 | 0 | _data->_streamData->is = is = 0; |
734 | 0 | } |
735 | |
|
736 | 0 | delete _data->_streamData; |
737 | 0 | } |
738 | |
|
739 | 0 | if (is != 0) |
740 | 0 | delete is; |
741 | |
|
742 | 0 | REPLACE_EXC (e, "Cannot open image file " |
743 | 0 | "\"" << fileName << "\". " << e); |
744 | 0 | throw; |
745 | 0 | } |
746 | 0 | catch (...) |
747 | 0 | { |
748 | 0 | if ( _data->_streamData != 0) |
749 | 0 | { |
750 | 0 | if ( _data->_streamData->is != 0) |
751 | 0 | { |
752 | 0 | delete _data->_streamData->is; |
753 | 0 | _data->_streamData->is = is = 0; |
754 | 0 | } |
755 | |
|
756 | 0 | delete _data->_streamData; |
757 | 0 | } |
758 | |
|
759 | 0 | if (is != 0) |
760 | 0 | delete is; |
761 | 0 | throw; |
762 | 0 | } |
763 | 0 | } |
764 | | |
765 | | |
766 | | TiledInputFile::TiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads): |
767 | | _data (new Data (numThreads)) |
768 | 0 | { |
769 | 0 | _data->_deleteStream=false; |
770 | | // |
771 | | // This constructor is called when a user |
772 | | // explicitly wants to read a tiled file. |
773 | | // |
774 | |
|
775 | 0 | bool streamDataCreated = false; |
776 | |
|
777 | 0 | try |
778 | 0 | { |
779 | 0 | readMagicNumberAndVersionField(is, _data->version); |
780 | | |
781 | | // |
782 | | // Backward compatibility to read multpart file. |
783 | | // |
784 | 0 | if (isMultiPart(_data->version)) |
785 | 0 | { |
786 | 0 | compatibilityInitialize(is); |
787 | 0 | return; |
788 | 0 | } |
789 | | |
790 | 0 | streamDataCreated = true; |
791 | 0 | _data->_streamData = new InputStreamMutex(); |
792 | 0 | _data->_streamData->is = &is; |
793 | 0 | _data->header.readFrom (*_data->_streamData->is, _data->version); |
794 | 0 | initialize(); |
795 | | // file is guaranteed to be single part, regular image |
796 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false); |
797 | 0 | _data->memoryMapped = _data->_streamData->is->isMemoryMapped(); |
798 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
799 | 0 | } |
800 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
801 | 0 | { |
802 | 0 | if (streamDataCreated) delete _data->_streamData; |
803 | 0 | delete _data; |
804 | |
|
805 | 0 | REPLACE_EXC (e, "Cannot open image file " |
806 | 0 | "\"" << is.fileName() << "\". " << e); |
807 | 0 | throw; |
808 | 0 | } |
809 | 0 | catch (...) |
810 | 0 | { |
811 | 0 | if (streamDataCreated) delete _data->_streamData; |
812 | 0 | delete _data; |
813 | 0 | throw; |
814 | 0 | } |
815 | 0 | } |
816 | | |
817 | | |
818 | | TiledInputFile::TiledInputFile (const Header &header, |
819 | | OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is, |
820 | | int version, |
821 | | int numThreads) : |
822 | | _data (new Data (numThreads)) |
823 | 0 | { |
824 | 0 | _data->_deleteStream=false; |
825 | 0 | _data->_streamData = new InputStreamMutex(); |
826 | | // |
827 | | // This constructor called by class Imf::InputFile |
828 | | // when a user wants to just read an image file, and |
829 | | // doesn't care or know if the file is tiled. |
830 | | // No need to have backward compatibility here, because |
831 | | // we have somehow got the header. |
832 | | // |
833 | |
|
834 | 0 | _data->_streamData->is = is; |
835 | 0 | _data->header = header; |
836 | 0 | _data->version = version; |
837 | 0 | initialize(); |
838 | 0 | _data->tileOffsets.readFrom (*(_data->_streamData->is),_data->fileIsComplete,false,false); |
839 | 0 | _data->memoryMapped = is->isMemoryMapped(); |
840 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
841 | 0 | } |
842 | | |
843 | | |
844 | | TiledInputFile::TiledInputFile (InputPartData* part) |
845 | 0 | { |
846 | 0 | _data = new Data (part->numThreads); |
847 | 0 | _data->_deleteStream=false; |
848 | 0 | multiPartInitialize(part); |
849 | 0 | } |
850 | | |
851 | | |
852 | | void |
853 | | TiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is) |
854 | 0 | { |
855 | 0 | is.seekg(0); |
856 | | // |
857 | | // Construct a MultiPartInputFile, initialize TiledInputFile |
858 | | // with the part 0 data. |
859 | | // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later. |
860 | | // |
861 | 0 | _data->multiPartBackwardSupport = true; |
862 | 0 | _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads); |
863 | 0 | InputPartData* part = _data->multiPartFile->getPart(0); |
864 | |
|
865 | 0 | multiPartInitialize(part); |
866 | 0 | } |
867 | | |
868 | | |
869 | | void |
870 | | TiledInputFile::multiPartInitialize(InputPartData* part) |
871 | 0 | { |
872 | 0 | if (part->header.type() != TILEDIMAGE) |
873 | 0 | throw IEX_NAMESPACE::ArgExc("Can't build a TiledInputFile from a type-mismatched part."); |
874 | | |
875 | 0 | _data->_streamData = part->mutex; |
876 | 0 | _data->header = part->header; |
877 | 0 | _data->version = part->version; |
878 | 0 | _data->partNumber = part->partNumber; |
879 | 0 | _data->memoryMapped = _data->_streamData->is->isMemoryMapped(); |
880 | 0 | initialize(); |
881 | 0 | _data->tileOffsets.readFrom(part->chunkOffsets,_data->fileIsComplete); |
882 | 0 | _data->_streamData->currentPosition = _data->_streamData->is->tellg(); |
883 | 0 | } |
884 | | |
885 | | |
886 | | void |
887 | | TiledInputFile::initialize () |
888 | 0 | { |
889 | | // fix bad types in header (arises when a tool built against an older version of |
890 | | // OpenEXR converts a scanline image to tiled) |
891 | | // only applies when file is a single part, regular image, tiled file |
892 | | // |
893 | 0 | if(!isMultiPart(_data->version) && |
894 | 0 | !isNonImage(_data->version) && |
895 | 0 | isTiled(_data->version) && |
896 | 0 | _data->header.hasType() ) |
897 | 0 | { |
898 | 0 | _data->header.setType(TILEDIMAGE); |
899 | 0 | } |
900 | | |
901 | 0 | if (_data->partNumber == -1) |
902 | 0 | { |
903 | 0 | if (!isTiled (_data->version)) |
904 | 0 | throw IEX_NAMESPACE::ArgExc ("Expected a tiled file but the file is not tiled."); |
905 | | |
906 | 0 | } |
907 | 0 | else |
908 | 0 | { |
909 | 0 | if(_data->header.hasType() && _data->header.type()!=TILEDIMAGE) |
910 | 0 | { |
911 | 0 | throw IEX_NAMESPACE::ArgExc ("TiledInputFile used for non-tiledimage part."); |
912 | 0 | } |
913 | 0 | } |
914 | | |
915 | 0 | _data->header.sanityCheck (true); |
916 | |
|
917 | 0 | _data->tileDesc = _data->header.tileDescription(); |
918 | 0 | _data->lineOrder = _data->header.lineOrder(); |
919 | | |
920 | | // |
921 | | // Save the dataWindow information |
922 | | // |
923 | | |
924 | 0 | const Box2i &dataWindow = _data->header.dataWindow(); |
925 | 0 | _data->minX = dataWindow.min.x; |
926 | 0 | _data->maxX = dataWindow.max.x; |
927 | 0 | _data->minY = dataWindow.min.y; |
928 | 0 | _data->maxY = dataWindow.max.y; |
929 | | |
930 | | // |
931 | | // Precompute level and tile information to speed up utility functions |
932 | | // |
933 | |
|
934 | 0 | precalculateTileInfo (_data->tileDesc, |
935 | 0 | _data->minX, _data->maxX, |
936 | 0 | _data->minY, _data->maxY, |
937 | 0 | _data->numXTiles, _data->numYTiles, |
938 | 0 | _data->numXLevels, _data->numYLevels); |
939 | |
|
940 | 0 | _data->bytesPerPixel = calculateBytesPerPixel (_data->header); |
941 | |
|
942 | 0 | _data->maxBytesPerTileLine = _data->bytesPerPixel * _data->tileDesc.xSize; |
943 | |
|
944 | 0 | _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize; |
945 | | |
946 | | // |
947 | | // Create all the TileBuffers and allocate their internal buffers |
948 | | // |
949 | |
|
950 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); i++) |
951 | 0 | { |
952 | 0 | _data->tileBuffers[i] = new TileBuffer (newTileCompressor |
953 | 0 | (_data->header.compression(), |
954 | 0 | _data->maxBytesPerTileLine, |
955 | 0 | _data->tileDesc.ySize, |
956 | 0 | _data->header)); |
957 | |
|
958 | 0 | if (!_data->_streamData->is->isMemoryMapped ()) |
959 | 0 | _data->tileBuffers[i]->buffer = new char [_data->tileBufferSize]; |
960 | 0 | } |
961 | |
|
962 | 0 | _data->tileOffsets = TileOffsets (_data->tileDesc.mode, |
963 | 0 | _data->numXLevels, |
964 | 0 | _data->numYLevels, |
965 | 0 | _data->numXTiles, |
966 | 0 | _data->numYTiles); |
967 | 0 | } |
968 | | |
969 | | |
970 | | TiledInputFile::~TiledInputFile () |
971 | 0 | { |
972 | 0 | if (!_data->memoryMapped) |
973 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); i++) |
974 | 0 | delete [] _data->tileBuffers[i]->buffer; |
975 | |
|
976 | 0 | if (_data->_deleteStream) |
977 | 0 | delete _data->_streamData->is; |
978 | |
|
979 | 0 | if (_data->partNumber == -1) |
980 | 0 | delete _data->_streamData; |
981 | |
|
982 | 0 | delete _data; |
983 | 0 | } |
984 | | |
985 | | |
986 | | const char * |
987 | | TiledInputFile::fileName () const |
988 | 0 | { |
989 | 0 | return _data->_streamData->is->fileName(); |
990 | 0 | } |
991 | | |
992 | | |
993 | | const Header & |
994 | | TiledInputFile::header () const |
995 | 0 | { |
996 | 0 | return _data->header; |
997 | 0 | } |
998 | | |
999 | | |
1000 | | int |
1001 | | TiledInputFile::version () const |
1002 | 0 | { |
1003 | 0 | return _data->version; |
1004 | 0 | } |
1005 | | |
1006 | | |
1007 | | void |
1008 | | TiledInputFile::setFrameBuffer (const FrameBuffer &frameBuffer) |
1009 | 0 | { |
1010 | 0 | Lock lock (*_data->_streamData); |
1011 | | |
1012 | | // |
1013 | | // Set the frame buffer |
1014 | | // |
1015 | | |
1016 | | // |
1017 | | // Check if the new frame buffer descriptor is |
1018 | | // compatible with the image file header. |
1019 | | // |
1020 | |
|
1021 | 0 | const ChannelList &channels = _data->header.channels(); |
1022 | |
|
1023 | 0 | for (FrameBuffer::ConstIterator j = frameBuffer.begin(); |
1024 | 0 | j != frameBuffer.end(); |
1025 | 0 | ++j) |
1026 | 0 | { |
1027 | 0 | ChannelList::ConstIterator i = channels.find (j.name()); |
1028 | |
|
1029 | 0 | if (i == channels.end()) |
1030 | 0 | continue; |
1031 | | |
1032 | 0 | if (i.channel().xSampling != j.slice().xSampling || |
1033 | 0 | i.channel().ySampling != j.slice().ySampling) |
1034 | 0 | THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors " |
1035 | 0 | "of \"" << i.name() << "\" channel " |
1036 | 0 | "of input file \"" << fileName() << "\" are " |
1037 | 0 | "not compatible with the frame buffer's " |
1038 | 0 | "subsampling factors."); |
1039 | 0 | } |
1040 | | |
1041 | | // |
1042 | | // Initialize the slice table for readPixels(). |
1043 | | // |
1044 | | |
1045 | 0 | vector<TInSliceInfo> slices; |
1046 | 0 | ChannelList::ConstIterator i = channels.begin(); |
1047 | |
|
1048 | 0 | for (FrameBuffer::ConstIterator j = frameBuffer.begin(); |
1049 | 0 | j != frameBuffer.end(); |
1050 | 0 | ++j) |
1051 | 0 | { |
1052 | 0 | while (i != channels.end() && strcmp (i.name(), j.name()) < 0) |
1053 | 0 | { |
1054 | | // |
1055 | | // Channel i is present in the file but not |
1056 | | // in the frame buffer; data for channel i |
1057 | | // will be skipped during readPixels(). |
1058 | | // |
1059 | |
|
1060 | 0 | slices.push_back (TInSliceInfo (i.channel().type, |
1061 | 0 | i.channel().type, |
1062 | 0 | 0, // base |
1063 | 0 | 0, // xStride |
1064 | 0 | 0, // yStride |
1065 | 0 | false, // fill |
1066 | 0 | true, // skip |
1067 | 0 | 0.0)); // fillValue |
1068 | 0 | ++i; |
1069 | 0 | } |
1070 | |
|
1071 | 0 | bool fill = false; |
1072 | |
|
1073 | 0 | if (i == channels.end() || strcmp (i.name(), j.name()) > 0) |
1074 | 0 | { |
1075 | | // |
1076 | | // Channel i is present in the frame buffer, but not in the file. |
1077 | | // In the frame buffer, slice j will be filled with a default value. |
1078 | | // |
1079 | |
|
1080 | 0 | fill = true; |
1081 | 0 | } |
1082 | |
|
1083 | 0 | slices.push_back (TInSliceInfo (j.slice().type, |
1084 | 0 | fill? j.slice().type: i.channel().type, |
1085 | 0 | j.slice().base, |
1086 | 0 | j.slice().xStride, |
1087 | 0 | j.slice().yStride, |
1088 | 0 | fill, |
1089 | 0 | false, // skip |
1090 | 0 | j.slice().fillValue, |
1091 | 0 | (j.slice().xTileCoords)? 1: 0, |
1092 | 0 | (j.slice().yTileCoords)? 1: 0)); |
1093 | |
|
1094 | 0 | if (i != channels.end() && !fill) |
1095 | 0 | ++i; |
1096 | 0 | } |
1097 | |
|
1098 | 0 | while (i != channels.end()) |
1099 | 0 | { |
1100 | | // |
1101 | | // Channel i is present in the file but not |
1102 | | // in the frame buffer; data for channel i |
1103 | | // will be skipped during readPixels(). |
1104 | | // |
1105 | |
|
1106 | 0 | slices.push_back (TInSliceInfo (i.channel().type, |
1107 | 0 | i.channel().type, |
1108 | 0 | 0, // base |
1109 | 0 | 0, // xStride |
1110 | 0 | 0, // yStride |
1111 | 0 | false, // fill |
1112 | 0 | true, // skip |
1113 | 0 | 0.0)); // fillValue |
1114 | 0 | ++i; |
1115 | 0 | } |
1116 | | |
1117 | | // |
1118 | | // Store the new frame buffer. |
1119 | | // |
1120 | |
|
1121 | 0 | _data->frameBuffer = frameBuffer; |
1122 | 0 | _data->slices = slices; |
1123 | 0 | } |
1124 | | |
1125 | | |
1126 | | const FrameBuffer & |
1127 | | TiledInputFile::frameBuffer () const |
1128 | 0 | { |
1129 | 0 | Lock lock (*_data->_streamData); |
1130 | 0 | return _data->frameBuffer; |
1131 | 0 | } |
1132 | | |
1133 | | |
1134 | | bool |
1135 | | TiledInputFile::isComplete () const |
1136 | 0 | { |
1137 | 0 | return _data->fileIsComplete; |
1138 | 0 | } |
1139 | | |
1140 | | |
1141 | | void |
1142 | | TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly) |
1143 | 0 | { |
1144 | | // |
1145 | | // Read a range of tiles from the file into the framebuffer |
1146 | | // |
1147 | |
|
1148 | 0 | try |
1149 | 0 | { |
1150 | 0 | Lock lock (*_data->_streamData); |
1151 | |
|
1152 | 0 | if (_data->slices.size() == 0) |
1153 | 0 | throw IEX_NAMESPACE::ArgExc ("No frame buffer specified " |
1154 | 0 | "as pixel data destination."); |
1155 | | |
1156 | 0 | if (!isValidLevel (lx, ly)) |
1157 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1158 | 0 | "Level coordinate " |
1159 | 0 | "(" << lx << ", " << ly << ") " |
1160 | 0 | "is invalid."); |
1161 | | |
1162 | | // |
1163 | | // Determine the first and last tile coordinates in both dimensions. |
1164 | | // We always attempt to read the range of tiles in the order that |
1165 | | // they are stored in the file. |
1166 | | // |
1167 | | |
1168 | 0 | if (dx1 > dx2) |
1169 | 0 | std::swap (dx1, dx2); |
1170 | | |
1171 | 0 | if (dy1 > dy2) |
1172 | 0 | std::swap (dy1, dy2); |
1173 | | |
1174 | 0 | int dyStart = dy1; |
1175 | 0 | int dyStop = dy2 + 1; |
1176 | 0 | int dY = 1; |
1177 | |
|
1178 | 0 | if (_data->lineOrder == DECREASING_Y) |
1179 | 0 | { |
1180 | 0 | dyStart = dy2; |
1181 | 0 | dyStop = dy1 - 1; |
1182 | 0 | dY = -1; |
1183 | 0 | } |
1184 | | |
1185 | | // |
1186 | | // Create a task group for all tile buffer tasks. When the |
1187 | | // task group goes out of scope, the destructor waits until |
1188 | | // all tasks are complete. |
1189 | | // |
1190 | | |
1191 | 0 | { |
1192 | 0 | TaskGroup taskGroup; |
1193 | 0 | int tileNumber = 0; |
1194 | | |
1195 | 0 | for (int dy = dyStart; dy != dyStop; dy += dY) |
1196 | 0 | { |
1197 | 0 | for (int dx = dx1; dx <= dx2; dx++) |
1198 | 0 | { |
1199 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1200 | 0 | THROW (IEX_NAMESPACE::ArgExc, |
1201 | 0 | "Tile (" << dx << ", " << dy << ", " << |
1202 | 0 | lx << "," << ly << ") is not a valid tile."); |
1203 | | |
1204 | 0 | ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup, |
1205 | 0 | _data->_streamData, |
1206 | 0 | _data, |
1207 | 0 | tileNumber++, |
1208 | 0 | dx, dy, |
1209 | 0 | lx, ly)); |
1210 | 0 | } |
1211 | 0 | } |
1212 | | |
1213 | | // |
1214 | | // finish all tasks |
1215 | | // |
1216 | 0 | } |
1217 | | |
1218 | | // |
1219 | | // Exeption handling: |
1220 | | // |
1221 | | // TileBufferTask::execute() may have encountered exceptions, but |
1222 | | // those exceptions occurred in another thread, not in the thread |
1223 | | // that is executing this call to TiledInputFile::readTiles(). |
1224 | | // TileBufferTask::execute() has caught all exceptions and stored |
1225 | | // the exceptions' what() strings in the tile buffers. |
1226 | | // Now we check if any tile buffer contains a stored exception; if |
1227 | | // this is the case then we re-throw the exception in this thread. |
1228 | | // (It is possible that multiple tile buffers contain stored |
1229 | | // exceptions. We re-throw the first exception we find and |
1230 | | // ignore all others.) |
1231 | | // |
1232 | | |
1233 | 0 | const string *exception = 0; |
1234 | |
|
1235 | 0 | for (size_t i = 0; i < _data->tileBuffers.size(); ++i) |
1236 | 0 | { |
1237 | 0 | TileBuffer *tileBuffer = _data->tileBuffers[i]; |
1238 | |
|
1239 | 0 | if (tileBuffer->hasException && !exception) |
1240 | 0 | exception = &tileBuffer->exception; |
1241 | |
|
1242 | 0 | tileBuffer->hasException = false; |
1243 | 0 | } |
1244 | |
|
1245 | 0 | if (exception) |
1246 | 0 | throw IEX_NAMESPACE::IoExc (*exception); |
1247 | 0 | } |
1248 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1249 | 0 | { |
1250 | 0 | REPLACE_EXC (e, "Error reading pixel data from image " |
1251 | 0 | "file \"" << fileName() << "\". " << e); |
1252 | 0 | throw; |
1253 | 0 | } |
1254 | 0 | } |
1255 | | |
1256 | | |
1257 | | void |
1258 | | TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l) |
1259 | 0 | { |
1260 | 0 | readTiles (dx1, dx2, dy1, dy2, l, l); |
1261 | 0 | } |
1262 | | |
1263 | | |
1264 | | void |
1265 | | TiledInputFile::readTile (int dx, int dy, int lx, int ly) |
1266 | 0 | { |
1267 | 0 | readTiles (dx, dx, dy, dy, lx, ly); |
1268 | 0 | } |
1269 | | |
1270 | | |
1271 | | void |
1272 | | TiledInputFile::readTile (int dx, int dy, int l) |
1273 | 0 | { |
1274 | 0 | readTile (dx, dy, l, l); |
1275 | 0 | } |
1276 | | |
1277 | | |
1278 | | void |
1279 | | TiledInputFile::rawTileData (int &dx, int &dy, |
1280 | | int &lx, int &ly, |
1281 | | const char *&pixelData, |
1282 | | int &pixelDataSize) |
1283 | 0 | { |
1284 | 0 | try |
1285 | 0 | { |
1286 | 0 | Lock lock (*_data->_streamData); |
1287 | |
|
1288 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1289 | 0 | throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside " |
1290 | 0 | "the image file's data window."); |
1291 | | |
1292 | 0 | TileBuffer *tileBuffer = _data->getTileBuffer (0); |
1293 | | |
1294 | | // |
1295 | | // if file is a multipart file, we have to seek to the required tile |
1296 | | // since we don't know where the file pointer is |
1297 | | // |
1298 | 0 | int old_dx=dx; |
1299 | 0 | int old_dy=dy; |
1300 | 0 | int old_lx=lx; |
1301 | 0 | int old_ly=ly; |
1302 | 0 | if(isMultiPart(version())) |
1303 | 0 | { |
1304 | 0 | _data->_streamData->is->seekg(_data->tileOffsets(dx,dy,lx,ly)); |
1305 | 0 | } |
1306 | 0 | readNextTileData (_data->_streamData, _data, dx, dy, lx, ly, |
1307 | 0 | tileBuffer->buffer, |
1308 | 0 | pixelDataSize); |
1309 | 0 | if(isMultiPart(version())) |
1310 | 0 | { |
1311 | 0 | if (old_dx!=dx || old_dy !=dy || old_lx!=lx || old_ly!=ly) |
1312 | 0 | { |
1313 | 0 | throw IEX_NAMESPACE::ArgExc ("rawTileData read the wrong tile"); |
1314 | 0 | } |
1315 | 0 | } |
1316 | 0 | pixelData = tileBuffer->buffer; |
1317 | 0 | } |
1318 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1319 | 0 | { |
1320 | 0 | REPLACE_EXC (e, "Error reading pixel data from image " |
1321 | 0 | "file \"" << fileName() << "\". " << e); |
1322 | 0 | throw; |
1323 | 0 | } |
1324 | 0 | } |
1325 | | |
1326 | | |
1327 | | unsigned int |
1328 | | TiledInputFile::tileXSize () const |
1329 | 0 | { |
1330 | 0 | return _data->tileDesc.xSize; |
1331 | 0 | } |
1332 | | |
1333 | | |
1334 | | unsigned int |
1335 | | TiledInputFile::tileYSize () const |
1336 | 0 | { |
1337 | 0 | return _data->tileDesc.ySize; |
1338 | 0 | } |
1339 | | |
1340 | | |
1341 | | LevelMode |
1342 | | TiledInputFile::levelMode () const |
1343 | 0 | { |
1344 | 0 | return _data->tileDesc.mode; |
1345 | 0 | } |
1346 | | |
1347 | | |
1348 | | LevelRoundingMode |
1349 | | TiledInputFile::levelRoundingMode () const |
1350 | 0 | { |
1351 | 0 | return _data->tileDesc.roundingMode; |
1352 | 0 | } |
1353 | | |
1354 | | |
1355 | | int |
1356 | | TiledInputFile::numLevels () const |
1357 | 0 | { |
1358 | 0 | if (levelMode() == RIPMAP_LEVELS) |
1359 | 0 | THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image " |
1360 | 0 | "file \"" << fileName() << "\" " |
1361 | 0 | "(numLevels() is not defined for files " |
1362 | 0 | "with RIPMAP level mode)."); |
1363 | | |
1364 | 0 | return _data->numXLevels; |
1365 | 0 | } |
1366 | | |
1367 | | |
1368 | | int |
1369 | | TiledInputFile::numXLevels () const |
1370 | 0 | { |
1371 | 0 | return _data->numXLevels; |
1372 | 0 | } |
1373 | | |
1374 | | |
1375 | | int |
1376 | | TiledInputFile::numYLevels () const |
1377 | 0 | { |
1378 | 0 | return _data->numYLevels; |
1379 | 0 | } |
1380 | | |
1381 | | |
1382 | | bool |
1383 | | TiledInputFile::isValidLevel (int lx, int ly) const |
1384 | 0 | { |
1385 | 0 | if (lx < 0 || ly < 0) |
1386 | 0 | return false; |
1387 | | |
1388 | 0 | if (levelMode() == MIPMAP_LEVELS && lx != ly) |
1389 | 0 | return false; |
1390 | | |
1391 | 0 | if (lx >= numXLevels() || ly >= numYLevels()) |
1392 | 0 | return false; |
1393 | | |
1394 | 0 | return true; |
1395 | 0 | } |
1396 | | |
1397 | | |
1398 | | int |
1399 | | TiledInputFile::levelWidth (int lx) const |
1400 | 0 | { |
1401 | 0 | try |
1402 | 0 | { |
1403 | 0 | return levelSize (_data->minX, _data->maxX, lx, |
1404 | 0 | _data->tileDesc.roundingMode); |
1405 | 0 | } |
1406 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1407 | 0 | { |
1408 | 0 | REPLACE_EXC (e, "Error calling levelWidth() on image " |
1409 | 0 | "file \"" << fileName() << "\". " << e); |
1410 | 0 | throw; |
1411 | 0 | } |
1412 | 0 | } |
1413 | | |
1414 | | |
1415 | | int |
1416 | | TiledInputFile::levelHeight (int ly) const |
1417 | 0 | { |
1418 | 0 | try |
1419 | 0 | { |
1420 | 0 | return levelSize (_data->minY, _data->maxY, ly, |
1421 | 0 | _data->tileDesc.roundingMode); |
1422 | 0 | } |
1423 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1424 | 0 | { |
1425 | 0 | REPLACE_EXC (e, "Error calling levelHeight() on image " |
1426 | 0 | "file \"" << fileName() << "\". " << e); |
1427 | 0 | throw; |
1428 | 0 | } |
1429 | 0 | } |
1430 | | |
1431 | | |
1432 | | int |
1433 | | TiledInputFile::numXTiles (int lx) const |
1434 | 0 | { |
1435 | 0 | if (lx < 0 || lx >= _data->numXLevels) |
1436 | 0 | { |
1437 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image " |
1438 | 0 | "file \"" << _data->_streamData->is->fileName() << "\" " |
1439 | 0 | "(Argument is not in valid range)."); |
1440 | |
|
1441 | 0 | } |
1442 | | |
1443 | 0 | return _data->numXTiles[lx]; |
1444 | 0 | } |
1445 | | |
1446 | | |
1447 | | int |
1448 | | TiledInputFile::numYTiles (int ly) const |
1449 | 0 | { |
1450 | 0 | if (ly < 0 || ly >= _data->numYLevels) |
1451 | 0 | { |
1452 | 0 | THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image " |
1453 | 0 | "file \"" << _data->_streamData->is->fileName() << "\" " |
1454 | 0 | "(Argument is not in valid range)."); |
1455 | 0 | } |
1456 | | |
1457 | 0 | return _data->numYTiles[ly]; |
1458 | 0 | } |
1459 | | |
1460 | | |
1461 | | Box2i |
1462 | | TiledInputFile::dataWindowForLevel (int l) const |
1463 | 0 | { |
1464 | 0 | return dataWindowForLevel (l, l); |
1465 | 0 | } |
1466 | | |
1467 | | |
1468 | | Box2i |
1469 | | TiledInputFile::dataWindowForLevel (int lx, int ly) const |
1470 | 0 | { |
1471 | 0 | try |
1472 | 0 | { |
1473 | 0 | return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel ( |
1474 | 0 | _data->tileDesc, |
1475 | 0 | _data->minX, _data->maxX, |
1476 | 0 | _data->minY, _data->maxY, |
1477 | 0 | lx, ly); |
1478 | 0 | } |
1479 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1480 | 0 | { |
1481 | 0 | REPLACE_EXC (e, "Error calling dataWindowForLevel() on image " |
1482 | 0 | "file \"" << fileName() << "\". " << e); |
1483 | 0 | throw; |
1484 | 0 | } |
1485 | 0 | } |
1486 | | |
1487 | | |
1488 | | Box2i |
1489 | | TiledInputFile::dataWindowForTile (int dx, int dy, int l) const |
1490 | 0 | { |
1491 | 0 | return dataWindowForTile (dx, dy, l, l); |
1492 | 0 | } |
1493 | | |
1494 | | |
1495 | | Box2i |
1496 | | TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const |
1497 | 0 | { |
1498 | 0 | try |
1499 | 0 | { |
1500 | 0 | if (!isValidTile (dx, dy, lx, ly)) |
1501 | 0 | throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range."); |
1502 | | |
1503 | 0 | return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile ( |
1504 | 0 | _data->tileDesc, |
1505 | 0 | _data->minX, _data->maxX, |
1506 | 0 | _data->minY, _data->maxY, |
1507 | 0 | dx, dy, lx, ly); |
1508 | 0 | } |
1509 | 0 | catch (IEX_NAMESPACE::BaseExc &e) |
1510 | 0 | { |
1511 | 0 | REPLACE_EXC (e, "Error calling dataWindowForTile() on image " |
1512 | 0 | "file \"" << fileName() << "\". " << e); |
1513 | 0 | throw; |
1514 | 0 | } |
1515 | 0 | } |
1516 | | |
1517 | | |
1518 | | bool |
1519 | | TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const |
1520 | 0 | { |
1521 | 0 | return ((lx < _data->numXLevels && lx >= 0) && |
1522 | 0 | (ly < _data->numYLevels && ly >= 0) && |
1523 | 0 | (dx < _data->numXTiles[lx] && dx >= 0) && |
1524 | 0 | (dy < _data->numYTiles[ly] && dy >= 0)); |
1525 | 0 | } |
1526 | | |
1527 | | void TiledInputFile::tileOrder(int dx[], int dy[], int lx[], int ly[]) const |
1528 | 0 | { |
1529 | 0 | return _data->tileOffsets.getTileOrder(dx,dy,lx,ly); |
1530 | 0 | } |
1531 | | |
1532 | | |
1533 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |