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 InputFile 38// 39//----------------------------------------------------------------------------- 40 41#include <ImfInputFile.h> 42#include <ImfScanLineInputFile.h> 43#include <ImfTiledInputFile.h> 44#include <ImfChannelList.h> 45#include <ImfMisc.h> 46#include <ImfStdIO.h> 47#include <ImfVersion.h> 48#include "ImathFun.h" 49#include "IlmThreadMutex.h" 50#include "Iex.h" 51#include "half.h" 52#include <fstream> 53#include <algorithm> 54 55 56namespace Imf { 57 58 59using Imath::Box2i; 60using Imath::divp; 61using Imath::modp; 62using IlmThread::Mutex; 63using IlmThread::Lock; 64 65 66// 67// Struct InputFile::Data stores things that will be 68// needed between calls to readPixels 69// 70 71struct InputFile::Data: public Mutex 72{ 73 Header header; 74 int version; 75 IStream * is; 76 bool deleteStream; 77 78 TiledInputFile * tFile; 79 ScanLineInputFile * sFile; 80 81 LineOrder lineOrder; // the file's lineorder 82 int minY; // data window's min y coord 83 int maxY; // data window's max x coord 84 85 FrameBuffer tFileBuffer; 86 FrameBuffer * cachedBuffer; 87 88 int cachedTileY; 89 int offset; 90 91 int numThreads; 92 93 Data (bool del, int numThreads); 94 ~Data (); 95 96 void deleteCachedBuffer(); 97}; 98 99 100InputFile::Data::Data (bool del, int numThreads): 101 is (0), 102 deleteStream (del), 103 tFile (0), 104 sFile (0), 105 cachedBuffer (0), 106 cachedTileY (-1), 107 numThreads (numThreads) 108{ 109 // empty 110} 111 112 113InputFile::Data::~Data () 114{ 115 delete tFile; 116 delete sFile; 117 118 if (deleteStream) 119 delete is; 120 121 deleteCachedBuffer(); 122} 123 124 125void 126InputFile::Data::deleteCachedBuffer() 127{ 128 // 129 // Delete the cached frame buffer, and all memory 130 // allocated for the slices in the cached frameBuffer. 131 // 132 133 if (cachedBuffer) 134 { 135 for (FrameBuffer::Iterator k = cachedBuffer->begin(); 136 k != cachedBuffer->end(); 137 ++k) 138 { 139 Slice &s = k.slice(); 140 141 switch (s.type) 142 { 143 case UINT: 144 145 delete [] (((unsigned int *)s.base) + offset); 146 break; 147 148 case HALF: 149 150 delete [] ((half *)s.base + offset); 151 break; 152 153 case FLOAT: 154 155 delete [] (((float *)s.base) + offset); 156 break; 157 } 158 } 159 160 // 161 // delete the cached frame buffer 162 // 163 164 delete cachedBuffer; 165 cachedBuffer = 0; 166 } 167} 168 169 170namespace { 171 172void 173bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2) 174{ 175 // 176 // bufferedReadPixels reads each row of tiles that intersect the 177 // scan-line range (scanLine1 to scanLine2). The previous row of 178 // tiles is cached in order to prevent redundent tile reads when 179 // accessing scanlines sequentially. 180 // 181 182 int minY = std::min (scanLine1, scanLine2); 183 int maxY = std::max (scanLine1, scanLine2); 184 185 if (minY < ifd->minY || maxY > ifd->maxY) 186 { 187 throw Iex::ArgExc ("Tried to read scan line outside " 188 "the image file's data window."); 189 } 190 191 // 192 // The minimum and maximum y tile coordinates that intersect this 193 // scanline range 194 // 195 196 int minDy = (minY - ifd->minY) / ifd->tFile->tileYSize(); 197 int maxDy = (maxY - ifd->minY) / ifd->tFile->tileYSize(); 198 199 // 200 // Figure out which one is first in the file so we can read without seeking 201 // 202 203 int yStart, yEnd, yStep; 204 205 if (ifd->lineOrder == DECREASING_Y) 206 { 207 yStart = maxDy; 208 yEnd = minDy - 1; 209 yStep = -1; 210 } 211 else 212 { 213 yStart = minDy; 214 yEnd = maxDy + 1; 215 yStep = 1; 216 } 217 218 // 219 // the number of pixels in a row of tiles 220 // 221 222 Box2i levelRange = ifd->tFile->dataWindowForLevel(0); 223 224 // 225 // Read the tiles into our temporary framebuffer and copy them into 226 // the user's buffer 227 // 228 229 for (int j = yStart; j != yEnd; j += yStep) 230 { 231 Box2i tileRange = ifd->tFile->dataWindowForTile (0, j, 0); 232 233 int minYThisRow = std::max (minY, tileRange.min.y); 234 int maxYThisRow = std::min (maxY, tileRange.max.y); 235 236 if (j != ifd->cachedTileY) 237 { 238 // 239 // We don't have any valid buffered info, so we need to read in 240 // from the file. 241 // 242 243 ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j); 244 ifd->cachedTileY = j; 245 } 246 247 // 248 // Copy the data from our cached framebuffer into the user's 249 // framebuffer. 250 // 251 252 for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin(); 253 k != ifd->cachedBuffer->end(); 254 ++k) 255 { 256 Slice fromSlice = k.slice(); // slice to write from 257 Slice toSlice = ifd->tFileBuffer[k.name()]; // slice to write to 258 259 char *fromPtr, *toPtr; 260 int size = pixelTypeSize (toSlice.type); 261 262 int xStart = levelRange.min.x; 263 int yStart = minYThisRow; 264 265 while (modp (xStart, toSlice.xSampling) != 0) 266 ++xStart; 267 268 while (modp (yStart, toSlice.ySampling) != 0) 269 ++yStart; 270 271 for (int y = yStart; 272 y <= maxYThisRow; 273 y += toSlice.ySampling) 274 { 275 // 276 // Set the pointers to the start of the y scanline in 277 // this row of tiles 278 // 279 280 fromPtr = fromSlice.base + 281 (y - tileRange.min.y) * fromSlice.yStride + 282 xStart * fromSlice.xStride; 283 284 toPtr = toSlice.base + 285 divp (y, toSlice.ySampling) * toSlice.yStride + 286 divp (xStart, toSlice.xSampling) * toSlice.xStride; 287 288 // 289 // Copy all pixels for the scanline in this row of tiles 290 // 291 292 for (int x = xStart; 293 x <= levelRange.max.x; 294 x += toSlice.xSampling) 295 { 296 for (size_t i = 0; i < size; ++i) 297 toPtr[i] = fromPtr[i]; 298 299 fromPtr += fromSlice.xStride * toSlice.xSampling; 300 toPtr += toSlice.xStride; 301 } 302 } 303 } 304 } 305} 306 307} // namespace 308 309 310 311InputFile::InputFile (const char fileName[], int numThreads): 312 _data (new Data (true, numThreads)) 313{ 314 try 315 { 316 _data->is = new StdIFStream (fileName); 317 initialize(); 318 } 319 catch (Iex::BaseExc &e) 320 { 321 delete _data; 322 323 REPLACE_EXC (e, "Cannot read image file " 324 "\"" << fileName << "\". " << e); 325 throw; 326 } 327 catch (...) 328 { 329 delete _data; 330 throw; 331 } 332} 333 334 335InputFile::InputFile (IStream &is, int numThreads): 336 _data (new Data (false, numThreads)) 337{ 338 try 339 { 340 _data->is = &is; 341 initialize(); 342 } 343 catch (Iex::BaseExc &e) 344 { 345 delete _data; 346 347 REPLACE_EXC (e, "Cannot read image file " 348 "\"" << is.fileName() << "\". " << e); 349 throw; 350 } 351 catch (...) 352 { 353 delete _data; 354 throw; 355 } 356} 357 358 359void 360InputFile::initialize () 361{ 362 _data->header.readFrom (*_data->is, _data->version); 363 _data->header.sanityCheck (isTiled (_data->version)); 364 365 if (isTiled (_data->version)) 366 { 367 _data->lineOrder = _data->header.lineOrder(); 368 369 // 370 // Save the dataWindow information 371 // 372 373 const Box2i &dataWindow = _data->header.dataWindow(); 374 _data->minY = dataWindow.min.y; 375 _data->maxY = dataWindow.max.y; 376 377 _data->tFile = new TiledInputFile (_data->header, 378 _data->is, 379 _data->version, 380 _data->numThreads); 381 } 382 else 383 { 384 _data->sFile = new ScanLineInputFile (_data->header, 385 _data->is, 386 _data->numThreads); 387 } 388} 389 390 391InputFile::~InputFile () 392{ 393 delete _data; 394} 395 396 397const char * 398InputFile::fileName () const 399{ 400 return _data->is->fileName(); 401} 402 403 404const Header & 405InputFile::header () const 406{ 407 return _data->header; 408} 409 410 411int 412InputFile::version () const 413{ 414 return _data->version; 415} 416 417 418void 419InputFile::setFrameBuffer (const FrameBuffer &frameBuffer) 420{ 421 if (isTiled (_data->version)) 422 { 423 Lock lock (*_data); 424 425 // 426 // We must invalidate the cached buffer if the new frame 427 // buffer has a different set of channels than the old 428 // frame buffer, or if the type of a channel has changed. 429 // 430 431 const FrameBuffer &oldFrameBuffer = _data->tFileBuffer; 432 433 FrameBuffer::ConstIterator i = oldFrameBuffer.begin(); 434 FrameBuffer::ConstIterator j = frameBuffer.begin(); 435 436 while (i != oldFrameBuffer.end()) 437 { 438 if (strcmp (i.name(), j.name()) || i.slice().type != j.slice().type) 439 break; 440 441 ++i; 442 ++j; 443 } 444 445 if (i != oldFrameBuffer.end() || j != frameBuffer.end()) 446 { 447 // 448 // Invalidate the cached buffer. 449 // 450 451 _data->deleteCachedBuffer (); 452 _data->cachedTileY = -1; 453 454 // 455 // Create new a cached frame buffer. It can hold a single 456 // row of tiles. The cached buffer can be reused for each 457 // row of tiles because we set the yTileCoords parameter of 458 // each Slice to true. 459 // 460 461 const Box2i &dataWindow = _data->header.dataWindow(); 462 _data->cachedBuffer = new FrameBuffer(); 463 _data->offset = dataWindow.min.x; 464 465 int tileRowSize = (dataWindow.max.x - dataWindow.min.x + 1) * 466 _data->tFile->tileYSize(); 467 468 for (FrameBuffer::ConstIterator k = frameBuffer.begin(); 469 k != frameBuffer.end(); 470 ++k) 471 { 472 Slice s = k.slice(); 473 474 switch (s.type) 475 { 476 case UINT: 477 478 _data->cachedBuffer->insert 479 (k.name(), 480 Slice (UINT, 481 (char *)(new unsigned int[tileRowSize] - 482 _data->offset), 483 sizeof (unsigned int), 484 sizeof (unsigned int) * 485 _data->tFile->levelWidth(0), 486 1, 1, 487 s.fillValue, 488 false, true)); 489 break; 490 491 case HALF: 492 493 _data->cachedBuffer->insert 494 (k.name(), 495 Slice (HALF, 496 (char *)(new half[tileRowSize] - 497 _data->offset), 498 sizeof (half), 499 sizeof (half) * 500 _data->tFile->levelWidth(0), 501 1, 1, 502 s.fillValue, 503 false, true)); 504 break; 505 506 case FLOAT: 507 508 _data->cachedBuffer->insert 509 (k.name(), 510 Slice (FLOAT, 511 (char *)(new float[tileRowSize] - 512 _data->offset), 513 sizeof(float), 514 sizeof(float) * 515 _data->tFile->levelWidth(0), 516 1, 1, 517 s.fillValue, 518 false, true)); 519 break; 520 521 default: 522 523 throw Iex::ArgExc ("Unknown pixel data type."); 524 } 525 } 526 527 _data->tFile->setFrameBuffer (*_data->cachedBuffer); 528 } 529 530 _data->tFileBuffer = frameBuffer; 531 } 532 else 533 { 534 _data->sFile->setFrameBuffer (frameBuffer); 535 } 536} 537 538 539const FrameBuffer & 540InputFile::frameBuffer () const 541{ 542 if (isTiled (_data->version)) 543 { 544 Lock lock (*_data); 545 return _data->tFileBuffer; 546 } 547 else 548 { 549 return _data->sFile->frameBuffer(); 550 } 551} 552 553 554bool 555InputFile::isComplete () const 556{ 557 if (isTiled (_data->version)) 558 return _data->tFile->isComplete(); 559 else 560 return _data->sFile->isComplete(); 561} 562 563 564void 565InputFile::readPixels (int scanLine1, int scanLine2) 566{ 567 if (isTiled (_data->version)) 568 { 569 Lock lock (*_data); 570 bufferedReadPixels (_data, scanLine1, scanLine2); 571 } 572 else 573 { 574 _data->sFile->readPixels (scanLine1, scanLine2); 575 } 576} 577 578 579void 580InputFile::readPixels (int scanLine) 581{ 582 readPixels (scanLine, scanLine); 583} 584 585 586void 587InputFile::rawPixelData (int firstScanLine, 588 const char *&pixelData, 589 int &pixelDataSize) 590{ 591 try 592 { 593 if (isTiled (_data->version)) 594 { 595 throw Iex::ArgExc ("Tried to read a raw scanline " 596 "from a tiled image."); 597 } 598 599 _data->sFile->rawPixelData (firstScanLine, pixelData, pixelDataSize); 600 } 601 catch (Iex::BaseExc &e) 602 { 603 REPLACE_EXC (e, "Error reading pixel data from image " 604 "file \"" << fileName() << "\". " << e); 605 throw; 606 } 607} 608 609 610void 611InputFile::rawTileData (int &dx, int &dy, 612 int &lx, int &ly, 613 const char *&pixelData, 614 int &pixelDataSize) 615{ 616 try 617 { 618 if (!isTiled (_data->version)) 619 { 620 throw Iex::ArgExc ("Tried to read a raw tile " 621 "from a scanline-based image."); 622 } 623 624 _data->tFile->rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize); 625 } 626 catch (Iex::BaseExc &e) 627 { 628 REPLACE_EXC (e, "Error reading tile data from image " 629 "file \"" << fileName() << "\". " << e); 630 throw; 631 } 632} 633 634 635TiledInputFile* 636InputFile::tFile() 637{ 638 if (!isTiled (_data->version)) 639 { 640 throw Iex::ArgExc ("Cannot get a TiledInputFile pointer " 641 "from an InputFile that is not tiled."); 642 } 643 644 return _data->tFile; 645} 646 647 648} // namespace Imf 649