00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <algorithm>
00023 #include <numeric>
00024 #include <boost/scoped_ptr.hpp>
00025 #include <boost/scoped_array.hpp>
00026
00027 #include <libopenraw++/thumbnail.h>
00028 #include <libopenraw++/rawdata.h>
00029
00030 #include "debug.h"
00031 #include "io/stream.h"
00032 #include "io/streamclone.h"
00033 #include "io/file.h"
00034 #include "ifd.h"
00035 #include "ifdfile.h"
00036 #include "ifdfilecontainer.h"
00037 #include "jfifcontainer.h"
00038 #include "neffile.h"
00039 #include "metavalue.h"
00040 #include "unpack.h"
00041
00042 using namespace Debug;
00043 using boost::scoped_ptr;
00044
00045
00046 namespace OpenRaw {
00047 namespace Internals {
00048
00049
00050 IFDFile::IFDFile(const char *_filename, Type _type,
00051 bool instantiateContainer)
00052 : RawFile(_filename, _type),
00053 m_thumbLocations(),
00054 m_io(new IO::File(_filename)),
00055 m_container(NULL)
00056 {
00057 if(instantiateContainer) {
00058 m_container = new IFDFileContainer(m_io, 0);
00059 }
00060 }
00061
00062 IFDFile::~IFDFile()
00063 {
00064 delete m_container;
00065 delete m_io;
00066 }
00067
00068
00069
00070 IFDDir::Ref IFDFile::_locateExifIfd()
00071 {
00072 m_mainIfd = _locateMainIfd();
00073 if (!m_mainIfd) {
00074 Trace(ERROR) << "IFDFile::_locateExifIfd() "
00075 "main IFD not found\n";
00076 return IFDDir::Ref();
00077 }
00078 return m_mainIfd->getExifIFD();
00079 }
00080
00081
00082 ::or_error IFDFile::_enumThumbnailSizes(std::vector<uint32_t> &list)
00083 {
00084 ::or_error err = OR_ERROR_NONE;
00085
00086 Trace(DEBUG1) << "_enumThumbnailSizes()\n";
00087 std::vector<IFDDir::Ref> & dirs = m_container->directories();
00088 std::vector<IFDDir::Ref>::iterator iter;
00089
00090 Trace(DEBUG1) << "num of dirs " << dirs.size() << "\n";
00091 for(iter = dirs.begin(); iter != dirs.end(); ++iter)
00092 {
00093 IFDDir::Ref & dir = *iter;
00094 dir->load();
00095 or_error ret = _locateThumbnail(dir, list);
00096 if (ret == OR_ERROR_NONE)
00097 {
00098 Trace(DEBUG1) << "Found " << list.back() << " pixels\n";
00099 }
00100 std::vector<IFDDir::Ref> subdirs;
00101 if(dir->getSubIFDs(subdirs)) {
00102 Trace(DEBUG1) << "Iterating subdirs\n";
00103 std::vector<IFDDir::Ref>::iterator iter2;
00104 for(iter2 = subdirs.begin(); iter2 != subdirs.end();
00105 ++iter2)
00106 {
00107 IFDDir::Ref & dir2 = *iter2;
00108 dir2->load();
00109 ret = _locateThumbnail(dir2, list);
00110 if (ret == OR_ERROR_NONE)
00111 {
00112 Trace(DEBUG1) << "Found " << list.back() << " pixels\n";
00113 }
00114 }
00115 }
00116 }
00117 if (list.size() <= 0) {
00118 err = OR_ERROR_NOT_FOUND;
00119 }
00120 return err;
00121 }
00122
00123
00124 ::or_error IFDFile::_locateThumbnail(const IFDDir::Ref & dir,
00125 std::vector<uint32_t> &list)
00126 {
00127 ::or_error ret = OR_ERROR_NOT_FOUND;
00128 bool got_it;
00129 uint32_t x = 0;
00130 uint32_t y = 0;
00131 ::or_data_type _type = OR_DATA_TYPE_NONE;
00132 uint32_t subtype = 0;
00133
00134 Trace(DEBUG1) << "_locateThumbnail\n";
00135
00136 got_it = dir->getValue(IFD::EXIF_TAG_NEW_SUBFILE_TYPE, subtype);
00137 Trace(DEBUG1) << "subtype " << subtype << "\n";
00138 if(!got_it) {
00139 if(!m_cfaIfd) {
00140 m_cfaIfd = _locateCfaIfd();
00141 }
00142 if(m_cfaIfd == dir) {
00143 return OR_ERROR_NOT_FOUND;
00144 }
00145 else {
00146 subtype = 1;
00147 }
00148 }
00149 if (subtype == 1) {
00150
00151 uint16_t photom_int = 0;
00152 got_it = dir->getValue(IFD::EXIF_TAG_PHOTOMETRIC_INTERPRETATION,
00153 photom_int);
00154
00155 if (got_it) {
00156 Trace(DEBUG1) << "photometric int " << photom_int << "\n";
00157 }
00158
00159 if (!got_it || (photom_int == 2)) {
00160
00161 got_it = dir->getIntegerValue(IFD::EXIF_TAG_IMAGE_WIDTH, x);
00162 got_it = dir->getIntegerValue(IFD::EXIF_TAG_IMAGE_LENGTH, y);
00163
00164 uint16_t compression = 0;
00165 got_it = dir->getValue(IFD::EXIF_TAG_COMPRESSION, compression);
00166
00167 uint32_t offset = 0;
00168 got_it = dir->getValue(IFD::EXIF_TAG_STRIP_OFFSETS, offset);
00169 if (!got_it || (compression == 6) || (compression == 7)) {
00170 if(!got_it) {
00171 got_it = dir->getValue(IFD::EXIF_TAG_JPEG_INTERCHANGE_FORMAT,
00172 offset);
00173 }
00174 if (got_it) {
00175
00176
00177 uint32_t byte_count;
00178 if(x && y && dir->getValue(IFD::EXIF_TAG_STRIP_BYTE_COUNTS, byte_count)) {
00179 if(byte_count >= (x * y * 3)) {
00180 _type = OR_DATA_TYPE_PIXMAP_8RGB;
00181 }
00182 else {
00183 _type = OR_DATA_TYPE_JPEG;
00184 }
00185 }
00186 else {
00187 _type = OR_DATA_TYPE_JPEG;
00188 Trace(DEBUG1) << "looking for JPEG at " << offset << "\n";
00189 if (x == 0 || y == 0) {
00190 scoped_ptr<IO::StreamClone> s(new IO::StreamClone(m_io, offset));
00191 scoped_ptr<JFIFContainer> jfif(new JFIFContainer(s.get(), 0));
00192 if (jfif->getDimensions(x,y)) {
00193 Trace(DEBUG1) << "JPEG dimensions x=" << x
00194 << " y=" << y << "\n";
00195 }
00196 else {
00197 _type = OR_DATA_TYPE_NONE;
00198 Trace(WARNING) << "Couldn't get JPEG "
00199 "dimensions.\n";
00200 }
00201 }
00202 else {
00203 Trace(DEBUG1) << "JPEG (supposed) dimensions x=" << x
00204 << " y=" << y << "\n";
00205 }
00206 }
00207
00208 }
00209 }
00210 else {
00211 Trace(DEBUG1) << "found strip offsets\n";
00212 if (x != 0 && y != 0) {
00213 _type = OR_DATA_TYPE_PIXMAP_8RGB;
00214 }
00215 }
00216 if(_type != OR_DATA_TYPE_NONE) {
00217 uint32_t dim = std::max(x, y);
00218 m_thumbLocations[dim] = IFDThumbDesc(x, y, _type, dir);
00219 list.push_back(dim);
00220 ret = OR_ERROR_NONE;
00221 }
00222 }
00223 else if (photom_int == 6) {
00224 Trace(WARNING) << "Unsupported YCbCr photometric "
00225 "interpretation.\n";
00226 }
00227 }
00228
00229 return ret;
00230 }
00231
00232
00233 ::or_error IFDFile::_getThumbnail(uint32_t size, Thumbnail & thumbnail)
00234 {
00235 ::or_error ret = OR_ERROR_NOT_FOUND;
00236 ThumbLocations::iterator iter = m_thumbLocations.find(size);
00237 if(iter != m_thumbLocations.end())
00238 {
00239 bool got_it;
00240
00241 IFDThumbDesc & desc = iter->second;
00242 thumbnail.setDataType(desc.type);
00243 uint32_t byte_length= 0;
00244 uint32_t offset = 0;
00245 uint32_t x = desc.x;
00246 uint32_t y = desc.y;
00247
00248 switch(desc.type)
00249 {
00250 case OR_DATA_TYPE_JPEG:
00251 got_it = desc.ifddir
00252 ->getValue(IFD::EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
00253 byte_length);
00254 if(got_it) {
00255 got_it = desc.ifddir
00256 ->getValue(IFD::EXIF_TAG_JPEG_INTERCHANGE_FORMAT,
00257 offset);
00258 }
00259 else {
00260
00261 got_it = desc.ifddir
00262 ->getValue(IFD::EXIF_TAG_STRIP_OFFSETS, offset);
00263 got_it = desc.ifddir
00264 ->getValue(IFD::EXIF_TAG_STRIP_BYTE_COUNTS, byte_length);
00265 }
00266 break;
00267 case OR_DATA_TYPE_PIXMAP_8RGB:
00268 got_it = desc.ifddir
00269 ->getValue(IFD::EXIF_TAG_STRIP_OFFSETS, offset);
00270 got_it = desc.ifddir
00271 ->getValue(IFD::EXIF_TAG_STRIP_BYTE_COUNTS, byte_length);
00272
00273 got_it = desc.ifddir
00274 ->getIntegerValue(IFD::EXIF_TAG_IMAGE_WIDTH, x);
00275 got_it = desc.ifddir
00276 ->getIntegerValue(IFD::EXIF_TAG_IMAGE_LENGTH, y);
00277 break;
00278 default:
00279 break;
00280 }
00281 if (byte_length != 0) {
00282 void *p = thumbnail.allocData(byte_length);
00283 size_t real_size = m_container->fetchData(p, offset,
00284 byte_length);
00285 if (real_size < byte_length) {
00286 Trace(WARNING) << "Size mismatch for data: ignoring.\n";
00287 }
00288
00289 thumbnail.setDimensions(x, y);
00290 ret = OR_ERROR_NONE;
00291 }
00292 }
00293
00294 return ret;
00295 }
00296
00297
00298 MetaValue *IFDFile::_getMetaValue(int32_t meta_index)
00299 {
00300 MetaValue * val = NULL;
00301 IFDDir::Ref ifd;
00302 if(META_INDEX_MASKOUT(meta_index) == META_NS_TIFF) {
00303 if(!m_mainIfd) {
00304 m_mainIfd = _locateMainIfd();
00305 }
00306 ifd = m_mainIfd;
00307 }
00308 else if(META_INDEX_MASKOUT(meta_index) == META_NS_EXIF) {
00309 if(!m_exifIfd) {
00310 m_exifIfd = _locateExifIfd();
00311 }
00312 ifd = m_exifIfd;
00313 }
00314 else {
00315 Trace(ERROR) << "Unknown Meta Namespace\n";
00316 }
00317 if(ifd) {
00318 Trace(DEBUG1) << "Meta value for "
00319 << META_NS_MASKOUT(meta_index) << "\n";
00320 uint16_t n = 0;
00321 bool got_it = ifd->getValue(META_NS_MASKOUT(meta_index), n);
00322 if(got_it){
00323 Trace(DEBUG1) << "found value\n";
00324 val = new MetaValue(boost::any(static_cast<int32_t>(n)));
00325 }
00326 }
00327 return val;
00328 }
00329
00330
00331 namespace {
00332
00333 RawData::CfaPattern
00334 _convertArrayToCfaPattern(const std::vector<uint8_t> &cfaPattern)
00335 {
00336 RawData::CfaPattern cfa_pattern = OR_CFA_PATTERN_NON_RGB22;
00337 if(cfaPattern.size() != 4) {
00338 Trace(WARNING) << "Unsupported bayer pattern\n";
00339 }
00340 else {
00341 Trace(DEBUG2) << "patter is = " << cfaPattern[0] << ", "
00342 << cfaPattern[1] << ", " << cfaPattern[2]
00343 << ", " << cfaPattern[3] << "\n";
00344 switch(cfaPattern[0]) {
00345 case IFD::CFA_RED:
00346 switch(cfaPattern[1]) {
00347 case IFD::CFA_GREEN:
00348 if((cfaPattern[2] == IFD::CFA_GREEN)
00349 && (cfaPattern[3] == IFD::CFA_BLUE))
00350 {
00351 cfa_pattern = OR_CFA_PATTERN_RGGB;
00352 }
00353 break;
00354 }
00355 break;
00356 case IFD::CFA_GREEN:
00357 switch(cfaPattern[1]) {
00358 case IFD::CFA_RED:
00359 if((cfaPattern[2] == 2)
00360 && (cfaPattern[3] == IFD::CFA_GREEN))
00361 {
00362 cfa_pattern = OR_CFA_PATTERN_GRBG;
00363 }
00364 break;
00365 case 2:
00366 if((cfaPattern[2] == IFD::CFA_RED)
00367 && (cfaPattern[3] == IFD::CFA_GREEN))
00368 {
00369 cfa_pattern = OR_CFA_PATTERN_GBRG;
00370 }
00371 break;
00372 }
00373 break;
00374 case IFD::CFA_BLUE:
00375 switch(cfaPattern[1]) {
00376 case IFD::CFA_GREEN:
00377 if((cfaPattern[2] == IFD::CFA_GREEN)
00378 && (cfaPattern[3] == IFD::CFA_RED))
00379 {
00380 cfa_pattern = OR_CFA_PATTERN_BGGR;
00381 }
00382 break;
00383 }
00384 break;
00385 }
00386
00387 }
00388 return cfa_pattern;
00389 }
00390
00391 RawData::CfaPattern _convertNewCfaPattern(const IFDEntry::Ref & e)
00392 {
00393 RawData::CfaPattern cfa_pattern = OR_CFA_PATTERN_NONE;
00394 if(!e || (e->count() < 4)) {
00395 return cfa_pattern;
00396 }
00397
00398 uint16_t hdim = IFDTypeTrait<uint16_t>::get(*e, 0, true);
00399 uint16_t vdim = IFDTypeTrait<uint16_t>::get(*e, 1, true);
00400 if(hdim != 2 && vdim != 2) {
00401 cfa_pattern = OR_CFA_PATTERN_NON_RGB22;
00402 }
00403 else {
00404 std::vector<uint8_t> cfaPattern;
00405 cfaPattern.push_back(IFDTypeTrait<uint8_t>::get(*e, 4, true));
00406 cfaPattern.push_back(IFDTypeTrait<uint8_t>::get(*e, 5, true));
00407 cfaPattern.push_back(IFDTypeTrait<uint8_t>::get(*e, 6, true));
00408 cfaPattern.push_back(IFDTypeTrait<uint8_t>::get(*e, 7, true));
00409 cfa_pattern = _convertArrayToCfaPattern(cfaPattern);
00410 }
00411 return cfa_pattern;
00412 }
00413
00414
00416 RawData::CfaPattern _convertCfaPattern(const IFDEntry::Ref & e)
00417 {
00418 std::vector<uint8_t> cfaPattern;
00419 RawData::CfaPattern cfa_pattern = OR_CFA_PATTERN_NONE;
00420
00421 e->getArray(cfaPattern);
00422 if(!cfaPattern.empty()) {
00423 cfa_pattern = _convertArrayToCfaPattern(cfaPattern);
00424 }
00425 return cfa_pattern;
00426 }
00427
00433 static RawData::CfaPattern _getCfaPattern(const IFDDir::Ref & dir)
00434 {
00435 Trace(DEBUG1) << __FUNCTION__ << "\n";
00436 RawData::CfaPattern cfa_pattern = OR_CFA_PATTERN_NONE;
00437 try {
00438 IFDEntry::Ref e = dir->getEntry(IFD::EXIF_TAG_CFA_PATTERN);
00439 if(e) {
00440 cfa_pattern = _convertCfaPattern(e);
00441 }
00442 else {
00443 e = dir->getEntry(IFD::EXIF_TAG_NEW_CFA_PATTERN);
00444 if(e) {
00445 cfa_pattern = _convertNewCfaPattern(e);
00446 }
00447 }
00448 }
00449 catch(...)
00450 {
00451 Trace(ERROR) << "Exception in _getCfaPattern().\n";
00452 }
00453 return cfa_pattern;
00454 }
00455
00456 }
00457
00458 ::or_error IFDFile::_getRawDataFromDir(RawData & data, IFDDir::Ref & dir)
00459 {
00460 ::or_error ret = OR_ERROR_NONE;
00461
00462 uint16_t bpc = 0;
00463 uint32_t offset = 0;
00464 uint32_t byte_length = 0;
00465 bool got_it;
00466 uint32_t x, y;
00467 x = 0;
00468 y = 0;
00469
00470 got_it = dir->getValue(IFD::EXIF_TAG_BITS_PER_SAMPLE, bpc);
00471 if(!got_it) {
00472 Trace(ERROR) << "unable to guess Bits per sample\n";
00473 }
00474
00475 got_it = dir->getValue(IFD::EXIF_TAG_STRIP_OFFSETS, offset);
00476 if(got_it) {
00477 IFDEntry::Ref e = dir->getEntry(IFD::EXIF_TAG_STRIP_BYTE_COUNTS);
00478 if(e) {
00479 std::vector<uint32_t> counts;
00480 e->getArray(counts);
00481 Trace(DEBUG1) << "counting tiles\n";
00482 byte_length = std::accumulate(counts.begin(), counts.end(), 0);
00483 }
00484 else {
00485 Trace(DEBUG1) << "byte len not found\n";
00486 return OR_ERROR_NOT_FOUND;
00487 }
00488 }
00489 else {
00490
00491
00492 IFDEntry::Ref e = dir->getEntry(IFD::TIFF_TAG_TILE_OFFSETS);
00493 if(e) {
00494 std::vector<uint32_t> offsets;
00495 e->getArray(offsets);
00496 if(offsets.size() > 1) {
00497 offset = offsets[0];
00498 }
00499 else {
00500 Trace(DEBUG1) << "tile offsets empty\n";
00501 return OR_ERROR_NOT_FOUND;
00502 }
00503 }
00504 else {
00505 Trace(DEBUG1) << "tile offsets not found\n";
00506 return OR_ERROR_NOT_FOUND;
00507 }
00508 e = dir->getEntry(IFD::TIFF_TAG_TILE_BYTECOUNTS);
00509 if(e) {
00510 std::vector<uint32_t> counts;
00511 e->getArray(counts);
00512 Trace(DEBUG1) << "counting tiles\n";
00513 byte_length = std::accumulate(counts.begin(), counts.end(), 0);
00514 }
00515 else {
00516 Trace(DEBUG1) << "tile byte counts not found\n";
00517 return OR_ERROR_NOT_FOUND;
00518 }
00519 }
00520 got_it = dir->getIntegerValue(IFD::EXIF_TAG_IMAGE_WIDTH, x);
00521 if(!got_it) {
00522 Trace(DEBUG1) << "X not found\n";
00523 return OR_ERROR_NOT_FOUND;
00524 }
00525 got_it = dir->getIntegerValue(IFD::EXIF_TAG_IMAGE_LENGTH, y);
00526 if(!got_it) {
00527 Trace(DEBUG1) << "Y not found\n";
00528 return OR_ERROR_NOT_FOUND;
00529 }
00530
00531 uint32_t compression = 0;
00532 got_it = dir->getIntegerValue(IFD::EXIF_TAG_COMPRESSION, compression);
00533 if(!got_it)
00534 {
00535 Trace(DEBUG1) << "Compression type not found\n";
00536 }
00537 BitmapData::DataType data_type = OR_DATA_TYPE_NONE;
00538
00539 switch(compression)
00540 {
00541 case IFD::COMPRESS_NONE:
00542 data_type = OR_DATA_TYPE_CFA;
00543 break;
00544 case IFD::COMPRESS_NIKON_PACK:
00545 data_type = OR_DATA_TYPE_CFA;
00546 break;
00547 case IFD::COMPRESS_NIKON_QUANTIZED:
00548
00549
00550 if( !NEFFile::isCompressed(*m_container, offset) ) {
00551 compression = IFD::COMPRESS_NIKON_PACK;
00552 data_type = OR_DATA_TYPE_CFA;
00553
00554
00555
00556
00557 x += 6;
00558 break;
00559 }
00560 default:
00561 data_type = OR_DATA_TYPE_COMPRESSED_CFA;
00562 break;
00563 }
00564
00565 Trace(DEBUG1) << "RAW Compression is " << compression << "\n";
00566
00567 RawData::CfaPattern cfa_pattern = _getCfaPattern(dir);
00568 if(cfa_pattern == OR_CFA_PATTERN_NONE) {
00569
00570 if(!m_exifIfd) {
00571 m_exifIfd = _locateExifIfd();
00572 }
00573 cfa_pattern = _getCfaPattern(m_exifIfd);
00574 }
00575
00576
00577 if((bpc == 12) && (compression == 1)
00578 && (byte_length == (x * y * 2)))
00579 {
00580 bpc = 16;
00581 }
00582 if((bpc == 16) || (data_type == OR_DATA_TYPE_COMPRESSED_CFA)) {
00583 void *p = data.allocData(byte_length);
00584 size_t real_size = m_container->fetchData(p, offset,
00585 byte_length);
00586 if (real_size < byte_length) {
00587 Trace(WARNING) << "Size mismatch for data: ignoring.\n";
00588 }
00589 }
00590 else if((bpc == 12) || (bpc == 8)) {
00591 size_t fetched = 0;
00592 Unpack unpack(x, y, compression);
00593 const size_t blocksize = (bpc == 8 ? x : unpack.block_size());
00594 Trace(DEBUG1) << "Block size = " << blocksize << "\n";
00595 Trace(DEBUG1) << "dimensions (x, y) " << x << ", "
00596 << y << "\n";
00597 boost::scoped_array<uint8_t> block(new uint8_t[blocksize]);
00598 size_t outleft = x * y * 2;
00599 uint8_t * outdata = (uint8_t*)data.allocData(outleft);
00600 size_t got;
00601 Trace(DEBUG1) << "offset of RAW data = " << offset << "\n";
00602 do {
00603 got = m_container->fetchData (block.get(),
00604 offset, blocksize);
00605 fetched += got;
00606 offset += got;
00607 if(got) {
00608 if(bpc == 12) {
00609 size_t out = unpack.unpack_be12to16(outdata,
00610 outleft,
00611 block.get(),
00612 got);
00613 outdata += out;
00614 outleft -= out;
00615 }
00616 else {
00617
00618 std::copy(block.get(), block.get()+got,
00619 (uint16_t*)outdata);
00620 outdata += (got << 1);
00621 }
00622 }
00623 } while((got != 0) && (fetched < byte_length));
00624 }
00625 else {
00626 Trace(ERROR) << "Unsupported bpc " << bpc << "\n";
00627 }
00628 data.setCfaPattern(cfa_pattern);
00629 data.setDataType(data_type);
00630 data.setCompression(data_type == OR_DATA_TYPE_COMPRESSED_CFA
00631 ? compression : 1);
00632 data.setDimensions(x, y);
00633
00634 return ret;
00635 }
00636
00637 }
00638 }