/* * libopenraw - ciffcontainer.cpp * * Copyright (C) 2006-2017 Hubert Figuière * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #include #include "ciffcontainer.hpp" #include "trace.hpp" using namespace Debug; namespace OpenRaw { namespace Internals { namespace CIFF { bool ImageSpec::readFrom(off_t offset, CIFFContainer *container) { auto file = container->file(); file->seek(offset, SEEK_SET); auto result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } imageWidth = result_u32.unwrap(); result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } imageHeight = result_u32.unwrap(); result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } pixelAspectRatio = result_u32.unwrap(); auto result_32 = container->readInt32(file); if (result_32.empty()) { return false; } rotationAngle = result_32.unwrap(); result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } componentBitDepth = result_u32.unwrap(); result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } colorBitDepth = result_u32.unwrap(); result_u32 = container->readUInt32(file); if (result_u32.empty()) { return false; } colorBW = result_u32.unwrap(); return true; } int32_t ImageSpec::exifOrientation() const { int32_t orientation = 0; switch(rotationAngle) { case 0: orientation = 1; break; case 90: orientation = 6; break; case 180: orientation = 3; break; case 270: orientation = 8; break; } return orientation; } RecordEntry::RecordEntry() : typeCode(0), length(0), offset(0) { } bool RecordEntry::readFrom(CIFFContainer *container) { auto file = container->file(); auto result_16 = container->readUInt16(file); if (result_16.empty()) { return false; } typeCode = result_16.unwrap(); auto result_32 = container->readUInt32(file); if (result_32.empty()) { return false; } length = result_32.unwrap(); result_32 = container->readUInt32(file); if (result_32.empty()) { return false; } offset = result_32.unwrap(); return true; } size_t RecordEntry::fetchData(Heap* heap, void* buf, size_t size) const { return heap->container()->fetchData(buf, offset + heap->offset(), size); } Heap::Heap(off_t start, off_t length, CIFFContainer * _container) : m_start(start), m_length(length), m_container(_container), m_records() { LOGDBG2("Heap @ %ld length = %ld\n", start, m_length); } std::vector & Heap::records() { if (m_records.size() == 0) { _loadRecords(); } return m_records; } bool Heap::_loadRecords() { auto file = m_container->file(); file->seek(m_start + m_length - 4, SEEK_SET); auto result = m_container->readInt32(file); if (result.ok()) { int32_t record_offset = result.unwrap(); m_records.clear(); file->seek(m_start + record_offset, SEEK_SET); auto result16 = m_container->readInt16(file); if (result16.empty()) { LOGDBG1("read numRecords failed\n"); return false; } int16_t numRecords = result16.unwrap(); LOGDBG2("numRecords %d\n", numRecords); m_records.reserve(numRecords); for (int16_t i = 0; i < numRecords; i++) { m_records.push_back(RecordEntry()); m_records.back().readFrom(m_container); } return true; } return false; } #if 0 class OffsetTable { uint16_t numRecords;/* the number tblArray elements */ RecordEntry tblArray[1];/* Array of the record entries */ }; #endif bool HeapFileHeader::readFrom(CIFFContainer *container) { endian = RawContainer::ENDIAN_NULL; bool ret = false; auto file = container->file(); int s = file->read(byteOrder, 2); if (s == 2) { if((byteOrder[0] == 'I') && (byteOrder[1] == 'I')) { endian = RawContainer::ENDIAN_LITTLE; } else if((byteOrder[0] == 'M') && (byteOrder[1] == 'M')) { endian = RawContainer::ENDIAN_BIG; } container->setEndian(endian); auto result32 = container->readUInt32(file); if (result32.ok()) { headerLength = result32.unwrap(); ret = true; } if (ret) { ret = (file->read(type, 4) == 4); } if (ret) { ret = (file->read(subType, 4) == 4); } if (ret) { result32 = container->readUInt32(file); if (result32.ok()) { version = result32.unwrap(); ret = true; } } } return ret; } } CIFFContainer::CIFFContainer(const IO::Stream::Ptr &_file) : RawContainer(_file, 0), m_hdr(), m_heap(nullptr), m_hasImageSpec(false) { m_endian = _readHeader(); } CIFFContainer::~CIFFContainer() { } CIFF::Heap::Ref CIFFContainer::heap() { if (m_heap == nullptr) { _loadHeap(); } return m_heap; } bool CIFFContainer::_loadHeap() { bool ret = false; if (m_heap) { return false; } if (m_endian != ENDIAN_NULL) { off_t heapLength = m_file->filesize() - m_hdr.headerLength; LOGDBG1("heap len %ld\n", heapLength); m_heap = std::make_shared(m_hdr.headerLength, heapLength, this); ret = true; } else { LOGDBG1("Unknown endian\n"); } return ret; } RawContainer::EndianType CIFFContainer::_readHeader() { EndianType _endian = ENDIAN_NULL; m_hdr.readFrom(this); if ((::strncmp(m_hdr.type, "HEAP", 4) == 0) && (::strncmp(m_hdr.subType, "CCDR", 4) == 0)) { _endian = m_hdr.endian; } return _endian; } CIFF::Heap::Ref CIFFContainer::getImageProps() { if(!m_imageprops) { if(!heap()) { return CIFF::Heap::Ref(); } auto & records = m_heap->records(); // locate the properties auto iter = std::find_if(records.cbegin(), records.cend(), [](const CIFF::RecordEntry& e) { return e.isA(static_cast(CIFF::TAG_IMAGEPROPS)); }); if (iter == records.end()) { LOGERR("Couldn't find the image properties.\n"); return CIFF::Heap::Ref(); } m_imageprops = std::make_shared( iter->offset + m_heap->offset(), iter->length, this); } return m_imageprops; } const CIFF::ImageSpec * CIFFContainer::getImageSpec() { if(!m_hasImageSpec) { CIFF::Heap::Ref props = getImageProps(); if(!props) { return nullptr; } auto & propsRecs = props->records(); auto iter = std::find_if(propsRecs.cbegin(), propsRecs.cend(), [] (const CIFF::RecordEntry &e) { return e.isA(static_cast(CIFF::TAG_IMAGEINFO)); }); if (iter == propsRecs.end()) { LOGERR("Couldn't find the image info.\n"); return nullptr; } m_imagespec.readFrom(iter->offset + props->offset(), this); m_hasImageSpec = true; } return &m_imagespec; } const CIFF::Heap::Ref CIFFContainer::getCameraProps() { if(!m_cameraprops) { CIFF::Heap::Ref props = getImageProps(); if(!props) { return CIFF::Heap::Ref(); } auto & propsRecs = props->records(); auto iter = std::find_if(propsRecs.cbegin(), propsRecs.cend(), [] (const CIFF::RecordEntry & e) { return e.isA(static_cast(CIFF::TAG_CAMERAOBJECT)); }); if (iter == propsRecs.end()) { LOGERR("Couldn't find the camera props.\n"); return CIFF::Heap::Ref(); } m_cameraprops = std::make_shared( iter->offset + props->offset(), iter->length, this); } return m_cameraprops; } const CIFF::RecordEntry * CIFFContainer::getRawDataRecord() const { if(!m_heap) { return nullptr; } auto & records = m_heap->records(); // locate the RAW data auto iter = std::find_if(records.cbegin(), records.cend(), [] (const CIFF::RecordEntry &e) { return e.isA(static_cast(CIFF::TAG_RAWIMAGEDATA)); }); if (iter != records.end()) { return &(*iter); } return nullptr; } } } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)) indent-tabs-mode:nil fill-column:80 End: */