From 1e43142851f51d2750ea251f0eefb3a3cd66a462 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:29:31 +0200 Subject: [PATCH 01/27] rawspeed: Canon EOS R support --- data/cameras.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 41645599d..cf7de1d25 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2134,6 +2134,23 @@ + + Canon EOS R + + RED + GREEN + GREEN + BLUE + + + + + + + + + + Canon PowerShot Pro1 From 650b0086626444b311de7cf0ffc6d00d7119d724 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:30:06 +0200 Subject: [PATCH 02/27] rawspeed: Canon EOS M50 support --- data/cameras.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index cf7de1d25..82c37619e 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2151,6 +2151,24 @@ + + Canon EOS M50 + + RED + GREEN + GREEN + BLUE + + + + + + + + + + + Canon PowerShot Pro1 From f2ddf186d5b1de4855fc3facfeb00e583239a15c Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:42:11 +0200 Subject: [PATCH 03/27] Add ISO BMFF parser This patch is based on a draft from Roman Lebedev. --- fuzz/all-fuzzers.txt | 2 + fuzz/librawspeed/parsers/CMakeLists.txt | 1 + fuzz/librawspeed/parsers/main.cpp | 3 +- src/librawspeed/common/Range.h | 9 + src/librawspeed/parsers/CMakeLists.txt | 3 + src/librawspeed/parsers/IsoMParser.cpp | 56 ++ src/librawspeed/parsers/IsoMParser.h | 48 ++ src/librawspeed/parsers/IsoMParserException.h | 39 ++ src/librawspeed/parsers/RawParser.cpp | 10 + src/librawspeed/tiff/CMakeLists.txt | 2 + src/librawspeed/tiff/IsoMBox.cpp | 619 ++++++++++++++++++ src/librawspeed/tiff/IsoMBox.h | 412 ++++++++++++ test/librawspeed/common/RangeTest.h | 59 ++ test/librawspeed/test/ExceptionsTest.cpp | 25 +- 14 files changed, 1277 insertions(+), 11 deletions(-) create mode 100644 src/librawspeed/parsers/IsoMParser.cpp create mode 100644 src/librawspeed/parsers/IsoMParser.h create mode 100644 src/librawspeed/parsers/IsoMParserException.h create mode 100644 src/librawspeed/tiff/IsoMBox.cpp create mode 100644 src/librawspeed/tiff/IsoMBox.h diff --git a/fuzz/all-fuzzers.txt b/fuzz/all-fuzzers.txt index a83873089..254ac9cea 100644 --- a/fuzz/all-fuzzers.txt +++ b/fuzz/all-fuzzers.txt @@ -67,6 +67,8 @@ HuffmanTableVectorFuzzer-BitPumpMSB-FullDecode HuffmanTableVectorFuzzer-BitPumpMSB-NoFullDecode HuffmanTableVectorFuzzer-BitPumpMSB32-FullDecode HuffmanTableVectorFuzzer-BitPumpMSB32-NoFullDecode +IsoMParserFuzzer-GetDecoder +IsoMParserFuzzer-GetDecoder-Decode KodakDecompressorFuzzer LJpegDecompressorFuzzer NikonDecompressorFuzzer diff --git a/fuzz/librawspeed/parsers/CMakeLists.txt b/fuzz/librawspeed/parsers/CMakeLists.txt index 4ffae2a48..ac335a9dc 100644 --- a/fuzz/librawspeed/parsers/CMakeLists.txt +++ b/fuzz/librawspeed/parsers/CMakeLists.txt @@ -19,6 +19,7 @@ endfunction() set(PARSERS "Ciff" "Fiff" + "IsoM" "Raw" "Tiff" ) diff --git a/fuzz/librawspeed/parsers/main.cpp b/fuzz/librawspeed/parsers/main.cpp index a44fc5938..6db139411 100644 --- a/fuzz/librawspeed/parsers/main.cpp +++ b/fuzz/librawspeed/parsers/main.cpp @@ -1,7 +1,7 @@ /* RawSpeed - RAW file decoder. - Copyright (C) 2017 Roman Lebedev + Copyright (C) 2017-2018 Roman Lebedev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -34,6 +34,7 @@ #include "io/IOException.h" // for IOException #include "parsers/CiffParser.h" // IWYU pragma: keep #include "parsers/FiffParser.h" // IWYU pragma: keep +#include "parsers/IsoMParser.h" // IWYU pragma: keep #include "parsers/RawParser.h" // IWYU pragma: keep #include "parsers/RawParserException.h" // for RawParserException #include "parsers/TiffParser.h" // IWYU pragma: keep diff --git a/src/librawspeed/common/Range.h b/src/librawspeed/common/Range.h index 58208a0fe..5296001dc 100644 --- a/src/librawspeed/common/Range.h +++ b/src/librawspeed/common/Range.h @@ -55,6 +55,15 @@ constexpr bool __attribute__((const)) RangeContains(const Tr& r, Tv pos) { return r.end() > pos; } +template +inline constexpr bool __attribute__((const)) +RangesAreNested(const Tro& outer, const Tri& inner) { + // 1. The inner range must not begin before the outer range, and + // 2. The outer range must not end before the inner range + // Same begin and/or end is ok. + return outer.begin() <= inner.begin() && inner.end() <= outer.end(); +} + template constexpr bool __attribute__((const)) RangesOverlap(const T& lhs, const T& rhs) { diff --git a/src/librawspeed/parsers/CMakeLists.txt b/src/librawspeed/parsers/CMakeLists.txt index 453c2dc11..5871280fc 100644 --- a/src/librawspeed/parsers/CMakeLists.txt +++ b/src/librawspeed/parsers/CMakeLists.txt @@ -5,6 +5,9 @@ FILE(GLOB SOURCES "FiffParser.cpp" "FiffParser.h" "FiffParserException.h" + "IsoMParser.cpp" + "IsoMParser.h" + "IsoMParserException.h" "RawParser.cpp" "RawParser.h" "RawParserException.h" diff --git a/src/librawspeed/parsers/IsoMParser.cpp b/src/librawspeed/parsers/IsoMParser.cpp new file mode 100644 index 000000000..9e7b0b349 --- /dev/null +++ b/src/librawspeed/parsers/IsoMParser.cpp @@ -0,0 +1,56 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "parsers/IsoMParser.h" // For IsoMParser +#include "decoders/RawDecoder.h" // for RawDecoder +#include "io/ByteStream.h" // for ByteStream +#include "io/Endianness.h" // for Endianness::big +#include "parsers/IsoMParserException.h" // for ThrowIPE + +namespace rawspeed { + +IsoMParser::IsoMParser(const Buffer* inputData) : RawParser(inputData) {} + +void IsoMParser::parseData() { + ByteStream bs(DataBuffer(*mInput, Endianness::unknown)); + + // The 'ISO base media file format' is big-endian. + bs.setByteOrder(Endianness::big); + + // *Everything* is the box. + auto box = std::make_unique(&bs); + // It should have consumed all of the buffer. + assert(bs.getRemainSize() == 0); + + box->parse(); + + rootBox = std::move(box); +} + +std::unique_ptr IsoMParser::getDecoder(const CameraMetaData* meta) { + if (!rootBox) + parseData(); + + + ThrowIPE("No decoder found. Sorry."); +} + +} // namespace rawspeed diff --git a/src/librawspeed/parsers/IsoMParser.h b/src/librawspeed/parsers/IsoMParser.h new file mode 100644 index 000000000..08d1c2ba5 --- /dev/null +++ b/src/librawspeed/parsers/IsoMParser.h @@ -0,0 +1,48 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "parsers/RawParser.h" // for RawParser +#include "tiff/IsoMBox.h" // for IsoMRootBox +#include // for unique_ptr + +namespace rawspeed { + +class Buffer; + +class RawDecoder; + +class CameraMetaData; + +class IsoMParser final : public RawParser { + std::unique_ptr rootBox; + + void parseData(); + +public: + explicit IsoMParser(const Buffer* input); + + std::unique_ptr + getDecoder(const CameraMetaData* meta = nullptr) override; +}; + +} // namespace rawspeed diff --git a/src/librawspeed/parsers/IsoMParserException.h b/src/librawspeed/parsers/IsoMParserException.h new file mode 100644 index 000000000..4a7420678 --- /dev/null +++ b/src/librawspeed/parsers/IsoMParserException.h @@ -0,0 +1,39 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "common/RawspeedException.h" // for ThrowExceptionHelper +#include "parsers/RawParserException.h" // for ThrowRPE, RawParserException +#include + +namespace rawspeed { + +class IsoMParserException final : public RawParserException { +public: + explicit IsoMParserException(const std::string& msg) + : RawParserException(msg.c_str()) {} + explicit IsoMParserException(const char* msg) : RawParserException(msg) {} +}; + +#define ThrowIPE(...) \ + ThrowExceptionHelper(rawspeed::IsoMParserException, __VA_ARGS__) + +} // namespace rawspeed diff --git a/src/librawspeed/parsers/RawParser.cpp b/src/librawspeed/parsers/RawParser.cpp index dbc294b35..e227f64d5 100644 --- a/src/librawspeed/parsers/RawParser.cpp +++ b/src/librawspeed/parsers/RawParser.cpp @@ -3,6 +3,7 @@ Copyright (C) 2009-2014 Klaus Post Copyright (C) 2017 Axel Waggershauser + Copyright (C) 2018 Roman Lebedev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -31,6 +32,8 @@ #include "parsers/CiffParserException.h" // for CiffParserException #include "parsers/FiffParser.h" // for FiffParser #include "parsers/FiffParserException.h" // for FiffParserException +#include "parsers/IsoMParser.h" // for IsoMParser +#include "parsers/IsoMParserException.h" // for IsoMParserException #include "parsers/TiffParser.h" // for TiffParser #include "parsers/TiffParserException.h" // for TiffParserException @@ -77,6 +80,13 @@ std::unique_ptr RawParser::getDecoder(const CameraMetaData* meta) { } catch (const CiffParserException&) { } + // ISO Media + try { + IsoMParser p(mInput); + return p.getDecoder(meta); + } catch (IsoMParserException&) { + } + // Detect camera on filesize (CHDK). if (meta != nullptr && meta->hasChdkCamera(mInput.getSize())) { const Camera* c = meta->getChdkCamera(mInput.getSize()); diff --git a/src/librawspeed/tiff/CMakeLists.txt b/src/librawspeed/tiff/CMakeLists.txt index 6c2f6d417..1015fb0ba 100644 --- a/src/librawspeed/tiff/CMakeLists.txt +++ b/src/librawspeed/tiff/CMakeLists.txt @@ -9,6 +9,8 @@ FILE(GLOB SOURCES "CiffIFD.cpp" "CiffIFD.h" "CiffTag.h" + "IsoMBox.cpp" + "IsoMBox.h" ) target_sources(rawspeed PRIVATE diff --git a/src/librawspeed/tiff/IsoMBox.cpp b/src/librawspeed/tiff/IsoMBox.cpp new file mode 100644 index 000000000..286904999 --- /dev/null +++ b/src/librawspeed/tiff/IsoMBox.cpp @@ -0,0 +1,619 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "tiff/IsoMBox.h" +#include "common/NORangesSet.h" // for NORangesSet +#include "io/Buffer.h" // for Buffer::size_type +#include "parsers/IsoMParserException.h" // for ThrowIPE, IsoMParserException +#include // for find, generate_n +#include // for assert +#include // for memcmp +#include // for numeric_limits +#include // for pair + +namespace rawspeed { + + +// The ODR-definitions + +const FourCharStr IsoMBoxTypes::ftyp; +const FourCharStr IsoMBoxTypes::co64; +const FourCharStr IsoMBoxTypes::stsz; +const FourCharStr IsoMBoxTypes::stsc; +const FourCharStr IsoMBoxTypes::stsd; +const FourCharStr IsoMBoxTypes::stbl; +const FourCharStr IsoMBoxTypes::url; +const FourCharStr IsoMBoxTypes::dref; +const FourCharStr IsoMBoxTypes::dinf; +const FourCharStr IsoMBoxTypes::minf; +const FourCharStr IsoMBoxTypes::mdia; +const FourCharStr IsoMBoxTypes::trak; +const FourCharStr IsoMBoxTypes::moov; +const FourCharStr IsoMBoxTypes::mdat; + +const FourCharStr IsoMBoxTypes::uuid; + + +// Base-level lexing/parsing. + +AbstractIsoMBox::AbstractIsoMBox(ByteStream* bs) { + const auto origPos = bs->getPosition(); + + // This is the size of this whole box, starting from the origPos. + const auto boxSize = bs->getU32(); + + boxType = FourCharStr(bs->getU32()); + + if (boxSize == 0) { + bs->setPosition(origPos); + // Rest is the whole box. + data = bs->getStream(bs->getRemainSize()); + } else if (boxSize != 1) { + bs->setPosition(origPos); + assert(bs->getRemainSize() >= boxSize); + // The good case, this is the size of the box. + data = bs->getStream(boxSize); + } else { + // Meh, the ugly case :/ + assert(boxSize == 1); + const auto largeSize = bs->get(); + + // The rawspeed::Buffer is 32-bit, so even we somehow get here with valid + // more-than 32-bit-sized box, we can't do anything about it. + // We have to handle this explicitly because else 64-bit will get truncated + // to 32-bit without us noticing, and nothing good will happen next. + if (largeSize > std::numeric_limits::max()) + ThrowIPE("IsoM Box uses largesize which does not fit into 32-bits"); + + bs->setPosition(origPos); + assert(bs->getRemainSize() >= largeSize); + data = bs->getStream(static_cast(largeSize)); + data.skipBytes(8); // skip the largeSize, + } + + data.skipBytes(8); // already read those before in any case + + if (FourCharStr({'u', 'u', 'i', 'd'}) == boxType) { + const auto userTypeBs = data.getBuffer(16); + std::copy(userTypeBs.begin(), userTypeBs.end(), userType.begin()); + } +} + +void IsoMContainer::lexBox() { boxes.emplace_back(&cData); } + +void IsoMContainer::lexSubBoxes() { + // A box is a series of boxes. + while (cData.getRemainSize() > 0) + lexBox(); + // There is nothing else left after boxes. + assert(cData.getRemainSize() == 0); +} + +IsoMContainer::IsoMContainer(ByteStream* bs) + : cData(bs->getStream(bs->getRemainSize())) { + lexSubBoxes(); + // There is nothing else left after boxes. + assert(cData.getRemainSize() == 0); +} + +const AbstractIsoMBox& +IsoMContainer::getBox(const AbstractIsoMBox::UuidType& uuid) const { + for(const auto& box : boxes) { + if(uuid == box.userType) { + return box; + } + } + ThrowIPE("Requested box UUID not found"); +} + + + +// FileType box parsing. + +const std::array IsoMFileTypeBox::supportedBrands; +IsoMFileTypeBox::operator bool() const { + if (std::find(supportedBrands.begin(), supportedBrands.end(), majorBrand) == + supportedBrands.end()) + ThrowIPE("Unsupported major brand: %s", majorBrand.str().c_str()); + + bool isComp = false; + for (const auto& compatibleBrand : compatibleBrands) { + isComp = std::find(supportedBrands.begin(), supportedBrands.end(), + compatibleBrand) != supportedBrands.end(); + if (isComp) + break; + } + if (!isComp) + ThrowIPE("No intersection between compatibleBrands and supported brands"); + + return true; // Supported! +} + +IsoMFileTypeBox::IsoMFileTypeBox(const AbstractIsoMBox& base) : BaseBox(base) { + majorBrand = FourCharStr(data.getU32()); + minorVersion = data.getU32(); + while (data.getRemainSize() > 0) + compatibleBrands.emplace_back(data.getU32()); + // There is nothing else left. + assert(data.getRemainSize() == 0); + + // Validate. + operator bool(); +} + +// SampleDescription box parsing. + +IsoMSampleDescriptionBox::SampleEntry::SampleEntry(ByteStream* bs) + : AbstractIsoMBox(bs) { + for (auto& c : reserved) + c = data.getByte(); + dataReferenceIndex = data.getU16(); +} + +IsoMSampleDescriptionBox::operator bool() const { + if (dscs.size() != 1) + ThrowIPE("Unexpected entry count: %zu", dscs.size()); + + for (const auto& dsc : dscs) { + if (dsc.dataReferenceIndex != 1) + ThrowIPE("Unexpected data reference index: %u", dsc.dataReferenceIndex); + } + + return true; // Supported! +} + +IsoMSampleDescriptionBox::IsoMSampleDescriptionBox(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + const auto entryCount = data.getU32(); + + // Can't check/reserve entryCount. + std::generate_n(std::back_inserter(dscs), entryCount, + [this]() { return SampleEntry(&data); }); + assert(dscs.size() == entryCount); + + // Validate. + operator bool(); +} + + + +IsoMSampleToChunkBox::operator bool() const { + if (dscs.size() != 1) + ThrowIPE("Unexpected entry count: %zu", dscs.size()); + + for (const auto& dsc : dscs) { + if (dsc.firstChunk != 1) + ThrowIPE("Unexpected first chunk: %u", dsc.firstChunk); + if (dsc.samplesPerChunk != 1) + ThrowIPE("Unexpected samples per chunk: %u", dsc.samplesPerChunk); + if (dsc.sampleDescriptionIndex != 1) { + ThrowIPE("Unexpected sample description index: %u", + dsc.sampleDescriptionIndex); + } + } + + return true; // Supported! +} + +IsoMSampleToChunkBox::IsoMSampleToChunkBox(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + const auto entryCount = data.getU32(); + + data.check(entryCount, 3 * 4); + dscs.reserve(entryCount); + std::generate_n(std::back_inserter(dscs), entryCount, [this]() { + Dsc d; + d.firstChunk = data.getU32(); + d.samplesPerChunk = data.getU32(); + d.sampleDescriptionIndex = data.getU32(); + return d; + }); + assert(dscs.size() == entryCount); + + // Validate. + operator bool(); +} + +// SampleSize box parsing. + +IsoMSampleSizeBox::operator bool() const { + if (chunkSizes.empty()) + ThrowIPE("No chunk sizes found"); + + // The actual validation of these values will happen + // during parsing of moov box. + + return true; // Supported! +} + +IsoMSampleSizeBox::IsoMSampleSizeBox(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + const auto sampleSize = data.getU32(); + const auto sampleCount = data.getU32(); + + if (sampleSize == 0) { + for(uint32_t i = 0; i < sampleCount; ++i) { + chunkSizes.emplace_back(data.getU32()); + } + } else { + // It's the only sample size and it is stored + // in the sampleSize directly. + chunkSizes.emplace_back(sampleSize); + } + + // Validate. + operator bool(); +} + +// ChunkLargeOffset box parsing. + +IsoMChunkLargeOffsetBox::operator bool() const { + if (chunkOffsets.empty()) + ThrowIPE("No chunk offsets found"); + + // The actual validation of these values will happen + // during parsing of moov box. + + return true; // Supported! +} + +IsoMChunkLargeOffsetBox::IsoMChunkLargeOffsetBox(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + const auto entryCount = data.getU32(); + data.check(entryCount, 8); + + if (entryCount != 1) + ThrowIPE("Don't know how to handle co64 box with %u entries", entryCount); + + chunkOffsets.reserve(entryCount); + std::generate_n( + std::back_inserter(chunkOffsets), entryCount, + [this]() -> Buffer::size_type { + const auto largeSize = data.get(); + + // The rawspeed::Buffer is 32-bit, so even we somehow get here with + // valid more-than 32-bit-sized box, we can't do anything about it. We + // have to handle this explicitly because else 64-bit will get truncated + // to 32-bit without us noticing, and nothing good will happen next. + if (largeSize > std::numeric_limits::max()) + ThrowIPE("IsoM Box uses largesize which does not fit into 32-bits"); + return static_cast(largeSize); + }); + assert(chunkOffsets.size() == entryCount); + // Could still have some padding bytes left, but don't care. + + // Validate. + operator bool(); +} + +// Sample Table box handling. + +void IsoMSampleTableBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMSampleDescriptionBox::BoxType == box.boxType) { + if (stsd) + ThrowIPE("duplicate stsd box found."); + stsd = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMSampleToChunkBox::BoxType == box.boxType) { + if (stsc) + ThrowIPE("duplicate stsc box found."); + stsc = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMSampleSizeBox::BoxType == box.boxType) { + if (stsz) + ThrowIPE("duplicate stsz box found."); + stsz = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMChunkLargeOffsetBox::BoxType == box.boxType) { + if (co64) + ThrowIPE("duplicate co64 box found."); + co64 = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMSampleTableBox::operator bool() const { + if (!stsd) + ThrowIPE("no stsd box found."); + if (!stsc) + ThrowIPE("no stsc box found."); + if (!stsz) + ThrowIPE("no stsz box found."); + if (!co64) + ThrowIPE("no co64 box found."); + + if (stsz->chunkSizes.size() != co64->chunkOffsets.size()) + ThrowIPE("Mismatch in chunk offset and size count."); + if (stsc->dscs.size() != co64->chunkOffsets.size()) + ThrowIPE("Mismatch in stsc entry count and chunk offset count."); + if (stsc->dscs.size() != stsd->dscs.size()) + ThrowIPE("Mismatch in stsc entry count and stsd entry count."); + + return true; // OK! +} + +// DataReference box parsing. + +IsoMDataReferenceBox::IsoMDataEntryUrlBox::operator bool() const { + if (flags != static_cast(Flags::SelfContained)) + ThrowIPE("Unexpected flags: %u; entry is not self-contained", flags); + + return true; // Supported! +} + +IsoMDataReferenceBox::IsoMDataEntryUrlBox::IsoMDataEntryUrlBox( + const AbstractIsoMBox& base) + : IsoMFullBox(base) { + // Validate. + operator bool(); +} + +IsoMDataReferenceBox::operator bool() const { + if (entries.size() != 1) + ThrowIPE("Unexpected entry count: %zu", entries.size()); + + return true; // Supported! +} + +IsoMDataReferenceBox::IsoMDataReferenceBox(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + const auto entryCount = data.getU32(); + + for (auto entry = 1U; entry <= entryCount; entry++) { + auto box = AbstractIsoMBox(&data); + if (IsoMDataEntryUrlBox::BoxType == box.boxType) { + entries.emplace_back(box); + entries.back().parse(); + } + } + + // Validate. + operator bool(); +} + +// Data Information box handling. + +void IsoMDataInformationBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMDataReferenceBox::BoxType == box.boxType) { + if (dref) + ThrowIPE("duplicate dref box found."); + dref = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMDataInformationBox::operator bool() const { + if (!dref) + ThrowIPE("no dref box found."); + + return true; // OK! +} + +// Media Information box handling. + +void IsoMMediaInformationBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMDataInformationBox::BoxType == box.boxType) { + if (dinf) + ThrowIPE("duplicate dinf box found."); + dinf = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMSampleTableBox::BoxType == box.boxType) { + if (stbl) + ThrowIPE("duplicate stbl box found."); + stbl = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMMediaInformationBox::operator bool() const { + if (!dinf) + ThrowIPE("no dinf box found."); + if (!stbl) + ThrowIPE("no stbl box found."); + + if (dinf->dref->entries.size() != stbl->stsd->dscs.size()) + ThrowIPE("Mismatch in dref entry count and stsd entry count."); + + return true; // OK! +} + +// Media box handling. + +void IsoMMediaBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMMediaInformationBox::BoxType == box.boxType) { + if (minf) + ThrowIPE("duplicate minf box found."); + minf = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMMediaBox::operator bool() const { + if (!minf) + ThrowIPE("no minf box found."); + + return true; // OK! +} + +// Track box handling. + +void IsoMTrackBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMMediaBox::BoxType == box.boxType) { + if (mdia) + ThrowIPE("duplicate mdia box found."); + mdia = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMTrackBox::operator bool() const { + if (!mdia) + ThrowIPE("no mdia box found."); + + return true; // OK! +} + +// Movie box handling. + +void IsoMMovieBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMTrackBox::BoxType == box.boxType) { + tracks.emplace_back(box); + tracks.back().parse(); + return; + } +} + +IsoMMovieBox::operator bool() const { + if (tracks.empty()) + ThrowIPE("no track boxes found."); + + + return true; // OK! +} + +// Media Data box handling. + +IsoMMediaDataBox::operator bool() const { + if (chunks.empty()) + ThrowIPE("no chunks found."); + + return true; // OK! +} + +void IsoMMediaDataBox::parse(IsoMRootBox* root) { + assert(root); + + // Visit each sample (offset+size pair) in each track. + auto forEachChunk = [root](auto fun) { + for (const auto& track : root->moov()->tracks) { + auto& stbl = track.mdia->minf->stbl; + const auto& stsz = stbl->stsz; + const auto& co64 = stbl->co64; + assert(stsz->chunkSizes.size() == co64->chunkOffsets.size()); + const auto numChunks = stsz->chunkSizes.size(); + stbl->chunks.reserve(numChunks); + for (auto chunk = 0U; chunk < numChunks; chunk++) { + fun(co64->chunkOffsets[chunk], stsz->chunkSizes[chunk], + std::back_inserter(stbl->chunks)); + } + } + }; + + const unsigned numChunks = [&forEachChunk]() -> unsigned { + unsigned i = 0; + // Just count them all. + forEachChunk( + [&i](Buffer::size_type, Buffer::size_type, + std::back_insert_iterator>) { + i++; + }); + return i; + }(); + chunks.reserve(numChunks); + + // chunk legality checks + NORangesSet clc; + + forEachChunk( + [root, &clc, this]( + Buffer::size_type offset, Buffer::size_type count, + std::back_insert_iterator> stblChunk) { + // The offset is global to the file (eww, ugh!). + const auto chunk = root->cData.getSubStream(offset, count); + // Is it actually in the mdat box? + if (!RangesAreNested(mData, chunk)) + ThrowIPE("Chunk is not in the mdat box."); + // Does it overlap with any previous chunk? + if (!clc.emplace(chunk).second) + ThrowIPE("Two chunks overlap."); + // OK! + chunks.emplace_back(chunk); + stblChunk = &(chunks.back()); + }); + assert(chunks.size() == numChunks); + + // Validate. + operator bool(); +} + +// The handling of the root container. + +void IsoMRootBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMFileTypeBox::BoxType == box.boxType) { + if (ftypBox) + ThrowIPE("duplicate ftyp box found."); + ftypBox = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMMovieBox::BoxType == box.boxType) { + if (!ftypBox) + ThrowIPE("no ftyp box found yet."); + if (moovBox) + ThrowIPE("duplicate moov box found."); + moovBox = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMMediaDataBox::BoxType == box.boxType) { + if (!moovBox) + ThrowIPE("no moov box found yet."); + if (mdatBox) + ThrowIPE("duplicate mdat box found."); + mdatBox = AbstractIsoMBox::ParseBox(box, this); + return; + } +} + +IsoMRootBox::operator bool() const { + if (!ftypBox) + ThrowIPE("ftyp box not found."); + if (!moovBox) + ThrowIPE("moov box not found."); + if (!mdatBox) + ThrowIPE("mdat box not found."); + + return true; // OK! +} + +const std::unique_ptr& IsoMRootBox::ftyp() const { + if(ftypBox) + return ftypBox; + else + ThrowIPE("ftyp box not available"); +} +const std::unique_ptr& IsoMRootBox::moov() const { + if(moovBox) + return moovBox; + else + ThrowIPE("moov box not available"); +} +const std::unique_ptr& IsoMRootBox::mdat() const { + if(mdatBox) + return mdatBox; + else + ThrowIPE("mdat box not available"); +} + + +} // namespace rawspeed diff --git a/src/librawspeed/tiff/IsoMBox.h b/src/librawspeed/tiff/IsoMBox.h new file mode 100644 index 000000000..3cde4361f --- /dev/null +++ b/src/librawspeed/tiff/IsoMBox.h @@ -0,0 +1,412 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "common/Common.h" // for uint32 +#include "io/ByteStream.h" // for ByteStream +#include "io/Endianness.h" // for getBE +#include "parsers/IsoMParserException.h" // for ThrowIPE +#include // for array +#include // for assert +#include // for memcpy, strncpy +#include // for initializer_list +#include // for unique_ptr +#include // for string +#include // for pair +#include // for vector + +namespace rawspeed { + +struct FourCharStr final { + using value_type = uint32_t; + static constexpr auto num_chars = sizeof(value_type) / sizeof(char); + static_assert(num_chars == 4, "wanted 4 chars specifically"); + + std::array data{}; + + FourCharStr() = default; + + explicit constexpr FourCharStr(decltype(data) data_) : data(data_) {} + + explicit FourCharStr(value_type data_) { + // Turn the unsigned integer into a 'string' + data_ = getBE(&data_); + std::memcpy(data.data(), &data_, num_chars); + } + + explicit operator std::string() const { + return std::string(data.begin(), data.end()); + } + + std::string str() const { return static_cast(*this); } +}; +inline bool operator==(const FourCharStr& lhs, const FourCharStr& rhs) { + return lhs.data == rhs.data; +} +inline bool operator!=(const FourCharStr& lhs, const FourCharStr& rhs) { + return !operator==(lhs, rhs); +} + +// The base foundation of the ISO Base Media File Format. + +class IsoMRootBox; +struct IsoMMediaDataBox; + +// The most basic box. +struct AbstractIsoMBox { + typedef std::array UuidType; + + ByteStream data; + + FourCharStr boxType; + UuidType userType{}; // when boxType == "uuid" + + AbstractIsoMBox() = default; + + explicit AbstractIsoMBox(ByteStream* bs); + + template + static std::unique_ptr ParseBox(const AbstractIsoMBox& base, + IsoMRootBox* root = nullptr) { + auto box = std::make_unique(base); + box->parse(root); + return box; + } +}; + +struct IsoMBoxTypes final { + static constexpr FourCharStr ftyp = FourCharStr({'f', 't', 'y', 'p'}); + static constexpr FourCharStr co64 = FourCharStr({'c', 'o', '6', '4'}); + static constexpr FourCharStr stsz = FourCharStr({'s', 't', 's', 'z'}); + static constexpr FourCharStr stsc = FourCharStr({'s', 't', 's', 'c'}); + static constexpr FourCharStr stsd = FourCharStr({'s', 't', 's', 'd'}); + static constexpr FourCharStr stbl = FourCharStr({'s', 't', 'b', 'l'}); + static constexpr FourCharStr url = FourCharStr({'u', 'r', 'l', ' '}); + static constexpr FourCharStr dref = FourCharStr({'d', 'r', 'e', 'f'}); + static constexpr FourCharStr dinf = FourCharStr({'d', 'i', 'n', 'f'}); + static constexpr FourCharStr minf = FourCharStr({'m', 'i', 'n', 'f'}); + static constexpr FourCharStr mdia = FourCharStr({'m', 'd', 'i', 'a'}); + static constexpr FourCharStr trak = FourCharStr({'t', 'r', 'a', 'k'}); + static constexpr FourCharStr moov = FourCharStr({'m', 'o', 'o', 'v'}); + static constexpr FourCharStr mdat = FourCharStr({'m', 'd', 'a', 't'}); + + static constexpr FourCharStr uuid = FourCharStr({'u', 'u', 'i', 'd'}); +}; + +// The basic container. +class IsoMContainer { + void lexBox(); + void lexSubBoxes(); + +protected: + ByteStream cData; + + // These are specific for each container, and must be implemented. + virtual void parseBox(const AbstractIsoMBox& box) = 0; + virtual explicit operator bool() const = 0; + + friend struct IsoMMediaDataBox; // needs access to cData + +public: + std::vector boxes; + + IsoMContainer() = default; + virtual ~IsoMContainer() = default; + + explicit IsoMContainer(ByteStream* bs); + + const AbstractIsoMBox& getBox(const AbstractIsoMBox::UuidType& uuid) const; + + // !!! DO NOT CALL FROM CONSTRUCTOR !!! + void parse(IsoMRootBox* root = nullptr) { + for (const auto& box : boxes) + parseBox(box); + operator bool(); + } +}; + +// No further boxes shall be constructible from ByteStream! + +// The box that knows what it is. +template +struct IsoMBox : public AbstractIsoMBox { + using BaseBox = IsoMBox; + + static constexpr const FourCharStr /* IsoMBoxTypes::* */& BoxType = type; + + IsoMBox() = default; + + explicit IsoMBox(const AbstractIsoMBox& base) : AbstractIsoMBox(base) { + assert(BoxType == boxType); + if (BoxType != boxType) { + ThrowIPE("Unexpected box type, got: '%s', expected: '%s'", + BoxType.str().c_str(), boxType.str().c_str()); + } + } +}; + +template +struct IsoMFullBox : public IsoMBox { + using BaseBox = IsoMBox; + + uint8_t version; + uint32_t flags : 24; + + uint8_t expectedVersion() const { return 0; } + + IsoMFullBox() = default; + virtual ~IsoMFullBox() = default; + + explicit IsoMFullBox(const AbstractIsoMBox& base) : IsoMBox(base) { + // Highest 8 bits - version + version = BaseBox::data.peekByte(); + // The rest, low 24 bits - flags + flags = BaseBox::data.getU32() & ((1U << 24U) - 1U); + + if (expectedVersion() != version) + ThrowIPE("Unexpected version of FullBox - %u", expectedVersion()); + } + + void parse(IsoMRootBox* root = nullptr) {} +}; + +template +class IsoMContainerBox : public IsoMBox, public IsoMContainer { +public: + using BaseBox = IsoMBox; + using BaseContainer = IsoMContainerBox; + + IsoMContainerBox() = default; + + explicit IsoMContainerBox(const AbstractIsoMBox& base) + : IsoMBox(base), IsoMContainer(&(BaseBox::data)) {} +}; + + +template +class IsoMContainerFullBox : public IsoMFullBox, public IsoMContainer { +public: + using BaseBox = IsoMFullBox; + using BaseContainer = IsoMContainerBox; + + IsoMContainerFullBox() = default; + + explicit IsoMContainerFullBox(const AbstractIsoMBox& base) + : IsoMFullBox(base), IsoMContainer(&(BaseBox::data)) {} +}; + +// The actual boxes + +struct IsoMFileTypeBox final : public IsoMBox { + static constexpr std::array supportedBrands = { + FourCharStr({'c', 'r', 'x', ' '})}; + + FourCharStr majorBrand; + uint32_t minorVersion; + std::vector compatibleBrands; + + explicit IsoMFileTypeBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; + + void parse(IsoMRootBox* root = nullptr) {} +}; + +struct IsoMSampleDescriptionBox final : public IsoMFullBox { + struct SampleEntry final : public AbstractIsoMBox { + std::array reserved; + uint16_t dataReferenceIndex; + + SampleEntry() = default; + + explicit SampleEntry(ByteStream* bs); + }; + + std::vector dscs; + + explicit IsoMSampleDescriptionBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; +}; + +struct IsoMSampleToChunkBox final : public IsoMFullBox { + struct Dsc final { + uint32_t firstChunk; + uint32_t samplesPerChunk; + uint32_t sampleDescriptionIndex; + }; + + std::vector dscs; + + explicit IsoMSampleToChunkBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; +}; + +struct IsoMSampleSizeBox final : public IsoMFullBox { + std::vector chunkSizes; + + explicit IsoMSampleSizeBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; +}; + +struct IsoMChunkLargeOffsetBox final : public IsoMFullBox { + std::vector chunkOffsets; + + explicit IsoMChunkLargeOffsetBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; +}; + +class IsoMSampleTableBox final : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr stsd; + std::unique_ptr stsc; + std::unique_ptr stsz; + std::unique_ptr co64; + + // will be filed by IsoMMediaDataBox::parse() + std::vector chunks; + + explicit IsoMSampleTableBox(const AbstractIsoMBox& base) + : IsoMContainerBox(base) {} +}; + +struct IsoMDataReferenceBox final : public IsoMFullBox { + struct IsoMDataEntryUrlBox final : public IsoMFullBox { + enum class Flags : decltype(IsoMFullBox::flags) { + SelfContained = 0b1, + }; + + explicit IsoMDataEntryUrlBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; + }; + + std::vector entries; + + explicit IsoMDataReferenceBox(const AbstractIsoMBox& base); + + // Validate. + explicit operator bool() const; +}; + +class IsoMDataInformationBox final + : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr dref; + + explicit IsoMDataInformationBox(const AbstractIsoMBox& base) + : IsoMContainerBox(base) {} +}; + +class IsoMMediaInformationBox final + : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr dinf; + std::unique_ptr stbl; + + explicit IsoMMediaInformationBox(const AbstractIsoMBox& base) + : IsoMContainerBox(base) {} +}; + +class IsoMMediaBox final : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr minf; + + explicit IsoMMediaBox(const AbstractIsoMBox& base) : IsoMContainerBox(base) {} +}; + +class IsoMTrackBox final : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr mdia; + + explicit IsoMTrackBox(const AbstractIsoMBox& base) : IsoMContainerBox(base) {} +}; + +class IsoMMovieBox final : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::vector tracks; + + explicit IsoMMovieBox(const AbstractIsoMBox& base) : IsoMContainerBox(base) {} +}; + +struct IsoMMediaDataBox final : public IsoMBox { + explicit IsoMMediaDataBox(const AbstractIsoMBox& base) + : BaseBox(base), + mData(BaseBox::data.getStream(BaseBox::data.getRemainSize())) {} + + ByteStream mData; + + // The actual slicing of mData. Derived from SampleTable box. + std::vector chunks; + + // Validate. + explicit operator bool() const; + + void parse(IsoMRootBox* root); +}; + +// The root box. It's just a container, and can only be created from ByteStream. + +class IsoMRootBox final : public IsoMContainer { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + +public: + std::unique_ptr ftypBox; + std::unique_ptr moovBox; + std::unique_ptr mdatBox; + + const std::unique_ptr& ftyp() const; + const std::unique_ptr& moov() const; + const std::unique_ptr& mdat() const; + + explicit IsoMRootBox(ByteStream* bs) : IsoMContainer(bs) {} +}; + +} // namespace rawspeed diff --git a/test/librawspeed/common/RangeTest.h b/test/librawspeed/common/RangeTest.h index 45a7a3444..f12a219f0 100644 --- a/test/librawspeed/common/RangeTest.h +++ b/test/librawspeed/common/RangeTest.h @@ -70,6 +70,29 @@ ::testing::AssertionResult RangeDoesntContain(const char* range_expr, << " (" << pos << ")"; } +template +::testing::AssertionResult RangesNest(const char* m_expr, const char* n_expr, + const T& outer, const T& inner) { + if (RangesAreNested(outer, inner)) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << "Range " << m_expr << " (" << outer << ") does not contain range " + << n_expr << " (" << inner << ")"; +} + +template +::testing::AssertionResult RangesDontNest(const char* m_expr, + const char* n_expr, const T& outer, + const T& inner) { + if (!RangesAreNested(outer, inner)) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << "Range " << m_expr << " (" << outer << ") does contain range " + << n_expr << " (" << inner << ")"; +} + template ::testing::AssertionResult RangesOverlap(const char* m_expr, const char* n_expr, const T& lhs, const T& rhs) { @@ -140,4 +163,40 @@ static const std::set AllOverlapped{ std::make_tuple(2, 2, 2, 2), }; +using threeRangesType = std::tuple; +class ThreeRangesTest : public ::testing::TestWithParam { +protected: + ThreeRangesTest() = default; + virtual void SetUp() { + r0 = Range(std::get<0>(GetParam()), std::get<1>(GetParam())); + r1 = Range(std::get<2>(GetParam()), std::get<3>(GetParam())); + r2 = Range(std::get<4>(GetParam()), std::get<5>(GetParam())); + } + + Range r0; + Range r1; + Range r2; +}; +INSTANTIATE_TEST_CASE_P( + Unsigned, ThreeRangesTest, + testing::Combine(testing::Range(0, 3), testing::Range(0U, 3U), + testing::Range(0, 3), testing::Range(0U, 3U), + testing::Range(0, 3), testing::Range(0U, 3U))); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThreeRangesTest); + +static const std::set AllNested{ + std::make_tuple(0, 0, 0, 0), std::make_tuple(0, 1, 0, 0), + std::make_tuple(0, 1, 0, 1), std::make_tuple(0, 1, 1, 0), + std::make_tuple(0, 2, 0, 0), std::make_tuple(0, 2, 0, 1), + std::make_tuple(0, 2, 0, 2), std::make_tuple(0, 2, 1, 0), + std::make_tuple(0, 2, 1, 1), std::make_tuple(0, 2, 2, 0), + std::make_tuple(1, 0, 1, 0), std::make_tuple(1, 1, 1, 0), + std::make_tuple(1, 1, 1, 1), std::make_tuple(1, 1, 2, 0), + std::make_tuple(1, 2, 1, 0), std::make_tuple(1, 2, 1, 1), + std::make_tuple(1, 2, 1, 2), std::make_tuple(1, 2, 2, 0), + std::make_tuple(1, 2, 2, 1), std::make_tuple(2, 0, 2, 0), + std::make_tuple(2, 1, 2, 0), std::make_tuple(2, 1, 2, 1), + std::make_tuple(2, 2, 2, 0), std::make_tuple(2, 2, 2, 1), + std::make_tuple(2, 2, 2, 2)}; + } // namespace rawspeed_test diff --git a/test/librawspeed/test/ExceptionsTest.cpp b/test/librawspeed/test/ExceptionsTest.cpp index 64f3fe0e8..7b8068dbe 100644 --- a/test/librawspeed/test/ExceptionsTest.cpp +++ b/test/librawspeed/test/ExceptionsTest.cpp @@ -1,7 +1,7 @@ /* RawSpeed - RAW file decoder. - Copyright (C) 2016-2017 Roman Lebedev + Copyright (C) 2016-2018 Roman Lebedev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,10 +25,11 @@ #include "metadata/CameraMetadataException.h" // for CameraMetadataExceptio... #include "parsers/CiffParserException.h" // for CiffParserException (p... #include "parsers/FiffParserException.h" // for FiffParserException (p... -#include "parsers/RawParserException.h" // for RawParserException (pt... -#include "parsers/TiffParserException.h" // for TiffParserException (p... -#include // for exception -#include // for HasSubstr, ASSERT_THAT +#include "parsers/IsoMParserException.h" // for ThrowIPE, IsoMParserEx... +#include "parsers/RawParserException.h" // for ThrowRPE, RawParserEx... +#include "parsers/TiffParserException.h" // for ThrowTPE, TiffParserEx... +#include // IWYU pragma: keep +#include // for MakePredicateFormatter... #include // for Message, TestPartResult #include // for unique_ptr #include // for runtime_error @@ -38,6 +39,7 @@ using rawspeed::CiffParserException; using rawspeed::FiffParserException; using rawspeed::FileIOException; using rawspeed::IOException; +using rawspeed::IsoMParserException; using rawspeed::RawDecoderException; using rawspeed::RawParserException; using rawspeed::RawspeedException; @@ -91,13 +93,16 @@ template <> void* MetaHelper(const char* str) { ThrowFPE(FMT, str); } +template <> void* MetaHelper(const char* str) { + ThrowIPE(FMT, str); +} + template class ExceptionsTest : public testing::Test {}; -using Classes = - testing::Types; +using Classes = testing::Types< + RawspeedException, CameraMetadataException, CiffParserException, + FileIOException, IOException, RawDecoderException, TiffParserException, + FiffParserException, IsoMParserException, RawParserException>; TYPED_TEST_CASE(ExceptionsTest, Classes); From bd427169f3f9d8f5820c4c6fb81f772ec19b04c5 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:44:03 +0200 Subject: [PATCH 04/27] Extend helper functions getI16() and getByteSwapped() --- src/librawspeed/io/ByteStream.h | 1 + src/librawspeed/io/Endianness.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/librawspeed/io/ByteStream.h b/src/librawspeed/io/ByteStream.h index 74f1d2589..5269de08d 100644 --- a/src/librawspeed/io/ByteStream.h +++ b/src/librawspeed/io/ByteStream.h @@ -164,6 +164,7 @@ class ByteStream : public DataBuffer return ret; } + inline uint16_t getI16() { return get(); } inline uint16_t getU16() { return get(); } inline int32_t getI32() { return get(); } inline uint32_t getU32() { return get(); } diff --git a/src/librawspeed/io/Endianness.h b/src/librawspeed/io/Endianness.h index 065c5047c..5073b2fd5 100644 --- a/src/librawspeed/io/Endianness.h +++ b/src/librawspeed/io/Endianness.h @@ -67,6 +67,12 @@ inline Endianness getHostEndianness() { #define BSWAP64(A) __builtin_bswap64(A) #endif +inline int8_t getByteSwapped(int8_t v) { + return v; +} +inline uint8_t getByteSwapped(uint8_t v) { + return v; +} inline int16_t getByteSwapped(int16_t v) { return static_cast(BSWAP16(static_cast(v))); } From e70009d3b065d4e63457870c789bbce2acd898d6 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:46:45 +0200 Subject: [PATCH 05/27] Add CrxDecompressor for CR3 raw files This decompressor is ported from libraw. Some of the file handling logic has been replaced by rawspeed::Buffer and derived classes. Most of the code is untouched. --- src/librawspeed/decompressors/CMakeLists.txt | 2 + .../decompressors/CrxDecompressor.cpp | 2505 +++++++++++++++++ .../decompressors/CrxDecompressor.h | 51 + 3 files changed, 2558 insertions(+) create mode 100644 src/librawspeed/decompressors/CrxDecompressor.cpp create mode 100644 src/librawspeed/decompressors/CrxDecompressor.h diff --git a/src/librawspeed/decompressors/CMakeLists.txt b/src/librawspeed/decompressors/CMakeLists.txt index e723c6761..fb2d327e9 100644 --- a/src/librawspeed/decompressors/CMakeLists.txt +++ b/src/librawspeed/decompressors/CMakeLists.txt @@ -11,6 +11,8 @@ FILE(GLOB SOURCES "Cr2Decompressor.h" "CrwDecompressor.cpp" "CrwDecompressor.h" + "CrxDecompressor.cpp" + "CrxDecompressor.h" "DeflateDecompressor.cpp" "DeflateDecompressor.h" "FujiDecompressor.cpp" diff --git a/src/librawspeed/decompressors/CrxDecompressor.cpp b/src/librawspeed/decompressors/CrxDecompressor.cpp new file mode 100644 index 000000000..9f35bb04e --- /dev/null +++ b/src/librawspeed/decompressors/CrxDecompressor.cpp @@ -0,0 +1,2505 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018-2019 Alexey Danilchenko + Copyright (C) 2019 Alex Tutubalin, LibRaw LLC + Copyright (C) 2021 Daniel Vogelbacher + + This code is ported from libraw: + https://github.com/LibRaw/LibRaw/blob/637b935bef384f6a96056c950696468d34335495/src/decoders/crx.cpp + For code compatibility, libraw routines like sgetn() are keept. + File reading was entirely replaced by rawspeed Buffer class. + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "decompressors/CrxDecompressor.h" // For CrxDecompressor +#include "common/Array2DRef.h" // for Array2DRef +#include "common/Point.h" // for iPoint2D, iPoint2D::area_type +#include "common/RawImage.h" // for RawImage, RawImageData +#include "decoders/Cr3Decoder.h" // for Cr3Decoder +#include "decoders/RawDecoderException.h" // for ThrowRDE +#include "decompressors/UncompressedDecompressor.h" // For UncompressedDecompresser +#include "io/Endianness.h" // For Endianness +#include // for copy_n, min +#include // for array +#include // for assert +#include // for initializer_list + +// this should be divisible by 4 +#define CRX_BUF_SIZE 0x10000 + +// Definitions copied from libraw +#ifdef _abs +#undef _abs +#undef _min +#undef _constrain +#endif +#define _abs(x) (((x) ^ ((int32_t)(x) >> 31)) - ((int32_t)(x) >> 31)) +#define _min(a, b) ((a) < (b) ? (a) : (b)) +#define _constrain(x, l, u) ((x) < (l) ? (l) : ((x) > (u) ? (u) : (x))) + +#if !defined(_WIN32) || \ + (defined(__GNUC__) && !defined(__INTRINSIC_SPECIAL__BitScanReverse)) +/* __INTRINSIC_SPECIAL__BitScanReverse found in MinGW32-W64 v7.30 headers, may + * be there is a better solution? */ +typedef uint32_t DWORD; +inline void _BitScanReverse(DWORD* Index, unsigned long Mask) { + *Index = sizeof(unsigned long) * 8 - 1 - __builtin_clzl(Mask); +} +#endif + +namespace rawspeed { + +static inline unsigned sgetn(int n, const uint8_t* s) { + unsigned result = 0; + while (n-- > 0) + result = (result << 8) | (*s++); + return result; +} + +struct CrxBitstream { + std::vector mdatBuf; + uint64_t mdatSize; + uint64_t curBufOffset; + uint32_t curPos; + uint32_t curBufSize; + uint32_t bitData; + int32_t bitsLeft; + rawspeed::Buffer crxRawData; +}; + +struct CrxBandParam { + CrxBitstream bitStream; + int16_t subbandWidth; + int16_t subbandHeight; + int32_t roundedBitsMask; + int32_t roundedBits; + int16_t curLine; + int32_t* lineBuf0; + int32_t* lineBuf1; + int32_t* lineBuf2; + int32_t sParam; + int32_t kParam; + int32_t* paramData; + int32_t* nonProgrData; + bool supportsPartial; +}; + +struct CrxWaveletTransform { + int32_t* subband0Buf; + int32_t* subband1Buf; + int32_t* subband2Buf; + int32_t* subband3Buf; + int32_t* lineBuf[8]; + int16_t curLine; + int16_t curH; + int8_t fltTapH; + int16_t height; + int16_t width; +}; + +struct CrxSubband { + CrxBandParam* bandParam; + uint64_t mdatOffset; + uint8_t* bandBuf; + uint16_t width; + uint16_t height; + int32_t qParam; + int32_t kParam; + uint32_t qStepBase; + uint32_t qStepMult; + bool supportsPartial; + int32_t bandSize; + uint64_t dataSize; + int64_t dataOffset; + short rowStartAddOn; + short rowEndAddOn; + short colStartAddOn; + short colEndAddOn; + short levelShift; +}; + +struct CrxPlaneComp { + uint8_t* compBuf; + CrxSubband* subBands; + CrxWaveletTransform* wvltTransform; + int8_t compNumber; + int64_t dataOffset; + int32_t compSize; + bool supportsPartial; + int32_t roundedBitsMask; + int8_t tileFlag; +}; + +struct CrxQStep { + uint32_t* qStepTbl; + int width; + int height; +}; + +struct CrxTile { + CrxPlaneComp* comps; + int8_t tileFlag; + int8_t tileNumber; + int64_t dataOffset; + int32_t tileSize; + uint16_t width; + uint16_t height; + bool hasQPData; + CrxQStep* qStep; + uint32_t mdatQPDataSize; + uint16_t mdatExtraSize; +}; + +struct CrxImage { + uint8_t nPlanes; + uint16_t planeWidth; + uint16_t planeHeight; + uint8_t samplePrecision; + uint8_t subbandCount; + uint8_t levels; + uint8_t nBits; + uint8_t encType; + uint16_t tileCols; + uint16_t tileRows; + CrxTile* tiles; + uint64_t mdatOffset; + int32_t mdatHdrSize; + int16_t* outBufs[4]; // one per plane + int16_t* planeBuf; + Buffer crxRawData; +}; + +enum TileFlags { + E_HAS_TILES_ON_THE_RIGHT = 1, + E_HAS_TILES_ON_THE_LEFT = 2, + E_HAS_TILES_ON_THE_BOTTOM = 4, + E_HAS_TILES_ON_THE_TOP = 8 +}; + +int32_t exCoefNumTbl[144] = { + 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, + 1, 1, 1, 2, 2, 1, 1, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +int32_t q_step_tbl[8] = {0x28, 0x2D, 0x33, 0x39, 0x40, 0x48}; + +uint32_t JS[32] = {1, 1, 1, 1, 2, 2, 2, 2, + 4, 4, 4, 4, 8, 8, 8, 8, + 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, + 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000}; + +uint32_t J[32] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + +static inline void crxFillBuffer(CrxBitstream* bitStrm) { + if (bitStrm->curPos >= bitStrm->curBufSize && bitStrm->mdatSize) { + bitStrm->curPos = 0; + bitStrm->curBufOffset += bitStrm->curBufSize; + + auto sub = bitStrm->crxRawData.getSubView(bitStrm->curBufOffset); + + bitStrm->mdatBuf.resize(CRX_BUF_SIZE); + auto bytesToRead = _min(bitStrm->mdatSize, CRX_BUF_SIZE); + + if (sub.getSize() >= bytesToRead) { + auto data = sub.getData(0, bytesToRead); + assert(bitStrm->mdatBuf.size() > 0); + ::memcpy(bitStrm->mdatBuf.data(), data, bytesToRead); + bitStrm->curBufSize = bytesToRead; + } + + if (bitStrm->curBufSize < 1) // nothing read + ThrowRDE("Error reading more bytes"); + bitStrm->mdatSize -= bitStrm->curBufSize; + } +} + +inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { + uint32_t nonZeroBit = 0; + uint64_t nextData = 0; + int32_t result = 0; + + if (bitStrm->bitData) { + _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)bitStrm->bitData); + result = 31 - nonZeroBit; + bitStrm->bitData <<= 32 - nonZeroBit; + bitStrm->bitsLeft -= 32 - nonZeroBit; + } else { + uint32_t bitsLeft = bitStrm->bitsLeft; + while (1) { + while (bitStrm->curPos + 4 <= bitStrm->curBufSize) { + nextData = getByteSwapped( + *(uint32_t*)(bitStrm->mdatBuf.data() + bitStrm->curPos)); + bitStrm->curPos += 4; + crxFillBuffer(bitStrm); + if (nextData) { + _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)nextData); + result = bitsLeft + 31 - nonZeroBit; + bitStrm->bitData = nextData << (32 - nonZeroBit); + bitStrm->bitsLeft = nonZeroBit; + return result; + } + bitsLeft += 32; + } + if (bitStrm->curBufSize < bitStrm->curPos + 1) + break; // error + nextData = bitStrm->mdatBuf[bitStrm->curPos++]; + crxFillBuffer(bitStrm); + if (nextData) + break; + bitsLeft += 8; + } + _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)nextData); + result = (uint32_t)(bitsLeft + 7 - nonZeroBit); + bitStrm->bitData = nextData << (32 - nonZeroBit); + bitStrm->bitsLeft = nonZeroBit; + } + return result; +} + +inline uint32_t crxBitstreamGetBits(CrxBitstream* bitStrm, int bits) { + int bitsLeft = bitStrm->bitsLeft; + uint32_t bitData = bitStrm->bitData; + uint32_t nextWord; + uint8_t nextByte; + uint32_t result; + + if (bitsLeft < bits) { + // get them from stream + if (bitStrm->curPos + 4 <= bitStrm->curBufSize) { + nextWord = getByteSwapped( + *(uint32_t*)(bitStrm->mdatBuf.data() + bitStrm->curPos)); + bitStrm->curPos += 4; + crxFillBuffer(bitStrm); + bitStrm->bitsLeft = 32 - (bits - bitsLeft); + result = ((nextWord >> bitsLeft) | bitData) >> (32 - bits); + bitStrm->bitData = nextWord << (bits - bitsLeft); + return result; + } + // less than a word left - read byte at a time + do { + if (bitStrm->curPos >= bitStrm->curBufSize) + break; // error + bitsLeft += 8; + nextByte = bitStrm->mdatBuf[bitStrm->curPos++]; + crxFillBuffer(bitStrm); + bitData |= nextByte << (32 - bitsLeft); + } while (bitsLeft < bits); + } + result = bitData >> (32 - bits); // 32-bits + bitStrm->bitData = bitData << bits; + bitStrm->bitsLeft = bitsLeft - bits; + return result; +} + +inline int32_t crxPrediction(int32_t left, int32_t top, int32_t deltaH, + int32_t deltaV) { + int32_t symb[4] = {left + deltaH, left + deltaH, left, top}; + + return symb[(((deltaV < 0) ^ (deltaH < 0)) << 1) + + ((left < top) ^ (deltaH < 0))]; +} + +inline int32_t crxPredictKParameter(int32_t prevK, int32_t bitCode, + int32_t maxVal = 0) { + int32_t newKParam = prevK - (bitCode < (1 << prevK >> 1)) + + ((bitCode >> prevK) > 2) + ((bitCode >> prevK) > 5); + + return !maxVal || newKParam < maxVal ? newKParam : maxVal; +} + +inline void crxDecodeSymbolL1(CrxBandParam* param, int32_t doMedianPrediction, + int32_t notEOL = 0) { + if (doMedianPrediction) { + int32_t symb[4]; + + int32_t delta = param->lineBuf0[1] - param->lineBuf0[0]; + symb[2] = param->lineBuf1[0]; + symb[0] = symb[1] = delta + symb[2]; + symb[3] = param->lineBuf0[1]; + + param->lineBuf1[1] = + symb[(((param->lineBuf0[0] < param->lineBuf1[0]) ^ (delta < 0)) << 1) + + ((param->lineBuf1[0] < param->lineBuf0[1]) ^ (delta < 0))]; + } else + param->lineBuf1[1] = param->lineBuf0[1]; + + // get next error symbol + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + + // add converted (+/-) error code to predicted value + param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1); + + // for not end of the line - use one symbol ahead to estimate next K + if (notEOL) { + int32_t nextDelta = (param->lineBuf0[2] - param->lineBuf0[1]) << 1; + bitCode = (bitCode + _abs(nextDelta)) >> 1; + ++param->lineBuf0; + } + + // update K parameter + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + + ++param->lineBuf1; +} + +int crxDecodeLine(CrxBandParam* param) { + int length = param->subbandWidth; + + param->lineBuf1[0] = param->lineBuf0[1]; + for (; length > 1; --length) { + if (param->lineBuf1[0] != param->lineBuf0[1] || + param->lineBuf1[0] != param->lineBuf0[2]) { + crxDecodeSymbolL1(param, 1, 1); + } else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (nSyms > length) { + nSyms = length; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (nSyms == length) + break; + } + + if (nSyms < length) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + if (nSyms > length) + return -1; + } + + length -= nSyms; + + // copy symbol nSyms times + param->lineBuf0 += nSyms; + + // copy symbol nSyms times + while (nSyms-- > 0) { + param->lineBuf1[1] = param->lineBuf1[0]; + ++param->lineBuf1; + } + } + + if (length > 0) + crxDecodeSymbolL1(param, 0, (length > 1)); + } + } + + if (length == 1) + crxDecodeSymbolL1(param, 1, 0); + + param->lineBuf1[1] = param->lineBuf1[0] + 1; + + return 0; +} + +inline void crxDecodeSymbolL1Rounded(CrxBandParam* param, int32_t doSym = 1, + int32_t doCode = 1) { + int32_t sym = param->lineBuf0[1]; + + if (doSym) { + // calculate the next symbol gradient + int32_t symb[4]; + int32_t deltaH = param->lineBuf0[1] - param->lineBuf0[0]; + symb[2] = param->lineBuf1[0]; + symb[0] = symb[1] = deltaH + symb[2]; + symb[3] = param->lineBuf0[1]; + sym = + symb[(((param->lineBuf0[0] < param->lineBuf1[0]) ^ (deltaH < 0)) << 1) + + ((param->lineBuf1[0] < param->lineBuf0[1]) ^ (deltaH < 0))]; + } + + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + int32_t code = -(bitCode & 1) ^ (bitCode >> 1); + param->lineBuf1[1] = param->roundedBitsMask * 2 * code + (code >> 31) + sym; + + if (doCode) { + if (param->lineBuf0[2] > param->lineBuf0[1]) + code = (param->lineBuf0[2] - param->lineBuf0[1] + param->roundedBitsMask - + 1) >> + param->roundedBits; + else + code = -( + (param->lineBuf0[1] - param->lineBuf0[2] + param->roundedBitsMask) >> + param->roundedBits); + + param->kParam = crxPredictKParameter(param->kParam, + (bitCode + 2 * _abs(code)) >> 1, 15); + } else + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + + ++param->lineBuf1; +} + +int crxDecodeLineRounded(CrxBandParam* param) { + int32_t valueReached = 0; + + param->lineBuf0[0] = param->lineBuf0[1]; + param->lineBuf1[0] = param->lineBuf0[1]; + int32_t length = param->subbandWidth; + + for (; length > 1; --length) { + if (_abs(param->lineBuf0[2] - param->lineBuf0[1]) > + param->roundedBitsMask) { + crxDecodeSymbolL1Rounded(param); + ++param->lineBuf0; + valueReached = 1; + } else if (valueReached || _abs(param->lineBuf0[0] - param->lineBuf1[0]) > + param->roundedBitsMask) { + crxDecodeSymbolL1Rounded(param); + ++param->lineBuf0; + valueReached = 0; + } else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (nSyms > length) { + nSyms = length; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (nSyms == length) + break; + } + if (nSyms < length) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + } + if (nSyms > length) + return -1; + } + length -= nSyms; + + // copy symbol nSyms times + param->lineBuf0 += nSyms; + + // copy symbol nSyms times + while (nSyms-- > 0) { + param->lineBuf1[1] = param->lineBuf1[0]; + ++param->lineBuf1; + } + + if (length > 1) { + crxDecodeSymbolL1Rounded(param, 0); + ++param->lineBuf0; + valueReached = _abs(param->lineBuf0[1] - param->lineBuf0[0]) > + param->roundedBitsMask; + } else if (length == 1) + crxDecodeSymbolL1Rounded(param, 0, 0); + } + } + if (length == 1) + crxDecodeSymbolL1Rounded(param, 1, 0); + + param->lineBuf1[1] = param->lineBuf1[0] + 1; + + return 0; +} + +int crxDecodeLineNoRefPrevLine(CrxBandParam* param) { + int32_t i = 0; + + for (; i < param->subbandWidth - 1; i++) { + if (param->lineBuf0[i + 2] | param->lineBuf0[i + 1] | param->lineBuf1[i]) { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[i + 1] = -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode); + if (param->lineBuf2[i + 1] - param->kParam <= 1) { + if (param->kParam >= 15) + param->kParam = 15; + } else + ++param->kParam; + } else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + if (i != param->subbandWidth - 1) { + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (i + nSyms > param->subbandWidth) { + nSyms = param->subbandWidth - i; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (i + nSyms == param->subbandWidth) + break; + } + if (i + nSyms < param->subbandWidth) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + } + if (i + nSyms > param->subbandWidth) + return -1; + } + } else if (i > param->subbandWidth) + return -1; + + if (nSyms > 0) { + memset(param->lineBuf1 + i + 1, 0, nSyms * sizeof(int32_t)); + memset(param->lineBuf2 + i, 0, nSyms * sizeof(int32_t)); + i += nSyms; + } + + if (i >= param->subbandWidth - 1) { + if (i == param->subbandWidth - 1) { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[i + 1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + param->lineBuf2[i] = param->kParam; + } + continue; + } else { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[i + 1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode); + if (param->lineBuf2[i + 1] - param->kParam <= 1) { + if (param->kParam >= 15) + param->kParam = 15; + } else + ++param->kParam; + } + } + param->lineBuf2[i] = param->kParam; + } + if (i == param->subbandWidth - 1) { + int32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[i + 1] = -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + param->lineBuf2[i] = param->kParam; + } + + return 0; +} + +int crxDecodeTopLine(CrxBandParam* param) { + param->lineBuf1[0] = 0; + + int32_t length = param->subbandWidth; + + // read the line from bitstream + for (; length > 1; --length) { + if (param->lineBuf1[0]) + param->lineBuf1[1] = param->lineBuf1[0]; + else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (nSyms > length) { + nSyms = length; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (nSyms == length) + break; + } + if (nSyms < length) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + if (nSyms > length) + return -1; + } + + length -= nSyms; + + // copy symbol nSyms times + while (nSyms-- > 0) { + param->lineBuf1[1] = param->lineBuf1[0]; + ++param->lineBuf1; + } + + if (length <= 0) + break; + } + + param->lineBuf1[1] = 0; + } + + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + ++param->lineBuf1; + } + + if (length == 1) { + param->lineBuf1[1] = param->lineBuf1[0]; + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[1] += -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + ++param->lineBuf1; + } + + param->lineBuf1[1] = param->lineBuf1[0] + 1; + + return 0; +} + +int crxDecodeTopLineRounded(CrxBandParam* param) { + param->lineBuf1[0] = 0; + + int32_t length = param->subbandWidth; + + // read the line from bitstream + for (; length > 1; --length) { + if (_abs(param->lineBuf1[0]) > param->roundedBitsMask) + param->lineBuf1[1] = param->lineBuf1[0]; + else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (nSyms > length) { + nSyms = length; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (nSyms == length) + break; + } + if (nSyms < length) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + if (nSyms > length) + return -1; + } + } + + length -= nSyms; + + // copy symbol nSyms times + while (nSyms-- > 0) { + param->lineBuf1[1] = param->lineBuf1[0]; + ++param->lineBuf1; + } + + if (length <= 0) + break; + + param->lineBuf1[1] = 0; + } + + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + + int32_t sVal = -(bitCode & 1) ^ (bitCode >> 1); + param->lineBuf1[1] += param->roundedBitsMask * 2 * sVal + (sVal >> 31); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + ++param->lineBuf1; + } + + if (length == 1) { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + int32_t sVal = -(bitCode & 1) ^ (bitCode >> 1); + param->lineBuf1[1] += param->roundedBitsMask * 2 * sVal + (sVal >> 31); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + ++param->lineBuf1; + } + + param->lineBuf1[1] = param->lineBuf1[0] + 1; + + return 0; +} + +int crxDecodeTopLineNoRefPrevLine(CrxBandParam* param) { + param->lineBuf0[0] = 0; + param->lineBuf1[0] = 0; + int32_t length = param->subbandWidth; + for (; length > 1; --length) { + if (param->lineBuf1[0]) { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[1] = -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + } else { + int nSyms = 0; + if (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms = 1; + while (crxBitstreamGetBits(¶m->bitStream, 1)) { + nSyms += JS[param->sParam]; + if (nSyms > length) { + nSyms = length; + break; + } + if (param->sParam < 31) + ++param->sParam; + if (nSyms == length) + break; + } + if (nSyms < length) { + if (J[param->sParam]) + nSyms += crxBitstreamGetBits(¶m->bitStream, J[param->sParam]); + if (param->sParam > 0) + --param->sParam; + if (nSyms > length) + return -1; + } + } + + length -= nSyms; + + // copy symbol nSyms times + while (nSyms-- > 0) { + param->lineBuf2[0] = 0; + param->lineBuf1[1] = 0; + ++param->lineBuf1; + ++param->lineBuf2; + } + + if (length <= 0) + break; + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[1] = -((bitCode + 1) & 1) ^ ((bitCode + 1) >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + } + param->lineBuf2[0] = param->kParam; + ++param->lineBuf2; + ++param->lineBuf1; + } + + if (length == 1) { + uint32_t bitCode = crxBitstreamGetZeros(¶m->bitStream); + if (bitCode >= 41) + bitCode = crxBitstreamGetBits(¶m->bitStream, 21); + else if (param->kParam) + bitCode = crxBitstreamGetBits(¶m->bitStream, param->kParam) | + (bitCode << param->kParam); + param->lineBuf1[1] = -(bitCode & 1) ^ (bitCode >> 1); + param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); + param->lineBuf2[0] = param->kParam; + ++param->lineBuf1; + } + + param->lineBuf1[1] = 0; + + return 0; +} + +int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { + if (!param || !bandBuf) + return -1; + if (param->curLine >= param->subbandHeight) + return -1; + + if (param->curLine == 0) { + int32_t lineLength = param->subbandWidth + 2; + + param->sParam = 0; + param->kParam = 0; + if (param->supportsPartial) { + if (param->roundedBitsMask <= 0) { + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeTopLine(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } else { + param->roundedBits = 1; + if (param->roundedBitsMask & ~1) { + while (param->roundedBitsMask >> param->roundedBits) + ++param->roundedBits; + } + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeTopLineRounded(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } + } else { + param->lineBuf2 = (int32_t*)param->nonProgrData; + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeTopLineNoRefPrevLine(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } + } else if (!param->supportsPartial) { + int32_t lineLength = param->subbandWidth + 2; + param->lineBuf2 = (int32_t*)param->nonProgrData; + if (param->curLine & 1) { + param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf0 = param->lineBuf1 + lineLength; + } else { + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + } + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeLineNoRefPrevLine(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } else if (param->roundedBitsMask <= 0) { + int32_t lineLength = param->subbandWidth + 2; + if (param->curLine & 1) { + param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf0 = param->lineBuf1 + lineLength; + } else { + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + } + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeLine(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } else { + int32_t lineLength = param->subbandWidth + 2; + if (param->curLine & 1) { + param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf0 = param->lineBuf1 + lineLength; + } else { + param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf1 = param->lineBuf0 + lineLength; + } + int32_t* lineBuf = param->lineBuf1 + 1; + if (crxDecodeLineRounded(param)) + return -1; + memcpy(bandBuf, lineBuf, param->subbandWidth * sizeof(int32_t)); + ++param->curLine; + } + return 0; +} + +int crxUpdateQparam(CrxSubband* subband) { + uint32_t bitCode = crxBitstreamGetZeros(&subband->bandParam->bitStream); + if (bitCode >= 23) + bitCode = crxBitstreamGetBits(&subband->bandParam->bitStream, 8); + else if (subband->kParam) + bitCode = + crxBitstreamGetBits(&subband->bandParam->bitStream, subband->kParam) | + (bitCode << subband->kParam); + + subband->qParam += + -(bitCode & 1) ^ (bitCode >> 1); // converting encoded to signed integer + subband->kParam = crxPredictKParameter(subband->kParam, bitCode); + if (subband->kParam > 7) + return -1; + return 0; +} + +inline int getSubbandRow(CrxSubband* band, int row) { + return row < band->rowStartAddOn ? 0 + : (row < band->height - band->rowEndAddOn + ? row - band->rowEndAddOn + : band->height - band->rowEndAddOn - + band->rowStartAddOn - 1); +} +int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { + if (!band->dataSize) { + memset(band->bandBuf, 0, band->bandSize); + return 0; + } + + if (band->supportsPartial && !qStep && crxUpdateQparam(band)) + return -1; + if (crxDecodeLine(band->bandParam, band->bandBuf)) + return -1; + + if (band->width <= 0) + return 0; + + // update band buffers + int32_t* bandBuf = (int32_t*)band->bandBuf; + if (qStep) { + // new version + uint32_t* qStepTblPtr = + &qStep->qStepTbl[qStep->width * + getSubbandRow(band, band->bandParam->curLine - 1)]; + + for (int i = 0; i < band->colStartAddOn; ++i) { + uint32_t quantVal = + band->qStepBase + ((qStepTblPtr[0] * band->qStepMult) >> 3); + bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + } + + for (int i = band->colStartAddOn; i < band->width - band->colEndAddOn; + ++i) { + uint32_t quantVal = + band->qStepBase + + ((qStepTblPtr[(i - band->colStartAddOn) >> band->levelShift] * + band->qStepMult) >> + 3); + bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + } + int lastIdx = (band->width - band->colEndAddOn - band->colStartAddOn - 1) >> + band->levelShift; + for (int i = band->width - band->colEndAddOn; i < band->width; ++i) { + uint32_t quantVal = + band->qStepBase + ((qStepTblPtr[lastIdx] * band->qStepMult) >> 3); + bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + } + } else { + // prev. version + int32_t qScale = q_step_tbl[band->qParam % 6] >> (6 - band->qParam / 6); + if (band->qParam / 6 >= 6) + qScale = q_step_tbl[band->qParam % 6] * (1 << (band->qParam / 6 + 26)); + + if (qScale != 1) + for (int32_t i = 0; i < band->width; ++i) + bandBuf[i] *= qScale; + } + + return 0; +} + +void crxHorizontal53(int32_t* lineBufLA, int32_t* lineBufLB, + CrxWaveletTransform* wavelet, uint32_t tileFlag) { + int32_t* band0Buf = wavelet->subband0Buf; + int32_t* band1Buf = wavelet->subband1Buf; + int32_t* band2Buf = wavelet->subband2Buf; + int32_t* band3Buf = wavelet->subband3Buf; + + if (wavelet->width <= 1) { + lineBufLA[0] = band0Buf[0]; + lineBufLB[0] = band2Buf[0]; + } else { + if (tileFlag & E_HAS_TILES_ON_THE_LEFT) { + lineBufLA[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufLB[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + ++band1Buf; + ++band3Buf; + } else { + lineBufLA[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + lineBufLB[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + } + ++band0Buf; + ++band2Buf; + + for (int i = 0; i < wavelet->width - 3; i += 2) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufLA[1] = band1Buf[0] + ((delta + lineBufLA[0]) >> 1); + lineBufLA[2] = delta; + + delta = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufLB[1] = band3Buf[0] + ((delta + lineBufLB[0]) >> 1); + lineBufLB[2] = delta; + + ++band0Buf; + ++band1Buf; + ++band2Buf; + ++band3Buf; + lineBufLA += 2; + lineBufLB += 2; + } + if (tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + int32_t deltaA = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufLA[1] = band1Buf[0] + ((deltaA + lineBufLA[0]) >> 1); + + int32_t deltaB = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufLB[1] = band3Buf[0] + ((deltaB + lineBufLB[0]) >> 1); + + if (wavelet->width & 1) { + lineBufLA[2] = deltaA; + lineBufLB[2] = deltaB; + } + } else if (wavelet->width & 1) { + lineBufLA[1] = + band1Buf[0] + + ((lineBufLA[0] + band0Buf[0] - ((band1Buf[0] + 1) >> 1)) >> 1); + lineBufLA[2] = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + + lineBufLB[1] = + band3Buf[0] + + ((lineBufLB[0] + band2Buf[0] - ((band3Buf[0] + 1) >> 1)) >> 1); + lineBufLB[2] = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + } else { + lineBufLA[1] = lineBufLA[0] + band1Buf[0]; + lineBufLB[1] = lineBufLB[0] + band3Buf[0]; + } + } +} + +int32_t* crxIdwt53FilterGetLine(CrxPlaneComp* comp, int32_t level) { + int32_t* result = + comp->wvltTransform[level].lineBuf[(comp->wvltTransform[level].fltTapH - + comp->wvltTransform[level].curH + 5) % + 5 + + 3]; + comp->wvltTransform[level].curH--; + return result; +} + +int crxIdwt53FilterDecode(CrxPlaneComp* comp, int32_t level, CrxQStep* qStep) { + if (comp->wvltTransform[level].curH) + return 0; + + CrxSubband* sband = comp->subBands + 3 * level; + CrxQStep* qStepLevel = qStep ? qStep + level : 0; + + if (comp->wvltTransform[level].height - 3 <= + comp->wvltTransform[level].curLine && + !(comp->tileFlag & E_HAS_TILES_ON_THE_BOTTOM)) { + if (comp->wvltTransform[level].height & 1) { + if (level) { + if (crxIdwt53FilterDecode(comp, level - 1, qStep)) + return -1; + } else if (crxDecodeLineWithIQuantization(sband, qStepLevel)) + return -1; + + if (crxDecodeLineWithIQuantization(sband + 1, qStepLevel)) + return -1; + } + } else { + if (level) { + if (crxIdwt53FilterDecode(comp, level - 1, qStep)) + return -1; + } else if (crxDecodeLineWithIQuantization(sband, qStepLevel)) // LL band + return -1; + + if (crxDecodeLineWithIQuantization(sband + 1, qStepLevel) || // HL band + crxDecodeLineWithIQuantization(sband + 2, qStepLevel) || // LH band + crxDecodeLineWithIQuantization(sband + 3, qStepLevel)) // HH band + return -1; + } + + return 0; +} + +int crxIdwt53FilterTransform(CrxPlaneComp* comp, uint32_t level) { + CrxWaveletTransform* wavelet = comp->wvltTransform + level; + + if (wavelet->curH) + return 0; + + if (wavelet->curLine >= wavelet->height - 3) { + if (!(comp->tileFlag & E_HAS_TILES_ON_THE_BOTTOM)) { + if (wavelet->height & 1) { + if (level) { + if (!wavelet[-1].curH) + if (crxIdwt53FilterTransform(comp, level - 1)) + return -1; + wavelet->subband0Buf = crxIdwt53FilterGetLine(comp, level - 1); + } + int32_t* band0Buf = wavelet->subband0Buf; + int32_t* band1Buf = wavelet->subband1Buf; + int32_t* lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3]; + int32_t* lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3]; + int32_t* lineBufH2 = wavelet->lineBuf[(wavelet->fltTapH + 2) % 5 + 3]; + + int32_t* lineBufL0 = wavelet->lineBuf[0]; + int32_t* lineBufL1 = wavelet->lineBuf[1]; + wavelet->lineBuf[1] = wavelet->lineBuf[2]; + wavelet->lineBuf[2] = lineBufL1; + + // process L bands + if (wavelet->width <= 1) { + lineBufL0[0] = band0Buf[0]; + } else { + if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT) { + lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + ++band1Buf; + } else { + lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + } + ++band0Buf; + for (int i = 0; i < wavelet->width - 3; i += 2) { + int32_t delta = + band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1); + lineBufL0[2] = delta; + ++band0Buf; + ++band1Buf; + lineBufL0 += 2; + } + if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + int32_t delta = + band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1); + if (wavelet->width & 1) + lineBufL0[2] = delta; + } else if (wavelet->width & 1) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + lineBufL0[1] = band1Buf[0] + ((lineBufL0[0] + delta) >> 1); + lineBufL0[2] = delta; + } else + lineBufL0[1] = band1Buf[0] + lineBufL0[0]; + } + + // process H bands + lineBufL0 = wavelet->lineBuf[0]; + lineBufL1 = wavelet->lineBuf[1]; + for (int32_t i = 0; i < wavelet->width; i++) { + int32_t delta = lineBufL0[i] - ((lineBufL1[i] + 1) >> 1); + lineBufH1[i] = lineBufL1[i] + ((delta + lineBufH0[i]) >> 1); + lineBufH2[i] = delta; + } + wavelet->curH += 3; + wavelet->curLine += 3; + wavelet->fltTapH = (wavelet->fltTapH + 3) % 5; + } else { + int32_t* lineBufL2 = wavelet->lineBuf[2]; + int32_t* lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3]; + int32_t* lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3]; + wavelet->lineBuf[1] = lineBufL2; + wavelet->lineBuf[2] = wavelet->lineBuf[1]; + + for (int32_t i = 0; i < wavelet->width; i++) + lineBufH1[i] = lineBufH0[i] + lineBufL2[i]; + + wavelet->curH += 2; + wavelet->curLine += 2; + wavelet->fltTapH = (wavelet->fltTapH + 2) % 5; + } + } + } else { + if (level) { + if (!wavelet[-1].curH && crxIdwt53FilterTransform(comp, level - 1)) + return -1; + wavelet->subband0Buf = crxIdwt53FilterGetLine(comp, level - 1); + } + + int32_t* band0Buf = wavelet->subband0Buf; + int32_t* band1Buf = wavelet->subband1Buf; + int32_t* band2Buf = wavelet->subband2Buf; + int32_t* band3Buf = wavelet->subband3Buf; + + int32_t* lineBufL0 = wavelet->lineBuf[0]; + int32_t* lineBufL1 = wavelet->lineBuf[1]; + int32_t* lineBufL2 = wavelet->lineBuf[2]; + int32_t* lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3]; + int32_t* lineBufH1 = wavelet->lineBuf[(wavelet->fltTapH + 1) % 5 + 3]; + int32_t* lineBufH2 = wavelet->lineBuf[(wavelet->fltTapH + 2) % 5 + 3]; + + wavelet->lineBuf[1] = wavelet->lineBuf[2]; + wavelet->lineBuf[2] = lineBufL1; + + // process L bands + if (wavelet->width <= 1) { + lineBufL0[0] = band0Buf[0]; + lineBufL1[0] = band2Buf[0]; + } else { + if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT) { + lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufL1[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + ++band1Buf; + ++band3Buf; + } else { + lineBufL0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + lineBufL1[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + } + ++band0Buf; + ++band2Buf; + for (int i = 0; i < wavelet->width - 3; i += 2) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufL0[1] = band1Buf[0] + ((delta + lineBufL0[0]) >> 1); + lineBufL0[2] = delta; + + delta = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufL1[1] = band3Buf[0] + ((delta + lineBufL1[0]) >> 1); + lineBufL1[2] = delta; + + ++band0Buf; + ++band1Buf; + ++band2Buf; + ++band3Buf; + lineBufL0 += 2; + lineBufL1 += 2; + } + if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + int32_t deltaA = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufL0[1] = band1Buf[0] + ((deltaA + lineBufL0[0]) >> 1); + + int32_t deltaB = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufL1[1] = band3Buf[0] + ((deltaB + lineBufL1[0]) >> 1); + + if (wavelet->width & 1) { + lineBufL0[2] = deltaA; + lineBufL1[2] = deltaB; + } + } else if (wavelet->width & 1) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + lineBufL0[1] = band1Buf[0] + ((delta + lineBufL0[0]) >> 1); + lineBufL0[2] = delta; + + delta = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + lineBufL1[1] = band3Buf[0] + ((delta + lineBufL1[0]) >> 1); + lineBufL1[2] = delta; + } else { + lineBufL0[1] = lineBufL0[0] + band1Buf[0]; + lineBufL1[1] = lineBufL1[0] + band3Buf[0]; + } + } + + // process H bands + lineBufL0 = wavelet->lineBuf[0]; + lineBufL1 = wavelet->lineBuf[1]; + lineBufL2 = wavelet->lineBuf[2]; + for (int32_t i = 0; i < wavelet->width; i++) { + int32_t delta = lineBufL0[i] - ((lineBufL2[i] + lineBufL1[i] + 2) >> 2); + lineBufH1[i] = lineBufL1[i] + ((delta + lineBufH0[i]) >> 1); + lineBufH2[i] = delta; + } + if (wavelet->curLine >= wavelet->height - 3 && wavelet->height & 1) { + wavelet->curH += 3; + wavelet->curLine += 3; + wavelet->fltTapH = (wavelet->fltTapH + 3) % 5; + } else { + wavelet->curH += 2; + wavelet->curLine += 2; + wavelet->fltTapH = (wavelet->fltTapH + 2) % 5; + } + } + + return 0; +} + +int crxIdwt53FilterInitialize(CrxPlaneComp* comp, int32_t level, + CrxQStep* qStep) { + if (level == 0) + return 0; + + for (int curLevel = 0, curBand = 0; curLevel < level; + curLevel++, curBand += 3) { + CrxQStep* qStepLevel = qStep ? qStep + curLevel : 0; + CrxWaveletTransform* wavelet = comp->wvltTransform + curLevel; + if (curLevel) + wavelet[0].subband0Buf = crxIdwt53FilterGetLine(comp, curLevel - 1); + else if (crxDecodeLineWithIQuantization(comp->subBands + curBand, + qStepLevel)) + return -1; + + int32_t* lineBufH0 = wavelet->lineBuf[wavelet->fltTapH + 3]; + if (wavelet->height > 1) { + if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 1, + qStepLevel) || + crxDecodeLineWithIQuantization(comp->subBands + curBand + 2, + qStepLevel) || + crxDecodeLineWithIQuantization(comp->subBands + curBand + 3, + qStepLevel)) + return -1; + + int32_t* lineBufL0 = wavelet->lineBuf[0]; + int32_t* lineBufL1 = wavelet->lineBuf[1]; + int32_t* lineBufL2 = wavelet->lineBuf[2]; + + if (comp->tileFlag & E_HAS_TILES_ON_THE_TOP) { + crxHorizontal53(lineBufL0, wavelet->lineBuf[1], wavelet, + comp->tileFlag); + if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 3, + qStepLevel) || + crxDecodeLineWithIQuantization(comp->subBands + curBand + 2, + qStepLevel)) + return -1; + + int32_t* band2Buf = wavelet->subband2Buf; + int32_t* band3Buf = wavelet->subband3Buf; + + // process L band + if (wavelet->width <= 1) + lineBufL2[0] = band2Buf[0]; + else { + if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT) { + lineBufL2[0] = band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + ++band3Buf; + } else + lineBufL2[0] = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + + ++band2Buf; + + for (int i = 0; i < wavelet->width - 3; i += 2) { + int32_t delta = + band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1); + lineBufL2[2] = delta; + + ++band2Buf; + ++band3Buf; + lineBufL2 += 2; + } + if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + int32_t delta = + band2Buf[0] - ((band3Buf[0] + band3Buf[1] + 2) >> 2); + lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1); + if (wavelet->width & 1) + lineBufL2[2] = delta; + } else if (wavelet->width & 1) { + int32_t delta = band2Buf[0] - ((band3Buf[0] + 1) >> 1); + + lineBufL2[1] = band3Buf[0] + ((lineBufL2[0] + delta) >> 1); + lineBufL2[2] = delta; + } else { + lineBufL2[1] = band3Buf[0] + lineBufL2[0]; + } + } + + // process H band + for (int32_t i = 0; i < wavelet->width; i++) + lineBufH0[i] = + lineBufL0[i] - ((lineBufL1[i] + lineBufL2[i] + 2) >> 2); + } else { + crxHorizontal53(lineBufL0, wavelet->lineBuf[2], wavelet, + comp->tileFlag); + for (int i = 0; i < wavelet->width; i++) + lineBufH0[i] = lineBufL0[i] - ((lineBufL2[i] + 1) >> 1); + } + + if (crxIdwt53FilterDecode(comp, curLevel, qStep) || + crxIdwt53FilterTransform(comp, curLevel)) + return -1; + } else { + if (crxDecodeLineWithIQuantization(comp->subBands + curBand + 1, + qStepLevel)) + return -1; + + int32_t* band0Buf = wavelet->subband0Buf; + int32_t* band1Buf = wavelet->subband1Buf; + + // process H band + if (wavelet->width <= 1) + lineBufH0[0] = band0Buf[0]; + else { + if (comp->tileFlag & E_HAS_TILES_ON_THE_LEFT) { + lineBufH0[0] = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + ++band1Buf; + } else + lineBufH0[0] = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + + ++band0Buf; + + for (int i = 0; i < wavelet->width - 3; i += 2) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1); + lineBufH0[2] = delta; + + ++band0Buf; + ++band1Buf; + lineBufH0 += 2; + } + + if (comp->tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + band1Buf[1] + 2) >> 2); + lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1); + lineBufH0[2] = delta; + } else if (wavelet->width & 1) { + int32_t delta = band0Buf[0] - ((band1Buf[0] + 1) >> 1); + lineBufH0[1] = band1Buf[0] + ((lineBufH0[0] + delta) >> 1); + lineBufH0[2] = delta; + } else { + lineBufH0[1] = band1Buf[0] + lineBufH0[0]; + } + } + ++wavelet->curLine; + ++wavelet->curH; + wavelet->fltTapH = (wavelet->fltTapH + 1) % 5; + } + } + + return 0; +} + +void crxFreeSubbandData(CrxImage* image, CrxPlaneComp* comp) { + if (comp->compBuf) { + free(comp->compBuf); + comp->compBuf = 0; + } + + if (!comp->subBands) + return; + + for (int32_t i = 0; i < image->subbandCount; i++) { + if (comp->subBands[i].bandParam) { + free(comp->subBands[i].bandParam); + comp->subBands[i].bandParam = 0LL; + } + + comp->subBands[i].bandBuf = 0; + comp->subBands[i].bandSize = 0; + } +} + +void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, + int plane = 0, int32_t* lineData = 0, + int lineLength = 0) { + if (lineData) { + uint64_t rawOffset = 4 * img->planeWidth * imageRow + 2 * imageCol; + if (img->encType == 1) { + int32_t maxVal = 1 << (img->nBits - 1); + int32_t minVal = -maxVal; + --maxVal; + for (int i = 0; i < lineLength; i++) + img->outBufs[plane][rawOffset + 2 * i] = + _constrain(lineData[i], minVal, maxVal); + } else if (img->encType == 3) { + // copy to intermediate planeBuf + rawOffset = plane * img->planeWidth * img->planeHeight + + img->planeWidth * imageRow + imageCol; + for (int i = 0; i < lineLength; i++) + img->planeBuf[rawOffset + i] = lineData[i]; + } else if (img->nPlanes == 4) { + int32_t median = 1 << (img->nBits - 1); + int32_t maxVal = (1 << img->nBits) - 1; + for (int i = 0; i < lineLength; i++) + img->outBufs[plane][rawOffset + 2 * i] = + _constrain(median + lineData[i], 0, maxVal); + } else if (img->nPlanes == 1) { + int32_t maxVal = (1 << img->nBits) - 1; + int32_t median = 1 << (img->nBits - 1); + rawOffset = img->planeWidth * imageRow + imageCol; + for (int i = 0; i < lineLength; i++) + img->outBufs[0][rawOffset + i] = + _constrain(median + lineData[i], 0, maxVal); + } + } else if (img->encType == 3 && img->planeBuf) { + int32_t planeSize = img->planeWidth * img->planeHeight; + int16_t* plane0 = img->planeBuf + imageRow * img->planeWidth; + int16_t* plane1 = plane0 + planeSize; + int16_t* plane2 = plane1 + planeSize; + int16_t* plane3 = plane2 + planeSize; + + int32_t median = 1 << (img->nBits - 1) << 10; + int32_t maxVal = (1 << img->nBits) - 1; + uint32_t rawLineOffset = 4 * img->planeWidth * imageRow; + + // for this stage - all except imageRow is ignored + for (int i = 0; i < img->planeWidth; i++) { + int32_t gr = + median + (plane0[i] << 10) - 168 * plane1[i] - 585 * plane3[i]; + int32_t val = 0; + if (gr < 0) + gr = -(((_abs(gr) + 512) >> 9) & ~1); + else + gr = ((_abs(gr) + 512) >> 9) & ~1; + + // Essentially R = round(median + P0 + 1.474*P3) + val = (median + (plane0[i] << 10) + 1510 * plane3[i] + 512) >> 10; + img->outBufs[0][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + // Essentially G1 = round(median + P0 + P2 - 0.164*P1 - 0.571*P3) + val = (plane2[i] + gr + 1) >> 1; + img->outBufs[1][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + // Essentially G2 = round(median + P0 - P2 - 0.164*P1 - 0.571*P3) + val = (gr - plane2[i] + 1) >> 1; + img->outBufs[2][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + // Essentially B = round(median + P0 + 1.881*P1) + val = (median + (plane0[i] << 10) + 1927 * plane1[i] + 512) >> 10; + img->outBufs[3][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + } + } +} + +int crxParamInit(CrxImage* img, CrxBandParam** param, + uint64_t subbandMdatOffset, uint64_t subbandDataSize, + uint32_t subbandWidth, uint32_t subbandHeight, + bool supportsPartial, uint32_t roundedBitsMask) { + int32_t progrDataSize = supportsPartial ? 0 : sizeof(int32_t) * subbandWidth; + int32_t paramLength = 2 * subbandWidth + 4; + uint8_t* paramBuf = 0; + + paramBuf = (uint8_t*)calloc( + 1, sizeof(CrxBandParam) + sizeof(int32_t) * paramLength + progrDataSize); + + if (!paramBuf) + return -1; + + *param = (CrxBandParam*)paramBuf; + + paramBuf += sizeof(CrxBandParam); + + (*param)->paramData = (int32_t*)paramBuf; + (*param)->nonProgrData = + progrDataSize ? (*param)->paramData + paramLength : 0; + (*param)->subbandWidth = subbandWidth; + (*param)->subbandHeight = subbandHeight; + (*param)->roundedBits = 0; + (*param)->curLine = 0; + (*param)->roundedBitsMask = roundedBitsMask; + (*param)->supportsPartial = supportsPartial; + (*param)->bitStream.bitData = 0; + (*param)->bitStream.bitsLeft = 0; + (*param)->bitStream.mdatSize = subbandDataSize; + (*param)->bitStream.curPos = 0; + (*param)->bitStream.curBufSize = 0; + (*param)->bitStream.curBufOffset = subbandMdatOffset; + (*param)->bitStream.crxRawData = img->crxRawData; + + crxFillBuffer(&(*param)->bitStream); + + return 0; +} + +int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, + const CrxTile* tile, uint32_t mdatOffset) { + long compDataSize = 0; + long waveletDataOffset = 0; + long compCoeffDataOffset = 0; + int32_t toSubbands = 3 * img->levels + 1; + int32_t transformWidth = 0; + + CrxSubband* subbands = planeComp->subBands; + + // calculate sizes + for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++) { + subbands[subbandNum].bandSize = + subbands[subbandNum].width * sizeof(int32_t); // 4bytes + compDataSize += subbands[subbandNum].bandSize; + } + + if (img->levels) { + int32_t encLevels = img->levels ? img->levels : 1; + waveletDataOffset = (compDataSize + 7) & ~7; + compDataSize = + (sizeof(CrxWaveletTransform) * encLevels + waveletDataOffset + 7) & ~7; + compCoeffDataOffset = compDataSize; + + // calc wavelet line buffer sizes (always at one level up from current) + for (int level = 0; level < img->levels; ++level) + if (level < img->levels - 1) + compDataSize += 8 * sizeof(int32_t) * + planeComp->subBands[3 * (level + 1) + 2].width; + else + compDataSize += 8 * sizeof(int32_t) * tile->width; + } + + // buffer allocation + planeComp->compBuf = (uint8_t*)malloc(compDataSize); + + if (!planeComp->compBuf) + return -1; + + // subbands buffer and sizes initialisation + uint64_t subbandMdatOffset = img->mdatOffset + mdatOffset; + uint8_t* subbandBuf = planeComp->compBuf; + + for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++) { + subbands[subbandNum].bandBuf = subbandBuf; + subbandBuf += subbands[subbandNum].bandSize; + subbands[subbandNum].mdatOffset = + subbandMdatOffset + subbands[subbandNum].dataOffset; + } + + // wavelet data initialisation + if (img->levels) { + CrxWaveletTransform* waveletTransforms = + (CrxWaveletTransform*)(planeComp->compBuf + waveletDataOffset); + int32_t* paramData = (int32_t*)(planeComp->compBuf + compCoeffDataOffset); + + planeComp->wvltTransform = waveletTransforms; + waveletTransforms[0].subband0Buf = (int32_t*)subbands->bandBuf; + + for (int level = 0; level < img->levels; ++level) { + int32_t band = 3 * level + 1; + + if (level >= img->levels - 1) { + waveletTransforms[level].height = tile->height; + transformWidth = tile->width; + } else { + waveletTransforms[level].height = subbands[band + 3].height; + transformWidth = subbands[band + 4].width; + } + waveletTransforms[level].width = transformWidth; + waveletTransforms[level].lineBuf[0] = paramData; + waveletTransforms[level].lineBuf[1] = + waveletTransforms[level].lineBuf[0] + transformWidth; + waveletTransforms[level].lineBuf[2] = + waveletTransforms[level].lineBuf[1] + transformWidth; + waveletTransforms[level].lineBuf[3] = + waveletTransforms[level].lineBuf[2] + transformWidth; + waveletTransforms[level].lineBuf[4] = + waveletTransforms[level].lineBuf[3] + transformWidth; + waveletTransforms[level].lineBuf[5] = + waveletTransforms[level].lineBuf[4] + transformWidth; + waveletTransforms[level].lineBuf[6] = + waveletTransforms[level].lineBuf[5] + transformWidth; + waveletTransforms[level].lineBuf[7] = + waveletTransforms[level].lineBuf[6] + transformWidth; + waveletTransforms[level].curLine = 0; + waveletTransforms[level].curH = 0; + waveletTransforms[level].fltTapH = 0; + waveletTransforms[level].subband1Buf = (int32_t*)subbands[band].bandBuf; + waveletTransforms[level].subband2Buf = + (int32_t*)subbands[band + 1].bandBuf; + waveletTransforms[level].subband3Buf = + (int32_t*)subbands[band + 2].bandBuf; + + paramData = waveletTransforms[level].lineBuf[7] + transformWidth; + } + } + + // decoding params and bitstream initialisation + for (int32_t subbandNum = 0; subbandNum < toSubbands; subbandNum++) { + if (subbands[subbandNum].dataSize) { + bool supportsPartial = false; + uint32_t roundedBitsMask = 0; + + if (planeComp->supportsPartial && subbandNum == 0) { + roundedBitsMask = planeComp->roundedBitsMask; + supportsPartial = true; + } + if (crxParamInit(img, &subbands[subbandNum].bandParam, + subbands[subbandNum].mdatOffset, + subbands[subbandNum].dataSize, + subbands[subbandNum].width, subbands[subbandNum].height, + supportsPartial, roundedBitsMask)) + return -1; + } + } + + return 0; +} + +int CrxDecompressor::crxDecodePlane(void* p, uint32_t planeNumber) { + CrxImage* img = (CrxImage*)p; + int imageRow = 0; + for (int tRow = 0; tRow < img->tileRows; tRow++) { + int imageCol = 0; + for (int tCol = 0; tCol < img->tileCols; tCol++) { + CrxTile* tile = img->tiles + tRow * img->tileCols + tCol; + CrxPlaneComp* planeComp = tile->comps + planeNumber; + uint64_t tileMdatOffset = tile->dataOffset + tile->mdatQPDataSize + + tile->mdatExtraSize + planeComp->dataOffset; + + // decode single tile + if (crxSetupSubbandData(img, planeComp, tile, tileMdatOffset)) + return -1; + + if (img->levels) { + if (crxIdwt53FilterInitialize(planeComp, img->levels, tile->qStep)) + return -1; + for (int i = 0; i < tile->height; ++i) { + if (crxIdwt53FilterDecode(planeComp, img->levels - 1, tile->qStep) || + crxIdwt53FilterTransform(planeComp, img->levels - 1)) + return -1; + int32_t* lineData = + crxIdwt53FilterGetLine(planeComp, img->levels - 1); + crxConvertPlaneLine(img, imageRow + i, imageCol, planeNumber, + lineData, tile->width); + } + } else { + // we have the only subband in this case + if (!planeComp->subBands->dataSize) { + memset(planeComp->subBands->bandBuf, 0, + planeComp->subBands->bandSize); + return 0; + } + + for (int i = 0; i < tile->height; ++i) { + if (crxDecodeLine(planeComp->subBands->bandParam, + planeComp->subBands->bandBuf)) + return -1; + int32_t* lineData = (int32_t*)planeComp->subBands->bandBuf; + crxConvertPlaneLine(img, imageRow + i, imageCol, planeNumber, + lineData, tile->width); + } + } + imageCol += tile->width; + } + imageRow += img->tiles[tRow * img->tileCols].height; + } + + return 0; +} + +uint32_t crxReadQP(CrxBitstream* bitStrm, int32_t kParam) { + uint32_t qp = crxBitstreamGetZeros(bitStrm); + if (qp >= 23) + qp = crxBitstreamGetBits(bitStrm, 8); + else if (kParam) + qp = crxBitstreamGetBits(bitStrm, kParam) | (qp << kParam); + + return qp; +} + +void crxDecodeGolombTop(CrxBitstream* bitStrm, int32_t width, int32_t* lineBuf, + int32_t* kParam) { + lineBuf[0] = 0; + while (width-- > 0) { + lineBuf[1] = lineBuf[0]; + uint32_t qp = crxReadQP(bitStrm, *kParam); + lineBuf[1] += -(qp & 1) ^ (qp >> 1); + *kParam = crxPredictKParameter(*kParam, qp, 7); + ++lineBuf; + } + lineBuf[1] = lineBuf[0] + 1; +} + +void crxDecodeGolombNormal(CrxBitstream* bitStrm, int32_t width, + int32_t* lineBuf0, int32_t* lineBuf1, + int32_t* kParam) { + lineBuf1[0] = lineBuf0[1]; + int32_t deltaH = lineBuf0[1] - lineBuf0[0]; + while (width-- > 0) { + lineBuf1[1] = crxPrediction(lineBuf1[0], lineBuf0[1], deltaH, + lineBuf0[0] - lineBuf1[0]); + uint32_t qp = crxReadQP(bitStrm, *kParam); + lineBuf1[1] += -(qp & 1) ^ (qp >> 1); + if (width) { + deltaH = lineBuf0[2] - lineBuf0[1]; + *kParam = crxPredictKParameter(*kParam, (qp + 2 * _abs(deltaH)) >> 1, 7); + ++lineBuf0; + } else + *kParam = crxPredictKParameter(*kParam, qp, 7); + ++lineBuf1; + } + lineBuf1[1] = lineBuf1[0] + 1; +} + +int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, + uint32_t totalQP) { + if (img->levels > 3 || img->levels < 1) + return -1; + int qpWidth = (tile->width >> 3) + ((tile->width & 7) != 0); + int qpHeight = (tile->height >> 1) + (tile->height & 1); + int qpHeight4 = (tile->height >> 2) + ((tile->height & 3) != 0); + int qpHeight8 = (tile->height >> 3) + ((tile->height & 7) != 0); + uint32_t totalHeight = qpHeight; + if (img->levels > 1) + totalHeight += qpHeight4; + if (img->levels > 2) + totalHeight += qpHeight8; + + tile->qStep = (CrxQStep*)malloc(totalHeight * qpWidth * sizeof(uint32_t) + + img->levels * sizeof(CrxQStep)); + + if (!tile->qStep) + return -1; + uint32_t* qStepTbl = (uint32_t*)(tile->qStep + img->levels); + CrxQStep* qStep = tile->qStep; + uint8_t curr_level = img->levels; + while (curr_level > 0) { + switch (curr_level) { + case 3: + qStep->qStepTbl = qStepTbl; + qStep->width = qpWidth; + qStep->height = qpHeight8; + for (int qpRow = 0; qpRow < qpHeight8; ++qpRow) { + int row0Idx = qpWidth * _min(4 * qpRow, qpHeight - 1); + int row1Idx = qpWidth * _min(4 * qpRow + 1, qpHeight - 1); + int row2Idx = qpWidth * _min(4 * qpRow + 2, qpHeight - 1); + int row3Idx = qpWidth * _min(4 * qpRow + 3, qpHeight - 1); + + for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl) { + int32_t quantVal = qpTable[row0Idx++] + qpTable[row1Idx++] + + qpTable[row2Idx++] + qpTable[row3Idx++]; + // not sure about this nonsense - why is it not just avg like with 2 + // levels? + quantVal = ((quantVal < 0) * 3 + quantVal) >> 2; + if (quantVal / 6 >= 6) + *qStepTbl = q_step_tbl[quantVal % 6] * (1 << (quantVal / 6 + 26)); + else + *qStepTbl = q_step_tbl[quantVal % 6] >> (6 - quantVal / 6); + } + } + // continue to the next level - we always decode all levels + ++qStep; + --curr_level; + break; + case 2: + qStep->qStepTbl = qStepTbl; + qStep->width = qpWidth; + qStep->height = qpHeight4; + for (int qpRow = 0; qpRow < qpHeight4; ++qpRow) { + int row0Idx = qpWidth * _min(2 * qpRow, qpHeight - 1); + int row1Idx = qpWidth * _min(2 * qpRow + 1, qpHeight - 1); + + for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl) { + int32_t quantVal = (qpTable[row0Idx++] + qpTable[row1Idx++]) / 2; + if (quantVal / 6 >= 6) + *qStepTbl = q_step_tbl[quantVal % 6] * (1 << (quantVal / 6 + 26)); + else + *qStepTbl = q_step_tbl[quantVal % 6] >> (6 - quantVal / 6); + } + } + // continue to the next level - we always decode all levels + ++qStep; + --curr_level; + break; + case 1: + qStep->qStepTbl = qStepTbl; + qStep->width = qpWidth; + qStep->height = qpHeight; + for (int qpRow = 0; qpRow < qpHeight; ++qpRow) + for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl, ++qpTable) + if (*qpTable / 6 >= 6) + *qStepTbl = q_step_tbl[*qpTable % 6] * (1 << (*qpTable / 6 + 26)); + else + *qStepTbl = q_step_tbl[*qpTable % 6] >> (6 - *qpTable / 6); + + --curr_level; + break; + default: + break; // oops, more then 3 levels? + } + } + return 0; +} + +inline void crxSetupSubbandIdx(const IsoMCanonCmp1Box* hdr, CrxImage* img, + CrxSubband* band, int level, short colStartIdx, + short bandWidthExCoef, short rowStartIdx, + short bandHeightExCoef) { + if (hdr->version == 0x200) { + band->rowStartAddOn = rowStartIdx; + band->rowEndAddOn = bandHeightExCoef; + band->colStartAddOn = colStartIdx; + band->colEndAddOn = bandWidthExCoef; + band->levelShift = 3 - level; + } else { + band->rowStartAddOn = 0; + band->rowEndAddOn = 0; + band->colStartAddOn = 0; + band->colEndAddOn = 0; + band->levelShift = 0; + } +} + +int crxProcessSubbands(const IsoMCanonCmp1Box* hdr, CrxImage* img, + CrxTile* tile, CrxPlaneComp* comp) { + CrxSubband* band = comp->subBands + img->subbandCount - 1; // set to last band + uint32_t bandHeight = tile->height; + uint32_t bandWidth = tile->width; + int32_t bandWidthExCoef = 0; + int32_t bandHeightExCoef = 0; + if (img->levels) { + // Build up subband sequences to crxDecode to a level in a header + + // Coefficient structure is a bit unclear and convoluted: + // 3 levels max - 8 groups (for tile width rounded to 8 bytes) + // of 3 band per level 4 sets of coefficients for each + int32_t* rowExCoef = + exCoefNumTbl + 0x30 * (img->levels - 1) + 6 * (tile->width & 7); + int32_t* colExCoef = + exCoefNumTbl + 0x30 * (img->levels - 1) + 6 * (tile->height & 7); + for (int level = 0; level < img->levels; ++level) { + int32_t widthOddPixel = bandWidth & 1; + int32_t heightOddPixel = bandHeight & 1; + bandWidth = (widthOddPixel + bandWidth) >> 1; + bandHeight = (heightOddPixel + bandHeight) >> 1; + + int32_t bandWidthExCoef0 = 0; + int32_t bandWidthExCoef1 = 0; + int32_t bandHeightExCoef0 = 0; + int32_t bandHeightExCoef1 = 0; + int32_t colStartIdx = 0; + int32_t rowStartIdx = 0; + if (tile->tileFlag & E_HAS_TILES_ON_THE_RIGHT) { + bandWidthExCoef0 = rowExCoef[2 * level]; + bandWidthExCoef1 = rowExCoef[2 * level + 1]; + } + if (tile->tileFlag & E_HAS_TILES_ON_THE_LEFT) { + ++bandWidthExCoef0; + colStartIdx = 1; + } + + if (tile->tileFlag & E_HAS_TILES_ON_THE_BOTTOM) { + bandHeightExCoef0 = colExCoef[2 * level]; + bandHeightExCoef1 = colExCoef[2 * level + 1]; + } + if (tile->tileFlag & E_HAS_TILES_ON_THE_TOP) { + ++bandHeightExCoef0; + rowStartIdx = 1; + } + + band[0].width = bandWidth + bandWidthExCoef0 - widthOddPixel; + band[0].height = bandHeight + bandHeightExCoef0 - heightOddPixel; + crxSetupSubbandIdx(hdr, img, band, level + 1, colStartIdx, + bandWidthExCoef0 - colStartIdx, rowStartIdx, + bandHeightExCoef0 - rowStartIdx); + + band[-1].width = bandWidth + bandWidthExCoef1; + band[-1].height = bandHeight + bandHeightExCoef0 - heightOddPixel; + + crxSetupSubbandIdx(hdr, img, band - 1, level + 1, 0, bandWidthExCoef1, + rowStartIdx, bandHeightExCoef0 - rowStartIdx); + + band[-2].width = bandWidth + bandWidthExCoef0 - widthOddPixel; + band[-2].height = bandHeight + bandHeightExCoef1; + crxSetupSubbandIdx(hdr, img, band - 2, level + 1, colStartIdx, + bandWidthExCoef0 - colStartIdx, 0, bandHeightExCoef1); + + band -= 3; + } + bandWidthExCoef = bandHeightExCoef = 0; + if (tile->tileFlag & E_HAS_TILES_ON_THE_RIGHT) + bandWidthExCoef = rowExCoef[2 * img->levels - 1]; + if (tile->tileFlag & E_HAS_TILES_ON_THE_BOTTOM) + bandHeightExCoef = colExCoef[2 * img->levels - 1]; + } + band->width = bandWidthExCoef + bandWidth; + band->height = bandHeightExCoef + bandHeight; + if (img->levels) + crxSetupSubbandIdx(hdr, img, band, img->levels, 0, bandWidthExCoef, 0, + bandHeightExCoef); + + return 0; +} + +int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, + CrxTile* tile, CrxPlaneComp* comp, + const uint8_t** subbandMdatPtr, int32_t* mdatSize) { + if (!img->subbandCount) + return 0; + int32_t subbandOffset = 0; + CrxSubband* band = comp->subBands; + for (int curSubband = 0; curSubband < img->subbandCount; + curSubband++, band++) { + if (*mdatSize < 4) + return -1; + + int hdrSign = sgetn(2, *subbandMdatPtr); + int hdrSize = sgetn(2, *subbandMdatPtr + 2); + if (*mdatSize < hdrSize + 4) + return -1; + if ((hdrSign != 0xFF03 || hdrSize != 8) && + (hdrSign != 0xFF13 || hdrSize != 16)) + return -1; + + int32_t subbandSize = sgetn(4, *subbandMdatPtr + 4); + + if (curSubband != ((*subbandMdatPtr)[8] & 0xF0) >> 4) { + band->dataSize = subbandSize; + return -1; + } + + band->dataOffset = subbandOffset; + band->kParam = 0; + band->bandParam = 0; + band->bandBuf = 0; + band->bandSize = 0; + + if (hdrSign == 0xFF03) { + // old header + uint32_t bitData = sgetn(4, *subbandMdatPtr + 8); + band->dataSize = subbandSize - (bitData & 0x7FFFF); + band->supportsPartial = bitData & 0x8000000; + band->qParam = (bitData >> 19) & 0xFF; + band->qStepBase = 0; + band->qStepMult = 0; + } else { + // new header + if (sgetn(2, *subbandMdatPtr + 8) & 0xFFF) + // partial and qParam are not supported + return -1; + if (sgetn(2, *subbandMdatPtr + 18)) + // new header terninated by 2 zero bytes + return -1; + band->supportsPartial = false; + band->qParam = 0; + band->dataSize = subbandSize - sgetn(2, *subbandMdatPtr + 16); + band->qStepBase = sgetn(4, *subbandMdatPtr + 12); + ; + band->qStepMult = sgetn(2, *subbandMdatPtr + 10); + ; + } + + subbandOffset += subbandSize; + + *subbandMdatPtr += hdrSize + 4; + *mdatSize -= hdrSize + 4; + } + + return 0; +} + +int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { + int nTiles = img->tileRows * img->tileCols; + + if (!nTiles) + ThrowRDE("Crx decompression error"); + + if (!img->tiles) { + img->tiles = (CrxTile*)calloc( + sizeof(CrxTile) * nTiles + + sizeof(CrxPlaneComp) * nTiles * img->nPlanes + + sizeof(CrxSubband) * nTiles * img->nPlanes * img->subbandCount, + 1); + + if (!img->tiles) + ThrowRDE("Crx decompression error"); + + // memory areas in allocated chunk + CrxTile* tile = img->tiles; + CrxPlaneComp* comps = (CrxPlaneComp*)(tile + nTiles); + CrxSubband* bands = (CrxSubband*)(comps + img->nPlanes * nTiles); + + for (int curTile = 0; curTile < nTiles; curTile++, tile++) { + tile->tileFlag = 0; // tile neighbouring flags + tile->tileNumber = curTile; + tile->tileSize = 0; + tile->comps = comps + curTile * img->nPlanes; + + if ((curTile + 1) % img->tileCols) { + // not the last tile in a tile row + tile->width = hdr->tileWidth; + if (img->tileCols > 1) { + tile->tileFlag = E_HAS_TILES_ON_THE_RIGHT; + if (curTile % img->tileCols) + // not the first tile in tile row + tile->tileFlag |= E_HAS_TILES_ON_THE_LEFT; + } + } else { + // last tile in a tile row + tile->width = img->planeWidth - hdr->tileWidth * (img->tileCols - 1); + if (img->tileCols > 1) + tile->tileFlag = E_HAS_TILES_ON_THE_LEFT; + } + if (curTile < nTiles - img->tileCols) { + // in first tile row + tile->height = hdr->tileHeight; + if (img->tileRows > 1) { + tile->tileFlag |= E_HAS_TILES_ON_THE_BOTTOM; + if (curTile >= img->tileCols) + tile->tileFlag |= E_HAS_TILES_ON_THE_TOP; + } + } else { + // non first tile row + tile->height = img->planeHeight - hdr->tileHeight * (img->tileRows - 1); + if (img->tileRows > 1) + tile->tileFlag |= E_HAS_TILES_ON_THE_TOP; + } + if (img->nPlanes) { + CrxPlaneComp* comp = tile->comps; + CrxSubband* band = bands + curTile * img->nPlanes * img->subbandCount; + + for (int curComp = 0; curComp < img->nPlanes; curComp++, comp++) { + comp->compNumber = curComp; + comp->supportsPartial = true; + comp->tileFlag = tile->tileFlag; + comp->subBands = band; + comp->compBuf = 0; + comp->wvltTransform = 0; + if (img->subbandCount) { + for (int curBand = 0; curBand < img->subbandCount; + curBand++, band++) { + band->supportsPartial = false; + band->qParam = 4; + band->bandParam = 0; + band->dataSize = 0; + } + } + } + } + } + } + + uint32_t tileOffset = 0; + Buffer mdatHdr = img->crxRawData.getSubView(0, img->mdatHdrSize); + int32_t dataSize = mdatHdr.getSize(); + const uint8_t* dataPtr = mdatHdr.getData(0, dataSize); + CrxTile* tile = img->tiles; + + for (int curTile = 0; curTile < nTiles; ++curTile, ++tile) { + if (dataSize < 4) + ThrowRDE("Crx decompression error"); + + int hdrSign = sgetn(2, dataPtr); + int hdrSize = sgetn(2, dataPtr + 2); + if ((hdrSign != 0xFF01 || hdrSize != 8) && + (hdrSign != 0xFF11 || (hdrSize != 8 && hdrSize != 16))) + ThrowRDE("Crx decompression error"); + if (dataSize < hdrSize + 4) + ThrowRDE("Crx decompression error"); + int tailSign = sgetn(2, dataPtr + 10); + if ((hdrSize == 8 && tailSign) || (hdrSize == 16 && tailSign != 0x4000)) + ThrowRDE("Crx decompression error"); + if (sgetn(2, dataPtr + 8) != (unsigned)curTile) + ThrowRDE("Crx decompression error"); + + dataSize -= hdrSize + 4; + + tile->tileSize = sgetn(4, dataPtr + 4); + tile->dataOffset = tileOffset; + tile->qStep = 0; + if (hdrSize == 16) { + // extended header data - terminated by 0 bytes + if (sgetn(2, dataPtr + 18) != 0) + return -1; + tile->hasQPData = true; + tile->mdatQPDataSize = sgetn(4, dataPtr + 12); + tile->mdatExtraSize = sgetn(2, dataPtr + 16); + } else { + tile->hasQPData = false; + tile->mdatQPDataSize = 0; + tile->mdatExtraSize = 0; + } + + dataPtr += hdrSize + 4; + tileOffset += tile->tileSize; + + uint32_t compOffset = 0; + CrxPlaneComp* comp = tile->comps; + + for (int compNum = 0; compNum < img->nPlanes; ++compNum, ++comp) { + if (dataSize < 0xC) + ThrowRDE("Crx decompression error"); + hdrSign = sgetn(2, dataPtr); + hdrSize = sgetn(2, dataPtr + 2); + if ((hdrSign != 0xFF02 && hdrSign != 0xFF12) || hdrSize != 8) + ThrowRDE("Crx decompression error"); + if (compNum != dataPtr[8] >> 4) + ThrowRDE("Crx decompression error"); + if (sgetn(3, dataPtr + 9) != 0) + ThrowRDE("Crx decompression error"); + + comp->compSize = sgetn(4, dataPtr + 4); + + int32_t compHdrRoundedBits = (dataPtr[8] >> 1) & 3; + comp->supportsPartial = (dataPtr[8] & 8) != 0; + + comp->dataOffset = compOffset; + comp->tileFlag = tile->tileFlag; + + compOffset += comp->compSize; + dataSize -= 0xC; + dataPtr += 0xC; + + comp->roundedBitsMask = 0; + + if (compHdrRoundedBits) { + if (img->levels || !comp->supportsPartial) + ThrowRDE("Crx decompression error"); + + comp->roundedBitsMask = 1 << (compHdrRoundedBits - 1); + } + + if (crxReadSubbandHeaders(hdr, img, tile, comp, &dataPtr, &dataSize) || + crxProcessSubbands(hdr, img, tile, comp)) + ThrowRDE("Crx decompression error"); + } + } + + if (hdr->version != 0x200) + return 0; + + tile = img->tiles; + for (int curTile = 0; curTile < nTiles; ++curTile, ++tile) { + if (tile->hasQPData) { + CrxBitstream bitStrm; + bitStrm.bitData = 0; + bitStrm.bitsLeft = 0; + bitStrm.curPos = 0; + bitStrm.curBufSize = 0; + bitStrm.mdatSize = tile->mdatQPDataSize; + bitStrm.curBufOffset = img->mdatOffset + tile->dataOffset; + bitStrm.crxRawData = img->crxRawData; + + crxFillBuffer(&bitStrm); + + unsigned int qpWidth = (tile->width >> 3) + ((tile->width & 7) != 0); + unsigned int qpHeight = (tile->height >> 1) + (tile->height & 1); + unsigned long totalQP = qpHeight * qpWidth; + + try { + std::vector qpTable(totalQP + 2 * (qpWidth + 2)); + int32_t* qpCurElem = qpTable.data(); + // 2 lines padded with extra pixels at the start and at the end + int32_t* qpLineBuf = qpTable.data() + totalQP; + int32_t kParam = 0; + for (unsigned qpRow = 0; qpRow < qpHeight; ++qpRow) { + int32_t* qpLine0 = qpRow & 1 ? qpLineBuf + qpWidth + 2 : qpLineBuf; + int32_t* qpLine1 = qpRow & 1 ? qpLineBuf : qpLineBuf + qpWidth + 2; + + if (qpRow) + crxDecodeGolombNormal(&bitStrm, qpWidth, qpLine0, qpLine1, &kParam); + else + crxDecodeGolombTop(&bitStrm, qpWidth, qpLine1, &kParam); + + for (unsigned qpCol = 0; qpCol < qpWidth; ++qpCol) + *qpCurElem++ = qpLine1[qpCol + 1] + 4; + } + + // now we read QP data - build tile QStep + if (crxMakeQStep(img, tile, qpTable.data(), totalQP)) + ThrowRDE("Crx decompression error"); + } catch (...) { + ThrowRDE("Crx decompression error"); + } + } + } + + return 0; +} + +int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, + int16_t* outBuf) { + int IncrBitTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0}; + + img->planeWidth = hdr->f_width; + img->planeHeight = hdr->f_height; + + if (hdr->tileWidth < 0x16 || hdr->tileHeight < 0x16 || + img->planeWidth > 0x7FFF || img->planeHeight > 0x7FFF) + ThrowRDE("Crx decompression error"); + + img->tileCols = (img->planeWidth + hdr->tileWidth - 1) / hdr->tileWidth; + img->tileRows = (img->planeHeight + hdr->tileHeight - 1) / hdr->tileHeight; + + if (img->tileCols > 0xFF || img->tileRows > 0xFF || + img->planeWidth - hdr->tileWidth * (img->tileCols - 1) < 0x16 || + img->planeHeight - hdr->tileHeight * (img->tileRows - 1) < 0x16) + ThrowRDE("Crx decompression error"); + + img->tiles = 0; + img->levels = hdr->imageLevels; + img->subbandCount = 3 * img->levels + 1; // 3 bands per level + one last LL + img->nPlanes = hdr->nPlanes; + img->nBits = hdr->nBits; + img->encType = hdr->encType; + img->samplePrecision = hdr->nBits + IncrBitTable[4 * hdr->encType + 2] + 1; + img->mdatOffset = hdr->mdatHdrSize; // after header, plane data follows + img->mdatHdrSize = hdr->mdatHdrSize; + img->planeBuf = 0; + img->outBufs[0] = img->outBufs[1] = img->outBufs[2] = img->outBufs[3] = 0; + + // The encoding type 3 needs all 4 planes to be decoded to generate row of + // RGGB values. It seems to be using some other colour space for raw encoding + // It is a massive buffer so ideallly it will need a different approach: + // decode planes line by line and convert single line then without + // intermediate plane buffer. At the moment though it's too many changes so + // left as is. + if (img->encType == 3 && img->nPlanes == 4 && img->nBits > 8) { + img->planeBuf = + (int16_t*)malloc(img->planeHeight * img->planeWidth * img->nPlanes * + ((img->samplePrecision + 7) >> 3)); + if (!img->planeBuf) + ThrowRDE("Crx decompression error"); + } + + int32_t rowSize = 2 * img->planeWidth; + + if (img->nPlanes == 1) + img->outBufs[0] = outBuf; + else + switch (hdr->cfaLayout) { + case 0: + // R G + // G B + img->outBufs[0] = outBuf; + img->outBufs[1] = outBuf + 1; + img->outBufs[2] = outBuf + rowSize; + img->outBufs[3] = img->outBufs[2] + 1; + break; + case 1: + // G R + // B G + img->outBufs[1] = outBuf; + img->outBufs[0] = outBuf + 1; + img->outBufs[3] = outBuf + rowSize; + img->outBufs[2] = img->outBufs[3] + 1; + break; + case 2: + // G B + // R G + img->outBufs[2] = outBuf; + img->outBufs[3] = outBuf + 1; + img->outBufs[0] = outBuf + rowSize; + img->outBufs[1] = img->outBufs[0] + 1; + break; + case 3: + // B G + // G R + img->outBufs[3] = outBuf; + img->outBufs[2] = outBuf + 1; + img->outBufs[1] = outBuf + rowSize; + img->outBufs[0] = img->outBufs[1] + 1; + break; + } + + // read header + return crxReadImageHeaders(hdr, img); +} + +int crxFreeImageData(CrxImage* img) { + CrxTile* tile = img->tiles; + int nTiles = img->tileRows * img->tileCols; + + if (img->tiles) { + for (int32_t curTile = 0; curTile < nTiles; curTile++) { + if (tile[curTile].comps) + for (int32_t curPlane = 0; curPlane < img->nPlanes; curPlane++) + crxFreeSubbandData(img, tile[curTile].comps + curPlane); + if (tile[curTile].qStep) + free(tile[curTile].qStep); + } + free(img->tiles); + img->tiles = 0; + } + + if (img->planeBuf) { + free(img->planeBuf); + img->planeBuf = 0; + } + + return 0; +} + +void CrxDecompressor::crxLoadDecodeLoop(void* img, int nPlanes) { + int results[4]; // nPlanes is always <= 4 + +#ifdef HAVE_OPENMP +#pragma omp for schedule(static) +#endif + for (int32_t plane = 0; plane < nPlanes; ++plane) + results[plane] = crxDecodePlane(img, plane); + + for (int32_t plane = 0; plane < nPlanes; ++plane) + if (results[plane]) + ThrowRDE("Crx decompression error"); +} + +void CrxDecompressor::crxConvertPlaneLineDf(void* p, int imageRow) { + crxConvertPlaneLine((CrxImage*)p, imageRow); +} + +void CrxDecompressor::crxLoadFinalizeLoopE3(void* p, int planeHeight) { +#ifdef HAVE_OPENMP +#pragma omp for schedule(static) +#endif + for (int i = 0; i < planeHeight; ++i) + crxConvertPlaneLineDf(p, i); +} + +void CrxDecompressor::decode(const IsoMCanonCmp1Box& cmp1Box, + Buffer& crxRawData) { + CrxImage img; + img.crxRawData = crxRawData; + + // Local copy because we must must modify some values for decompression + IsoMCanonCmp1Box hdr = cmp1Box; + + // Bytes required for decompression output + Buffer::size_type bufLen = + cmp1Box.f_height * cmp1Box.f_width * sizeof(uint16_t); + + // update sizes for the planes + if (hdr.nPlanes == 4) { + hdr.f_width >>= 1; + hdr.f_height >>= 1; + hdr.tileWidth >>= 1; + hdr.tileHeight >>= 1; + } + + auto storage = rawspeed::Buffer::Create(bufLen); + const rawspeed::Buffer outBuf(storage.get(), bufLen); + + if (crxSetupImageData(&hdr, &img, (int16_t*)storage.get())) { + ThrowRDE("Crx image setup failed"); + } + crxLoadDecodeLoop(&img, hdr.nPlanes); + + if (img.encType == 3) + crxLoadFinalizeLoopE3(&img, img.planeHeight); + + crxFreeImageData(&img); + + ByteStream input(DataBuffer(outBuf, Endianness::big)); + UncompressedDecompressor u(input, mRaw); + + // align output bytes + u.decodeRawUnpacked<16, Endianness::little>(cmp1Box.f_width, + cmp1Box.f_height); +} + +CrxDecompressor::CrxDecompressor(const RawImage& img) + : AbstractDecompressor(), mRaw(img) { + if (mRaw->getDataType() != TYPE_USHORT16) + ThrowRDE("Unexpected data type"); + + if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == sizeof(uint16_t)))) + ThrowRDE("Unexpected cpp: %u", mRaw->getCpp()); +} + +} // namespace rawspeed diff --git a/src/librawspeed/decompressors/CrxDecompressor.h b/src/librawspeed/decompressors/CrxDecompressor.h new file mode 100644 index 000000000..fc80717e3 --- /dev/null +++ b/src/librawspeed/decompressors/CrxDecompressor.h @@ -0,0 +1,51 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "common/RawImage.h" // for RawImage +#include "decoders/RawDecoderException.h" // for ThrowRDE +#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor +#include // for assert +#include // for uint16_t + +namespace rawspeed { + +class Buffer; +class RawImage; +class IsoMCanonCmp1Box; + +class CrxDecompressor final : public AbstractDecompressor { + RawImage mRaw; + +public: + CrxDecompressor(const RawImage& img); + + void decode(const IsoMCanonCmp1Box& cmp1Box, Buffer& crxRawData); + +private: + int crxDecodePlane(void* p, uint32_t planeNumber); + void crxLoadDecodeLoop(void* img, int nPlanes); + int crxParseImageHeader(uint8_t* cmp1TagData, int nTrack); + void crxConvertPlaneLineDf(void* p, int imageRow); + void crxLoadFinalizeLoopE3(void* p, int planeHeight); +}; + +} // namespace rawspeed From 7e23124f3dc9d76dc67d75a39e333ec4a26eaff7 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 11 May 2021 21:48:14 +0200 Subject: [PATCH 06/27] Add Cr3Decoder for CR3 raw files This decoder contains the logic to parse a CR3 ISOBMFF file. --- .../decoders/TiffDecoders/main.cpp | 1 + src/librawspeed/decoders/CMakeLists.txt | 2 + src/librawspeed/decoders/Cr3Decoder.cpp | 644 ++++++++++++++++++ src/librawspeed/decoders/Cr3Decoder.h | 359 ++++++++++ src/librawspeed/parsers/IsoMParser.cpp | 3 + 5 files changed, 1009 insertions(+) create mode 100644 src/librawspeed/decoders/Cr3Decoder.cpp create mode 100644 src/librawspeed/decoders/Cr3Decoder.h diff --git a/fuzz/librawspeed/decoders/TiffDecoders/main.cpp b/fuzz/librawspeed/decoders/TiffDecoders/main.cpp index 3aa01c7b8..5fbb89a9c 100644 --- a/fuzz/librawspeed/decoders/TiffDecoders/main.cpp +++ b/fuzz/librawspeed/decoders/TiffDecoders/main.cpp @@ -25,6 +25,7 @@ #include "common/RawspeedException.h" // for RawspeedException #include "decoders/ArwDecoder.h" // IWYU pragma: keep #include "decoders/Cr2Decoder.h" // IWYU pragma: keep +#include "decoders/Cr3Decoder.h" // IWYU pragma: keep #include "decoders/DcrDecoder.h" // IWYU pragma: keep #include "decoders/DcsDecoder.h" // IWYU pragma: keep #include "decoders/DngDecoder.h" // IWYU pragma: keep diff --git a/src/librawspeed/decoders/CMakeLists.txt b/src/librawspeed/decoders/CMakeLists.txt index 654116331..acb267543 100644 --- a/src/librawspeed/decoders/CMakeLists.txt +++ b/src/librawspeed/decoders/CMakeLists.txt @@ -5,6 +5,8 @@ FILE(GLOB SOURCES "ArwDecoder.h" "Cr2Decoder.cpp" "Cr2Decoder.h" + "Cr3Decoder.cpp" + "Cr3Decoder.h" "CrwDecoder.cpp" "CrwDecoder.h" "DcrDecoder.cpp" diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp new file mode 100644 index 000000000..be6280da8 --- /dev/null +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -0,0 +1,644 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "decoders/Cr3Decoder.h" // for Cr3Decoder +#include "decompressors/CrxDecompressor.h" // for CrxDecompressor +#include "parsers/IsoMParserException.h" // for ThrowIPE +#include "parsers/TiffParser.h" // for TiffParser + +namespace rawspeed { + +const FourCharStr IsoMBoxCanonTypes::CNCV; +const FourCharStr IsoMBoxCanonTypes::CCTP; +const FourCharStr IsoMBoxCanonTypes::CTBO; +const FourCharStr IsoMBoxCanonTypes::CMT1; +const FourCharStr IsoMBoxCanonTypes::CMT2; +const FourCharStr IsoMBoxCanonTypes::CMT3; +const FourCharStr IsoMBoxCanonTypes::CMT4; +const FourCharStr IsoMBoxCanonTypes::THMB; +const FourCharStr IsoMBoxCanonTypes::CRAW; +const FourCharStr IsoMBoxCanonTypes::CMP1; +const FourCharStr IsoMBoxCanonTypes::CDI1; +const FourCharStr IsoMBoxCanonTypes::IAD1; +const FourCharStr IsoMBoxCanonTypes::CTMD; + +const AbstractIsoMBox::UuidType CanonBoxUuid = { + 0x85, 0xc0, 0xb6, 0x87, 0x82, 0x0f, 0x11, 0xe0, + 0x81, 0x11, 0xf4, 0xce, 0x46, 0x2b, 0x6a, 0x48}; + +void IsoMCanonBox::parseBox(const AbstractIsoMBox& box) { + if (IsoMCanonCodecVersionBox::BoxType == box.boxType) { + if (cncvBox) + ThrowIPE("duplicate cncv box found."); + cncvBox = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCCTPBox::BoxType == box.boxType) { + if (cctpBox) + ThrowIPE("duplicate CCTP box found."); + cctpBox = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCTBOBox::BoxType == box.boxType) { + if (ctboBox) + ThrowIPE("duplicate CTBO box found."); + ctboBox = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCMT1Box::BoxType == box.boxType) { + if (cmt1Box) + ThrowIPE("duplicate CMT1 box found."); + cmt1Box = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCMT2Box::BoxType == box.boxType) { + if (cmt2Box) + ThrowIPE("duplicate CMT2 box found."); + cmt2Box = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCMT3Box::BoxType == box.boxType) { + if (cmt3Box) + ThrowIPE("duplicate CMT3 box found."); + cmt3Box = AbstractIsoMBox::ParseBox(box); + return; + } + if (IsoMCanonCMT4Box::BoxType == box.boxType) { + if (cmt4Box) + ThrowIPE("duplicate CMT4 box found."); + cmt4Box = AbstractIsoMBox::ParseBox(box); + return; + } + + if (IsoMCanonThumbnailBox::BoxType == box.boxType) { + if (thmbBox) + ThrowIPE("duplicate THMB box found."); + thmbBox = AbstractIsoMBox::ParseBox(box); + return; + } +} + +const std::unique_ptr& IsoMCanonBox::CNCV() const { + if (cncvBox) + return cncvBox; + else + ThrowIPE("CNCV box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CCTP() const { + if (cctpBox) + return cctpBox; + else + ThrowIPE("CCTP box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CTBO() const { + if (ctboBox) + return ctboBox; + else + ThrowIPE("CTBO box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CMT1() const { + if (cmt1Box) + return cmt1Box; + else + ThrowIPE("CMT1 box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CMT2() const { + if (cmt2Box) + return cmt2Box; + else + ThrowIPE("CMT2 box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CMT3() const { + if (cmt3Box) + return cmt3Box; + else + ThrowIPE("CMT3 box not available"); +} + +const std::unique_ptr& IsoMCanonBox::CMT4() const { + if (cmt4Box) + return cmt4Box; + else + ThrowIPE("CMT4 box not available"); +} + +const std::unique_ptr& IsoMCanonBox::THMB() const { + if (thmbBox) + return thmbBox; + else + ThrowIPE("THMB box not available"); +} + +IsoMCanonBox::operator bool() const { + if (!cncvBox) + ThrowIPE("no CNCV box found."); + if (!cctpBox) + ThrowIPE("no CCTP box found."); + if (!ctboBox) + ThrowIPE("no CTBO box found."); + if (!cmt1Box) + ThrowIPE("no CMT1 box found."); + if (!cmt2Box) + ThrowIPE("no CMT2 box found."); + if (!cmt3Box) + ThrowIPE("no CMT3 box found."); + if (!cmt4Box) + ThrowIPE("no CMT4 box found."); + + return true; // OK! +} + +IsoMCanonCodecVersionBox::IsoMCanonCodecVersionBox(const AbstractIsoMBox& base) + : IsoMBox(base) { + assert(data.getRemainSize() == 30); // Payload string is exactly 30 bytes long + auto payload = data.getBuffer(30); + compressorVersion = std::string(payload.begin(), payload.end()); + assert(data.getRemainSize() == 0); +} + +IsoMCanonCMT1Box::IsoMCanonCMT1Box(const AbstractIsoMBox& base) + : IsoMBox(base) { + NORangesSet rs; + auto payload = + DataBuffer(data.getBuffer(data.getRemainSize()), Endianness::little); + mRootIFD0 = TiffParser::parse(nullptr, payload); +} + +IsoMCanonCMT2Box::IsoMCanonCMT2Box(const AbstractIsoMBox& base) + : IsoMBox(base) { + NORangesSet rs; + auto payload = + DataBuffer(data.getBuffer(data.getRemainSize()), Endianness::little); + mRootIFD0 = TiffParser::parse(nullptr, payload); +} + +IsoMCanonCMT3Box::IsoMCanonCMT3Box(const AbstractIsoMBox& base) + : IsoMBox(base) { + NORangesSet rs; + auto payload = + DataBuffer(data.getBuffer(data.getRemainSize()), Endianness::little); + mRootIFD0 = TiffParser::parse(nullptr, payload); +} + +IsoMCanonCMT4Box::IsoMCanonCMT4Box(const AbstractIsoMBox& base) + : IsoMBox(base) { + NORangesSet rs; + auto payload = + DataBuffer(data.getBuffer(data.getRemainSize()), Endianness::little); + mRootIFD0 = TiffParser::parse(nullptr, payload); +} + +IsoMCanonTimedMetadataBox::IsoMCanonTimedMetadataBox( + const AbstractIsoMBox& base) + : IsoMBox(base) { + // Set position after box `size` and `boxtype` fields, so we + // can parse the custom SampleEntry ourself. + data.setPosition(8); + + for (auto& c : reserved1) + c = data.getByte(); + dataReferenceIndex = data.getU16(); + + const auto entryCount = data.getU32(); + + // Can't check/reserve entryCount. + std::generate_n(std::back_inserter(recDescs), entryCount, + [this]() { return RecordDesc(&data); }); + assert(recDescs.size() == entryCount); + + assert(data.getRemainSize() == 0); + + // Validate. + operator bool(); +} + +IsoMCanonTimedMetadataBox::operator bool() const { + // This CTMD box is not used for decoding, since record type and size + // are available in MDAT data for CTMD, too. + return true; // OK! +} + +IsoMCanonTimedMetadataBox::RecordDesc::RecordDesc(ByteStream* bs) { + recType = bs->getU32(); + recSize = bs->getU32(); +} + +IsoMCanonCrawBox::IsoMCanonCrawBox(const AbstractIsoMBox& base) + : IsoMBox(base) { + // Set position after box `size` and `boxtype` fields, so we + // can parse the custom SampleEntry ourself. + data.setPosition(8); + + for (auto& c : reserved1) + c = data.getByte(); + dataReferenceIndex = data.getU16(); + for (auto& c : reserved2) + c = data.getByte(); + width = data.getU16(); + height = data.getU16(); + xResolution = static_cast(data.getU16()) << 16 | data.getU16(); + yResolution = static_cast(data.getU16()) << 16 | data.getU16(); + reserved3 = data.getU32(); + reserved4 = data.getU16(); + for (auto& c : reserved5) + c = data.getByte(); + bitDepth = data.getU16(); + reserved6 = data.getU16(); + flags = data.getU16(); + formatInd = data.getU16(); + + // Change this if Canon adds more fields to CRAW box + assert(data.getPosition() == 90); + + // After fields, there are embedded boxes + cmp1Box = std::make_unique(AbstractIsoMBox(&data)); + cdi1Box = std::make_unique(AbstractIsoMBox(&data)); + cdi1Box->IsoMContainer::parse(); + // There is a 'free' box after CDI1 which we ignore + + // Validate. + operator bool(); +} + +IsoMCanonCrawBox::operator bool() const { + // For JPEG trak, CRAW has no CMP1/CDI1 boxes. But as we + // decode RAW, not JPEG, CMP1 and CDI1 are required. + if (!cmp1Box) + ThrowIPE("no CMP1 box found."); + if (!cdi1Box) + ThrowIPE("no CDI1 box found."); + + return true; // OK! +} + +const std::unique_ptr& IsoMCanonCrawBox::CMP1() const { + if (cmp1Box) + return cmp1Box; + else + ThrowIPE("CMP1 box not available"); +} + +const std::unique_ptr& IsoMCanonCrawBox::CDI1() const { + if (cdi1Box) + return cdi1Box; + else + ThrowIPE("CDI1 box not available"); +} + +IsoMCanonCmp1Box::IsoMCanonCmp1Box(const AbstractIsoMBox& base) + : IsoMBox(base) { + // Set position after box `size` and `boxtype` fields, so we + // can parse the custom SampleEntry ourself. + data.setPosition(8); + // This fields mainly used in the decoding process. + reserved1 = data.getU16(); + headerSize = data.getU16(); + assert(headerSize == 0x30); + version = data.getI16(); + versionSub = data.getI16(); + f_width = data.getI32(); + f_height = data.getI32(); + tileWidth = data.getI32(); + tileHeight = data.getI32(); + nBits = data.get(); + nPlanes = data.peek() >> 4; + cfaLayout = data.get() & 0xF; + encType = data.peek() >> 4; + imageLevels = data.get() & 0xF; + hasTileCols = data.peek() >> 7; + hasTileRows = data.get() & 1; + mdatHdrSize = data.getI32(); + // Some reserved fields, unknown. + reserved2 = data.getI32(); + for (auto& c : reserved3) + c = data.getByte(); + + // we assume this is fixed, until Canon makes CMP1 flexible + assert(data.getPosition() == 44 + 16); + // headerSize should match position + assert((data.getPosition() - 2 - 2 - 8) == headerSize); + assert(data.getRemainSize() == 0); + + // Validate. + operator bool(); +} + +IsoMCanonCmp1Box::operator bool() const { + // validation based on libraw decoder requirements + if (version != 0x100 && version != 0x200) { + ThrowRDE("Unsupported version in CMP1"); + } + if (!mdatHdrSize) { + ThrowRDE("CMP1 describes an empty MDAT header"); + } + if (encType == 1) { + if (nBits > 15) + ThrowRDE("Unknown encoding bit count in CMP1"); + } else { + if (encType && encType != 3) + ThrowRDE("Unknown encType in CMP1"); + if (nBits > 14) + ThrowRDE("Unknown encoding bit count in CMP1"); + } + if (nPlanes == 1) { + if (cfaLayout || encType || nBits != 8) + ThrowRDE("Unknown encoding parameters in CMP1"); + } else if (nPlanes != 4 || f_width & 1 || f_height & 1 || tileWidth & 1 || + tileHeight & 1 || cfaLayout > 3 || nBits == 8) + ThrowRDE("Unknown encoding parameters in CMP1"); + + if (tileWidth > f_width || tileHeight > f_height) + ThrowRDE("Unknown encoding parameters in CMP1"); + + if (imageLevels > 3 || hasTileCols > 1 || hasTileRows > 1) + ThrowRDE("Unknown encoding parameters in CMP1"); + + return true; // OK! +} + +void IsoMCanonCdi1Box::parseBox(const AbstractIsoMBox& box) { + if (IsoMCanonIad1Box::BoxType == box.boxType) { + if (iad1Box) + ThrowIPE("duplicate IAD1 box found."); + iad1Box = AbstractIsoMBox::ParseBox(box); + return; + } +} + +IsoMCanonCdi1Box::operator bool() const { + if (!iad1Box) + ThrowIPE("no IAD1 box found."); + + return true; // OK! +} + +const std::unique_ptr& IsoMCanonCdi1Box::IAD1() const { + if (iad1Box) + return iad1Box; + else + ThrowIPE("IAD1 box not available"); +} + +IsoMCanonIad1Box::IsoMCanonIad1Box(const AbstractIsoMBox& base) + : IsoMFullBox(base) { + sensorWidth = data.get(); + sensorHeight = data.get(); + reserved1 = data.get(); + ind = data.get(); + reserved2 = data.get(); + reserved3 = data.get(); + + if (2 == ind) { // ind is 2 for big images + sensorLeftBorder = data.get(); + sensorTopBorder = data.get(); + sensorRightBorder = data.get(); + sensorBottomBorder = data.get(); + data.skipBytes(2 * sizeof(uint16_t)); + sensorBlackAreaLeft = data.get(); + data.skipBytes(4 * sizeof(uint16_t)); + sensorBlackAreaTop = data.get(); + } + + writeLog(DEBUG_PRIO_WARNING, + "IAD1 sensor width: %d, height: %d, crop: %u, %u, %u, %u, black " + "area left: %u, top: %u", + sensorWidth, sensorHeight, sensorLeftBorder, sensorTopBorder, + sensorRightBorder, sensorBottomBorder, sensorBlackAreaLeft, + sensorBlackAreaTop); + + // Validate. + operator bool(); +} + +IsoMCanonIad1Box::operator bool() const { + // No fields yet to validate, IAD1 is unused for decoding. + return true; // OK! +} + +CanonTimedMetadata::CanonTimedMetadata::Record::Record(ByteStream* bs) { + assert(bs->getByteOrder() == Endianness::little); + auto origPos = bs->getPosition(); + recSize = bs->getU32(); + recType = bs->getU16(); + reserved1 = bs->get(); + reserved2 = bs->get(); + reserved3 = bs->get(); + reserved4 = bs->get(); + payload = bs->getStream(recSize - (bs->getPosition() - origPos)); +} + +CanonTimedMetadata::CanonTimedMetadata(const ByteStream* bs) : data(*bs) { + + // CTMD is little-endian, force stream to correct endianness + data.setByteOrder(Endianness::little); + + while (data.getRemainSize() > 0) { + auto rec = Record(&data); + // No record type can exists multiple times + assert(records.find(rec.recType) == records.end()); + records[rec.recType] = rec; + } + assert(data.getRemainSize() == 0); +} + +bool Cr3Decoder::isAppropriateDecoder(const IsoMRootBox& box) { + return box.ftyp()->majorBrand == FourCharStr({'c', 'r', 'x', ' '}); +} + +RawImage Cr3Decoder::decodeRawInternal() { + /* + ByteStream biggestImage; + + for (const auto& track : rootBox->moov()->tracks) { + for (const auto& chunk : track.mdia->minf->stbl->chunks) { + if (chunk->getSize() > biggestImage.getSize()) + biggestImage = *chunk; + } + } + */ + + assert(crawBox); + ByteStream biggestImage( + *rootBox->moov()->tracks[2].mdia->minf->stbl->chunks[0]); + + // Setup image dimensions + const auto& cmp1 = crawBox->CMP1(); + + mRaw->dim = iPoint2D(cmp1->f_width, cmp1->f_height); + mRaw->setCpp(1); + mRaw->createData(); + + assert(mRaw->getBpp() == 2); + + CrxDecompressor u(mRaw); + u.decode(*cmp1, biggestImage); + + return mRaw; +} + +bool Cr3Decoder::isCodecSupported(const std::string& compressorVersion) const { + if (compressorVersion == "CanonHEIF001/10.00.00/00.00.00") { + writeLog(DEBUG_PRIO_WARNING, "HEIF CNCV: '%s' is not supported", + compressorVersion.c_str()); + } + if (compressorVersion == "CanonCR3_001/01.09.00/01.00.00") { + writeLog(DEBUG_PRIO_WARNING, "Raw-burst roll CNCV: '%s' is not supported", + compressorVersion.c_str()); + } + if (compressorVersion == "CanonCRM0001/02.09.00/00.00.00") { + writeLog(DEBUG_PRIO_WARNING, "CRM movies CNCV: '%s' is not supported", + compressorVersion.c_str()); + } + + return compressorVersion == + "CanonCR3_001/00.10.00/00.00.00" // EOS R5, R6 and 1DX Mark III + // (raw) + || compressorVersion == + "CanonCR3_002/00.10.00/00.00.00" // CR3 of 1DX Mark III (craw) + || compressorVersion == + "CanonCR3_001/00.09.00/00.00.00"; // EOS R, EOS RP, M50, 250D, + // 90D, M6 Mark II, M200 +} + +void Cr3Decoder::checkSupportInternal(const CameraMetaData* meta) { + // Get Canon UUID box and parse + canonBox = + std::make_unique(rootBox->moov()->getBox(CanonBoxUuid)); + canonBox->parse(); + + // Check compressor version string + auto compressorVersion = canonBox->CNCV()->compressorVersion; + writeLog(DEBUG_PRIO_EXTRA, "Compressor Version: %s", + compressorVersion.c_str()); + if (!isCodecSupported(compressorVersion)) { + ThrowRDE("CR3 compressor version (CNCV: %s) is not supported", + compressorVersion.c_str()); + } + + // CMT1 contains a TIFF file with EXIF information + auto camId = canonBox->CMT1()->mRootIFD0->getID(); + writeLog(DEBUG_PRIO_EXTRA, "CMT1 EXIF make: %s", camId.make.c_str()); + writeLog(DEBUG_PRIO_EXTRA, "CMT1 EXIF model: %s", camId.model.c_str()); + + // Load CRAW box + auto& stsd = rootBox->moov()->tracks[2].mdia->minf->stbl->stsd; + crawBox = std::make_unique(stsd->dscs[0]); + + checkCameraSupported(meta, camId.make, camId.model, mode); +} + +void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { + const auto camId = canonBox->CMT1()->mRootIFD0->getID(); + + uint32_t iso = 0; + if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(ISOSPEEDRATINGS)) + iso = canonBox->CMT2() + ->mRootIFD0->getEntryRecursive(ISOSPEEDRATINGS) + ->getU32(); + + // Big raw image is always in track 4 + assert(rootBox->moov()->tracks.size() >= 4); + auto& track3Mdia = rootBox->moov()->tracks[3].mdia; + + // CTMD + auto& CTMD_stsd = track3Mdia->minf->stbl->stsd; + assert(CTMD_stsd->dscs.size() > 0); + + // Get Sample and rebuild a CTMD + IsoMCanonTimedMetadataBox ctmd = + IsoMCanonTimedMetadataBox(CTMD_stsd->dscs[0]); + + // CTMD MDAT + assert(track3Mdia->minf->stbl->chunks.size() > 0); + auto ctmd_chunk = track3Mdia->minf->stbl->chunks[0]; + + Buffer ctmd_chunk_buf = ctmd_chunk->getSubView(0); + + auto ctmd_recs = CanonTimedMetadata(ctmd_chunk); + + // Record 8 contains EXIF data with CANONCOLORDATA tag + auto rec8 = ctmd_recs.records[8].payload.getSubView(8); + + NORangesSet rs; + + // Rec. 8 contains TIFF data, but with corrupt IFD1 index. We + // parse it manually. + TiffRootIFD IFD_ctmd_rec8(nullptr, &rs, DataBuffer(rec8, Endianness::little), + 8); // skip TIFF header + + if (IFD_ctmd_rec8.hasEntryRecursive(CANONCOLORDATA)) { + TiffEntry* wb = IFD_ctmd_rec8.getEntryRecursive(CANONCOLORDATA); + // this entry is a big table, and different cameras store used WB in + // different parts, so find the offset, default is the most common one. + // The wb_offset values in cameras.xml are extracted from: + // https://github.com/exiftool/exiftool/blob/ceff3cbc4564e93518f3d2a2e00d8ae203ff54af/lib/Image/ExifTool/Canon.pm#L1910 + int offset = hints.get("wb_offset", 126); + + wb_coeffs[0] = static_cast(wb->getU16(offset + 0)) / 1024.0; + wb_coeffs[1] = static_cast(wb->getU16(offset + 1)) / 1024.0; + wb_coeffs[2] = 0; // GG + wb_coeffs[3] = static_cast(wb->getU16(offset + 3)) / 1024.0; + + writeLog(DEBUG_PRIO_EXTRA, "wb_coeffs:, 0: %f, 1: %f, 2: %f, 3: %f\n", + wb_coeffs[0], wb_coeffs[1], wb_coeffs[2], wb_coeffs[3]); + + } else { + writeLog(DEBUG_PRIO_EXTRA, "no wb_coeffs found"); + } + + // No CR3 camera has swapped_wb so far, but who knows... + if (hints.has("swapped_wb")) { + mRaw->metadata.wbCoeffs[0] = wb_coeffs[2]; + mRaw->metadata.wbCoeffs[1] = wb_coeffs[0]; + mRaw->metadata.wbCoeffs[2] = wb_coeffs[1]; + } else { + mRaw->metadata.wbCoeffs[0] = wb_coeffs[0]; + mRaw->metadata.wbCoeffs[1] = wb_coeffs[1]; + mRaw->metadata.wbCoeffs[2] = wb_coeffs[3]; + } + + setMetaData(meta, camId.make, camId.model, mode, iso); + + // IAD1 described sensor constraints + const auto& iad1 = crawBox->CDI1()->IAD1(); + + if (mRaw->blackAreas.empty()) { + mRaw->blackAreas.push_back(BlackArea(0, iad1->sensorBlackAreaLeft, true)); + mRaw->blackAreas.push_back(BlackArea(0, iad1->sensorBlackAreaTop, false)); + } + + if (applyCrop) { + // IAD1 sensor parameters always crop one more pixel as needed. + // We add back the missing pixels to match official specifications. + iRectangle2D new_area(iad1->sensorLeftBorder, iad1->sensorTopBorder, + iad1->sensorRightBorder - iad1->sensorLeftBorder + 1, + iad1->sensorBottomBorder - iad1->sensorTopBorder + 1); + + mRaw->subFrame(new_area); + } +} + +} // namespace rawspeed diff --git a/src/librawspeed/decoders/Cr3Decoder.h b/src/librawspeed/decoders/Cr3Decoder.h new file mode 100644 index 000000000..18b3f0055 --- /dev/null +++ b/src/librawspeed/decoders/Cr3Decoder.h @@ -0,0 +1,359 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2018 Roman Lebedev + Copyright (C) 2021 Daniel Vogelbacher + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "common/RawImage.h" // for RawImage +#include "decoders/RawDecoder.h" // for RawDecoder +#include "tiff/IsoMBox.h" // for IsoMRootBox +#include // for unique_ptr +#include // for move + +#include "tiff/TiffIFD.h" // for TiffRootIFDOwner + +namespace rawspeed { + +class CameraMetaData; + +class Buffer; + + +struct IsoMBoxCanonTypes final { + static constexpr FourCharStr CNCV = FourCharStr({'C', 'N', 'C', 'V'}); + static constexpr FourCharStr CCTP = FourCharStr({'C', 'C', 'T', 'P'}); + static constexpr FourCharStr CTBO = FourCharStr({'C', 'T', 'B', 'O'}); + static constexpr FourCharStr CMT1 = FourCharStr({'C', 'M', 'T', '1'}); + static constexpr FourCharStr CMT2 = FourCharStr({'C', 'M', 'T', '2'}); + static constexpr FourCharStr CMT3 = FourCharStr({'C', 'M', 'T', '3'}); + static constexpr FourCharStr CMT4 = FourCharStr({'C', 'M', 'T', '4'}); + static constexpr FourCharStr THMB = FourCharStr({'T', 'H', 'M', 'B'}); + + static constexpr FourCharStr CRAW = FourCharStr({'C', 'R', 'A', 'W'}); + static constexpr FourCharStr CMP1 = FourCharStr({'C', 'M', 'P', '1'}); + static constexpr FourCharStr CDI1 = FourCharStr({'C', 'D', 'I', '1'}); + static constexpr FourCharStr IAD1 = FourCharStr({'I', 'A', 'D', '1'}); + + static constexpr FourCharStr CTMD = FourCharStr({'C', 'T', 'M', 'D'}); +}; + + + + +class IsoMCanonCodecVersionBox final : public IsoMBox { +public: + std::string compressorVersion; + explicit IsoMCanonCodecVersionBox(const AbstractIsoMBox& base); + void parse(IsoMRootBox* root = nullptr) {} +}; + + +class IsoMCanonCTBOBox final : public IsoMBox { +public: + explicit IsoMCanonCTBOBox(const AbstractIsoMBox& base) : IsoMBox(base) {} + void parse(IsoMRootBox* root = nullptr) {} +}; + + +class IsoMCanonCCTPBox final : public IsoMBox { +public: + explicit IsoMCanonCCTPBox(const AbstractIsoMBox& base) : IsoMBox(base){} + void parse(IsoMRootBox* root = nullptr) {} +}; + + + +class IsoMCanonCMT1Box final : public IsoMBox { +public: + TiffRootIFDOwner mRootIFD0; + + explicit IsoMCanonCMT1Box(const AbstractIsoMBox& base); + void parse(IsoMRootBox* root = nullptr) {} +}; + +class IsoMCanonCMT2Box final : public IsoMBox { +public: + TiffRootIFDOwner mRootIFD0; + + explicit IsoMCanonCMT2Box(const AbstractIsoMBox& base); + void parse(IsoMRootBox* root = nullptr) {} +}; + + +class IsoMCanonCMT3Box final : public IsoMBox { +public: + TiffRootIFDOwner mRootIFD0; + + explicit IsoMCanonCMT3Box(const AbstractIsoMBox& base); + void parse(IsoMRootBox* root = nullptr) {} +}; + +class IsoMCanonCMT4Box final : public IsoMBox { +public: + TiffRootIFDOwner mRootIFD0; + + explicit IsoMCanonCMT4Box(const AbstractIsoMBox& base); + void parse(IsoMRootBox* root = nullptr) {} +}; + +class IsoMCanonThumbnailBox final : public IsoMBox { +public: + explicit IsoMCanonThumbnailBox(const AbstractIsoMBox& base) : IsoMBox(base) {} + void parse(IsoMRootBox* root = nullptr) {} +}; + + + + + +class IsoMCanonBox final : public IsoMContainerBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; + + std::unique_ptr cncvBox; + std::unique_ptr cctpBox; + std::unique_ptr ctboBox; + std::unique_ptr cmt1Box; + std::unique_ptr cmt2Box; + std::unique_ptr cmt3Box; + std::unique_ptr cmt4Box; + std::unique_ptr thmbBox; + +public: + explicit IsoMCanonBox(const AbstractIsoMBox& base) + : IsoMContainerBox(base) {} + + const std::unique_ptr& CNCV() const; + const std::unique_ptr& CCTP() const; + const std::unique_ptr& CTBO() const; + const std::unique_ptr& CMT1() const; + const std::unique_ptr& CMT2() const; + const std::unique_ptr& CMT3() const; + const std::unique_ptr& CMT4() const; + const std::unique_ptr& THMB() const; +}; + + + + + + + +class IsoMCanonCmp1Box final : public IsoMBox { + //void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const; + +public: + uint16_t reserved1; // unknown, -1? + uint16_t headerSize; + int16_t version; + int16_t versionSub; // Always 00 00 + int32_t f_width; + int32_t f_height; + int32_t tileWidth; + int32_t tileHeight; + int32_t nBits; + int32_t nPlanes; + int32_t cfaLayout; + int32_t encType; + int32_t imageLevels; + int32_t hasTileCols; + int32_t hasTileRows; + int32_t mdatHdrSize; + int32_t reserved2; // unknown + std::array reserved3; // unknown + + explicit IsoMCanonCmp1Box(const AbstractIsoMBox& base); +}; + + + + +class IsoMCanonIad1Box final : public IsoMFullBox { + //void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const; + +public: + // IAD1 data is not required to decode the image. + // We skip parsing IAD1. + + uint16_t sensorWidth; + uint16_t sensorHeight; + uint16_t reserved1; + uint16_t ind; // 0 = small, 1 = big + uint16_t reserved2; + uint16_t reserved3; + // Big image flags (we ignore small flags, not needed for decoding) + uint16_t sensorLeftBorder; + uint16_t sensorTopBorder; + uint16_t sensorRightBorder; + uint16_t sensorBottomBorder; + // followed by more unknown fields + uint16_t sensorBlackAreaLeft; + // followed by more unknown fields + uint16_t sensorBlackAreaTop; + // followed by more unknown fields + + explicit IsoMCanonIad1Box(const AbstractIsoMBox& base); +}; + + + +class IsoMCanonCdi1Box final : public IsoMContainerFullBox { + void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const override; +public: + std::unique_ptr iad1Box; + explicit IsoMCanonCdi1Box(const AbstractIsoMBox& base) + : IsoMContainerFullBox(base) { + } + + const std::unique_ptr& IAD1() const; +}; + + +// This is derived from SampleEntry +class IsoMCanonCrawBox final : public IsoMBox { + //void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const; + + std::unique_ptr cmp1Box; + std::unique_ptr cdi1Box; + +public: + std::array reserved1; + uint16_t dataReferenceIndex; + std::array reserved2; // unknown, all zero + uint16_t width; + uint16_t height; + uint32_t xResolution; // stored as 0072 0000 fixed point + uint32_t yResolution; // stored as 0072 0000 fixed point + uint32_t reserved3; // unknown + uint16_t reserved4; // unknown + std::array reserved5; // unknown + uint16_t bitDepth; + uint16_t reserved6; // unknown + uint16_t flags; // unknown, 3 for Jpeg, 1 for craw/raw + uint16_t formatInd; // 0 for jpeg, 1 for craw/raw + + explicit IsoMCanonCrawBox(const AbstractIsoMBox& base); + + const std::unique_ptr& CMP1() const; + const std::unique_ptr& CDI1() const; +}; + + + + + +// Derived from SampleEntry +class IsoMCanonTimedMetadataBox final : public IsoMBox { + struct RecordDesc final { + uint32_t recType; + uint32_t recSize; + + RecordDesc() = default; + + explicit RecordDesc(ByteStream* bs); + }; + + //void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const; + +public: + std::array reserved1; + uint16_t dataReferenceIndex; + std::vector recDescs; + + explicit IsoMCanonTimedMetadataBox(const AbstractIsoMBox& base); + +}; + + + + + + + +// TODO: List of records? +class CanonTimedMetadata final { + struct Record final { + uint32_t recSize; + uint16_t recType; + uint8_t reserved1; + uint8_t reserved2; + uint16_t reserved3; + uint16_t reserved4; + ByteStream payload; + + Record() = default; + + explicit Record(ByteStream* bs); + }; + + ByteStream data; + + //void parseBox(const AbstractIsoMBox& box) override; + explicit operator bool() const; + +public: + std::map records; // type + record + + explicit CanonTimedMetadata(const ByteStream* bs); + +}; + + + + + + + + + + + + + +class Cr3Decoder final : public RawDecoder { + std::unique_ptr rootBox; + std::unique_ptr canonBox; + std::unique_ptr crawBox; + + std::array wb_coeffs = {{NAN, NAN, NAN, NAN}}; + + std::string mode; + +public: + static bool isAppropriateDecoder(const IsoMRootBox& box); + + Cr3Decoder(std::unique_ptr rootBox_, const Buffer* file) + : RawDecoder(file), rootBox(std::move(rootBox_)) {} + + RawImage decodeRawInternal() override; + void checkSupportInternal(const CameraMetaData* meta) override; + void decodeMetaDataInternal(const CameraMetaData* meta) override; + +protected: + int getDecoderVersion() const override { return 0; } + bool isCodecSupported(const std::string& compressorVersion) const; +}; + +} // namespace rawspeed diff --git a/src/librawspeed/parsers/IsoMParser.cpp b/src/librawspeed/parsers/IsoMParser.cpp index 9e7b0b349..eb8a2d78d 100644 --- a/src/librawspeed/parsers/IsoMParser.cpp +++ b/src/librawspeed/parsers/IsoMParser.cpp @@ -20,6 +20,7 @@ */ #include "parsers/IsoMParser.h" // For IsoMParser +#include "decoders/Cr3Decoder.h" // for Cr3Decoder #include "decoders/RawDecoder.h" // for RawDecoder #include "io/ByteStream.h" // for ByteStream #include "io/Endianness.h" // for Endianness::big @@ -49,6 +50,8 @@ std::unique_ptr IsoMParser::getDecoder(const CameraMetaData* meta) { if (!rootBox) parseData(); + if (Cr3Decoder::isAppropriateDecoder(*rootBox)) + return std::make_unique(std::move(rootBox), mInput); ThrowIPE("No decoder found. Sorry."); } From 672a77f1400b6e6d8c776829f5ac919784af21b0 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Wed, 12 May 2021 22:53:57 +0200 Subject: [PATCH 07/27] rawspeed: Canon EOS R5 support --- data/cameras.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 82c37619e..4ee2abd44 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2151,6 +2151,22 @@ + + Canon EOS R5 + + RED + GREEN + GREEN + BLUE + + + + + + + + + Canon EOS M50 From c76dc382b3fcc5c21cdb40f2a34ed785dd5ae995 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Wed, 12 May 2021 22:54:20 +0200 Subject: [PATCH 08/27] rawspeed: Canon EOS R6 support --- data/cameras.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 4ee2abd44..f0b7ca58c 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2167,6 +2167,21 @@ + + Canon EOS R6 + + RED + GREEN + GREEN + BLUE + + + + + + + + Canon EOS M50 From d51133d65b69fdacf532cfe846fdb9aaa74f5586 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 14 May 2021 13:52:40 +0200 Subject: [PATCH 09/27] rawspeed: Canon EOS RP support --- data/cameras.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index f0b7ca58c..1e9d9885b 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2151,6 +2151,23 @@ + + Canon EOS RP + + RED + GREEN + GREEN + BLUE + + + + + + + + + + Canon EOS R5 From 146fb38bae8cd81ea3bc74255d9ad12526026487 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 14 May 2021 13:53:35 +0200 Subject: [PATCH 10/27] rawspeed: Canon EOS 850D support --- data/cameras.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 1e9d9885b..c9cbd9444 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -701,6 +701,26 @@ + + Canon EOS 850D + + RED + GREEN + GREEN + BLUE + + + + + + + + Canon EOS Rebel T8i + + + + + Canon EOS 760D From eba14a5008b422326f83970acf309b5bed932c39 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Mon, 14 Feb 2022 10:07:25 +0100 Subject: [PATCH 11/27] rawspeed: Canon EOS 90D support --- data/cameras.xml | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/data/cameras.xml b/data/cameras.xml index c9cbd9444..027d82d8f 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -550,12 +550,12 @@ Canon EOS 80D - - RED - GREEN - GREEN - BLUE - + + RED + GREEN + GREEN + BLUE + @@ -621,6 +621,23 @@ + + Canon EOS 90D + + RED + GREEN + GREEN + BLUE + + + + + + + + + + Canon EOS 700D From 0a5b9a161d9fa914e12babe1f16e2bc8773ec17f Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 14 May 2021 13:58:56 +0200 Subject: [PATCH 12/27] rawspeed: Canon EOS-1D X Mark III support --- data/cameras.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 027d82d8f..12f4e5652 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2171,6 +2171,24 @@ + + Canon EOS-1D X Mark III + + RED + GREEN + GREEN + BLUE + + + + + + + + + + + Canon EOS R From d0dd0889fb3da21c76fd456dca43f5172ee52bab Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 14 May 2021 13:59:58 +0200 Subject: [PATCH 13/27] Alias for Canon EOS M50: EOS KISS M --- data/cameras.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index 12f4e5652..ca7a5bcc8 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -2268,6 +2268,9 @@ + + Canon EOS KISS M + From eb8534941cbc97bc5fd3c1d4e601076c9462df6b Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 14 May 2021 14:02:08 +0200 Subject: [PATCH 14/27] CR3 IAD1 sensor info should not be DEBUG_PRIO_WARNING --- src/librawspeed/decoders/Cr3Decoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index be6280da8..dcb27ba73 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -422,7 +422,7 @@ IsoMCanonIad1Box::IsoMCanonIad1Box(const AbstractIsoMBox& base) sensorBlackAreaTop = data.get(); } - writeLog(DEBUG_PRIO_WARNING, + writeLog(DEBUG_PRIO_EXTRA, "IAD1 sensor width: %d, height: %d, crop: %u, %u, %u, %u, black " "area left: %u, top: %u", sensorWidth, sensorHeight, sensorLeftBorder, sensorTopBorder, From 95372ec80ec18c6d6503d940023c9e2f6b0225ba Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Sat, 15 May 2021 22:13:27 +0200 Subject: [PATCH 15/27] Add credit for CR3 file structure information from lclevy/canon_cr3 --- src/librawspeed/decoders/Cr3Decoder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index dcb27ba73..e26bf1c8e 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -4,6 +4,11 @@ Copyright (C) 2018 Roman Lebedev Copyright (C) 2021 Daniel Vogelbacher + Information about CR3 file structure and BMFF boxes + provided by Laurent Clévy and contributors + via https://github.com/lclevy/canon_cr3 + + 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 From a6f6e6d8ddb79be3ac9f93fd604dc0360cbb5fe7 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Mon, 26 Jul 2021 22:05:33 +0200 Subject: [PATCH 16/27] FIX: ISOSPEEDRATINGS is of type SHORT, larger ISO values stored in RECOMMENDEDEXPOSUREINDEX --- src/librawspeed/decoders/Cr3Decoder.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index e26bf1c8e..ce2db8803 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -563,6 +563,14 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { iso = canonBox->CMT2() ->mRootIFD0->getEntryRecursive(ISOSPEEDRATINGS) ->getU32(); + if(65535 == iso) { + // ISOSPEEDRATINGS is a SHORT EXIF value. For larger values, we have to look + // at RECOMMENDED_EXPOSURE_INDEX (maybe Canon specific). + if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(RECOMMENDEDEXPOSUREINDEX)) + iso = canonBox->CMT2() + ->mRootIFD0->getEntryRecursive(RECOMMENDEDEXPOSUREINDEX) + ->getU32(); + } // Big raw image is always in track 4 assert(rootBox->moov()->tracks.size() >= 4); From 06a6bc2f4c6061b8b68d1047bbcc72ac584931fb Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Thu, 20 May 2021 22:26:01 +0200 Subject: [PATCH 17/27] Improve black area detection and properly parse IAD1 box IAD1 contains 4 definitions for rectangles: * crop rectangle * left optical black area rectangle * top optical black area rectangle * active area rectangle This patch parses all IAD1 rectangles and uses them for black areas and crop. --- src/librawspeed/decoders/Cr3Decoder.cpp | 109 +++++++++++++++++++----- src/librawspeed/decoders/Cr3Decoder.h | 42 ++++++--- 2 files changed, 119 insertions(+), 32 deletions(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index ce2db8803..b0e3d9884 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -417,32 +417,86 @@ IsoMCanonIad1Box::IsoMCanonIad1Box(const AbstractIsoMBox& base) reserved3 = data.get(); if (2 == ind) { // ind is 2 for big images - sensorLeftBorder = data.get(); - sensorTopBorder = data.get(); - sensorRightBorder = data.get(); - sensorBottomBorder = data.get(); - data.skipBytes(2 * sizeof(uint16_t)); - sensorBlackAreaLeft = data.get(); - data.skipBytes(4 * sizeof(uint16_t)); - sensorBlackAreaTop = data.get(); + cropLeftOffset = data.get(); + cropTopOffset = data.get(); + cropRightOffset = data.get(); + cropBottomOffset = data.get(); + + leftOpticalBlackLeftOffset = data.get(); + leftOpticalBlackTopOffset = data.get(); + leftOpticalBlackRightOffset = data.get(); + leftOpticalBlackBottomOffset = data.get(); + + topOpticalBlackLeftOffset = data.get(); + topOpticalBlackTopOffset = data.get(); + topOpticalBlackRightOffset = data.get(); + topOpticalBlackBottomOffset = data.get(); + + activeAreaLeftOffset = data.get(); + activeAreaTopOffset = data.get(); + activeAreaRightOffset = data.get(); + activeAreaBottomOffset = data.get(); + } else { + // We hit a small image box?! + ThrowRDE("IAD1 box contains small image information, but big image expected"); } writeLog(DEBUG_PRIO_EXTRA, "IAD1 sensor width: %d, height: %d, crop: %u, %u, %u, %u, black " "area left: %u, top: %u", - sensorWidth, sensorHeight, sensorLeftBorder, sensorTopBorder, - sensorRightBorder, sensorBottomBorder, sensorBlackAreaLeft, - sensorBlackAreaTop); + sensorWidth, sensorHeight, cropLeftOffset, cropTopOffset, + cropRightOffset, cropBottomOffset, leftOpticalBlackRightOffset, + topOpticalBlackBottomOffset); // Validate. operator bool(); } IsoMCanonIad1Box::operator bool() const { - // No fields yet to validate, IAD1 is unused for decoding. + if(!sensorWidth || !sensorHeight) + ThrowIPE("IAD1 sensor size unknown"); + if(!cropRect().isThisInside(sensorRect())) + ThrowIPE("IAD1 crop rect is outside sensor rect"); return true; // OK! } +iRectangle2D IsoMCanonIad1Box::sensorRect() const { + return iRectangle2D(0, 0, sensorWidth, sensorHeight); +} + +iRectangle2D IsoMCanonIad1Box::cropRect() const { + return iRectangle2D( + cropLeftOffset, + cropTopOffset, + (cropRightOffset+1)-cropLeftOffset, + (cropBottomOffset+1)-cropTopOffset); +} + +iRectangle2D IsoMCanonIad1Box::leftOpticalBlackRect() const { + return iRectangle2D( + leftOpticalBlackLeftOffset, + leftOpticalBlackTopOffset, + (leftOpticalBlackRightOffset+1)-leftOpticalBlackLeftOffset, + (leftOpticalBlackBottomOffset+1)-leftOpticalBlackTopOffset); +} + +iRectangle2D IsoMCanonIad1Box::topOpticalBlackRect() const { + return iRectangle2D( + topOpticalBlackLeftOffset, + topOpticalBlackTopOffset, + (topOpticalBlackRightOffset+1)-topOpticalBlackLeftOffset, + (topOpticalBlackBottomOffset+1)-topOpticalBlackTopOffset); +} + +iRectangle2D IsoMCanonIad1Box::activeArea() const { + return iRectangle2D( + activeAreaLeftOffset, + activeAreaTopOffset, + (activeAreaRightOffset+1)-activeAreaLeftOffset, + (activeAreaBottomOffset+1)-activeAreaTopOffset); +} + + CanonTimedMetadata::CanonTimedMetadata::Record::Record(ByteStream* bs) { assert(bs->getByteOrder() == Endianness::little); auto origPos = bs->getPosition(); @@ -634,23 +688,34 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { } setMetaData(meta, camId.make, camId.model, mode, iso); + writeLog(DEBUG_PRIO_EXTRA, "blacklevel for ISO %d is %d", mRaw->metadata.isoSpeed, mRaw->blackLevel); - // IAD1 described sensor constraints + // IAD1 describes sensor constraints const auto& iad1 = crawBox->CDI1()->IAD1(); if (mRaw->blackAreas.empty()) { - mRaw->blackAreas.push_back(BlackArea(0, iad1->sensorBlackAreaLeft, true)); - mRaw->blackAreas.push_back(BlackArea(0, iad1->sensorBlackAreaTop, false)); + // IAD1 stores the rectangles for black areas. + auto leftOpticalBlack = iad1->leftOpticalBlackRect(); + auto topOpticalBlack = iad1->topOpticalBlackRect(); + if(leftOpticalBlack.dim.x >= 12+4) { + // if left optical black has >= 12+4 pixels, we reduce them by 12 as some + // models (EOS RP is known) has white pixels in this area. + // Yes, this is hacky, but IAD1 reports offset=0 which is either wrong or the white pixels + // are a camera bug and must be resolved in software. + leftOpticalBlack.pos.x += 12; + leftOpticalBlack.dim.x -= 12; + } + if(topOpticalBlack.dim.y >= 12+4) { + // Same must be done for horizontal pixels + topOpticalBlack.pos.y += 12; + topOpticalBlack.dim.y -= 12; + } + mRaw->blackAreas.push_back(BlackArea(leftOpticalBlack.pos.x, leftOpticalBlack.dim.x, true)); + mRaw->blackAreas.push_back(BlackArea(topOpticalBlack.pos.y, topOpticalBlack.pos.y, false)); } if (applyCrop) { - // IAD1 sensor parameters always crop one more pixel as needed. - // We add back the missing pixels to match official specifications. - iRectangle2D new_area(iad1->sensorLeftBorder, iad1->sensorTopBorder, - iad1->sensorRightBorder - iad1->sensorLeftBorder + 1, - iad1->sensorBottomBorder - iad1->sensorTopBorder + 1); - - mRaw->subFrame(new_area); + mRaw->subFrame(iad1->cropRect()); } } diff --git a/src/librawspeed/decoders/Cr3Decoder.h b/src/librawspeed/decoders/Cr3Decoder.h index 18b3f0055..89826ea81 100644 --- a/src/librawspeed/decoders/Cr3Decoder.h +++ b/src/librawspeed/decoders/Cr3Decoder.h @@ -200,18 +200,40 @@ class IsoMCanonIad1Box final : public IsoMFullBox { uint16_t ind; // 0 = small, 1 = big uint16_t reserved2; uint16_t reserved3; - // Big image flags (we ignore small flags, not needed for decoding) - uint16_t sensorLeftBorder; - uint16_t sensorTopBorder; - uint16_t sensorRightBorder; - uint16_t sensorBottomBorder; - // followed by more unknown fields - uint16_t sensorBlackAreaLeft; - // followed by more unknown fields - uint16_t sensorBlackAreaTop; - // followed by more unknown fields + + // Big image fields (we ignore small image, not needed for decoding) + + // Crop rectangle + uint16_t cropLeftOffset; + uint16_t cropTopOffset; + uint16_t cropRightOffset; + uint16_t cropBottomOffset; + + // Left optical black rectangle + uint16_t leftOpticalBlackLeftOffset; + uint16_t leftOpticalBlackTopOffset; + uint16_t leftOpticalBlackRightOffset; + uint16_t leftOpticalBlackBottomOffset; + + // Top optical black rectangle + uint16_t topOpticalBlackLeftOffset; + uint16_t topOpticalBlackTopOffset; + uint16_t topOpticalBlackRightOffset; + uint16_t topOpticalBlackBottomOffset; + + // Active area rectangle + uint16_t activeAreaLeftOffset; + uint16_t activeAreaTopOffset; + uint16_t activeAreaRightOffset; + uint16_t activeAreaBottomOffset; explicit IsoMCanonIad1Box(const AbstractIsoMBox& base); + + iRectangle2D sensorRect() const; + iRectangle2D cropRect() const; + iRectangle2D leftOpticalBlackRect() const; + iRectangle2D topOpticalBlackRect() const; + iRectangle2D activeArea() const; }; From 79647e3f45266fd73b637296a9e744a51ee066e2 Mon Sep 17 00:00:00 2001 From: Hubert Kowalski Date: Mon, 24 May 2021 12:44:02 +0200 Subject: [PATCH 18/27] Cleanup C to C++ conversion conventions --- src/librawspeed/decoders/Cr3Decoder.cpp | 7 +- .../decompressors/CrxDecompressor.cpp | 230 +++++++++--------- 2 files changed, 121 insertions(+), 116 deletions(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index b0e3d9884..402debf8e 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -613,10 +613,11 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { const auto camId = canonBox->CMT1()->mRootIFD0->getID(); uint32_t iso = 0; - if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(ISOSPEEDRATINGS)) + if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(ISOSPEEDRATINGS)) { iso = canonBox->CMT2() ->mRootIFD0->getEntryRecursive(ISOSPEEDRATINGS) ->getU32(); + } if(65535 == iso) { // ISOSPEEDRATINGS is a SHORT EXIF value. For larger values, we have to look // at RECOMMENDED_EXPOSURE_INDEX (maybe Canon specific). @@ -632,14 +633,14 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { // CTMD auto& CTMD_stsd = track3Mdia->minf->stbl->stsd; - assert(CTMD_stsd->dscs.size() > 0); + assert(!CTMD_stsd->dscs.empty()); // Get Sample and rebuild a CTMD IsoMCanonTimedMetadataBox ctmd = IsoMCanonTimedMetadataBox(CTMD_stsd->dscs[0]); // CTMD MDAT - assert(track3Mdia->minf->stbl->chunks.size() > 0); + assert(!track3Mdia->minf->stbl->chunks.empty()); auto ctmd_chunk = track3Mdia->minf->stbl->chunks[0]; Buffer ctmd_chunk_buf = ctmd_chunk->getSubView(0); diff --git a/src/librawspeed/decompressors/CrxDecompressor.cpp b/src/librawspeed/decompressors/CrxDecompressor.cpp index 9f35bb04e..dc85719ac 100644 --- a/src/librawspeed/decompressors/CrxDecompressor.cpp +++ b/src/librawspeed/decompressors/CrxDecompressor.cpp @@ -125,11 +125,11 @@ struct CrxSubband { int32_t bandSize; uint64_t dataSize; int64_t dataOffset; - short rowStartAddOn; - short rowEndAddOn; - short colStartAddOn; - short colEndAddOn; - short levelShift; + int16_t rowStartAddOn; + int16_t rowEndAddOn; + int16_t colStartAddOn; + int16_t colEndAddOn; + int16_t levelShift; }; struct CrxPlaneComp { @@ -190,7 +190,7 @@ enum TileFlags { E_HAS_TILES_ON_THE_TOP = 8 }; -int32_t exCoefNumTbl[144] = { +static int32_t exCoefNumTbl[144] = { 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0, @@ -198,14 +198,14 @@ int32_t exCoefNumTbl[144] = { 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1}; -int32_t q_step_tbl[8] = {0x28, 0x2D, 0x33, 0x39, 0x40, 0x48}; +static int32_t q_step_tbl[8] = {0x28, 0x2D, 0x33, 0x39, 0x40, 0x48}; -uint32_t JS[32] = {1, 1, 1, 1, 2, 2, 2, 2, +static uint32_t JS[32] = {1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000}; -uint32_t J[32] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, +static uint32_t J[32] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; @@ -221,7 +221,7 @@ static inline void crxFillBuffer(CrxBitstream* bitStrm) { if (sub.getSize() >= bytesToRead) { auto data = sub.getData(0, bytesToRead); - assert(bitStrm->mdatBuf.size() > 0); + assert(!bitStrm->mdatBuf.empty()); ::memcpy(bitStrm->mdatBuf.data(), data, bytesToRead); bitStrm->curBufSize = bytesToRead; } @@ -238,20 +238,20 @@ inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { int32_t result = 0; if (bitStrm->bitData) { - _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)bitStrm->bitData); + _BitScanReverse(static_cast(&nonZeroBit), static_cast(bitStrm->bitData)); result = 31 - nonZeroBit; bitStrm->bitData <<= 32 - nonZeroBit; bitStrm->bitsLeft -= 32 - nonZeroBit; } else { uint32_t bitsLeft = bitStrm->bitsLeft; - while (1) { + while (true) { while (bitStrm->curPos + 4 <= bitStrm->curBufSize) { nextData = getByteSwapped( - *(uint32_t*)(bitStrm->mdatBuf.data() + bitStrm->curPos)); + *reinterpret_cast(bitStrm->mdatBuf.data() + bitStrm->curPos)); bitStrm->curPos += 4; crxFillBuffer(bitStrm); if (nextData) { - _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)nextData); + _BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); result = bitsLeft + 31 - nonZeroBit; bitStrm->bitData = nextData << (32 - nonZeroBit); bitStrm->bitsLeft = nonZeroBit; @@ -267,8 +267,8 @@ inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { break; bitsLeft += 8; } - _BitScanReverse((DWORD*)&nonZeroBit, (DWORD)nextData); - result = (uint32_t)(bitsLeft + 7 - nonZeroBit); + _BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); + result = static_cast(bitsLeft + 7 - nonZeroBit); bitStrm->bitData = nextData << (32 - nonZeroBit); bitStrm->bitsLeft = nonZeroBit; } @@ -286,7 +286,7 @@ inline uint32_t crxBitstreamGetBits(CrxBitstream* bitStrm, int bits) { // get them from stream if (bitStrm->curPos + 4 <= bitStrm->curBufSize) { nextWord = getByteSwapped( - *(uint32_t*)(bitStrm->mdatBuf.data() + bitStrm->curPos)); + *reinterpret_cast(bitStrm->mdatBuf.data() + bitStrm->curPos)); bitStrm->curPos += 4; crxFillBuffer(bitStrm); bitStrm->bitsLeft = 32 - (bits - bitsLeft); @@ -366,7 +366,7 @@ inline void crxDecodeSymbolL1(CrxBandParam* param, int32_t doMedianPrediction, ++param->lineBuf1; } -int crxDecodeLine(CrxBandParam* param) { +static int crxDecodeLine(CrxBandParam* param) { int length = param->subbandWidth; param->lineBuf1[0] = param->lineBuf0[1]; @@ -450,14 +450,15 @@ inline void crxDecodeSymbolL1Rounded(CrxBandParam* param, int32_t doSym = 1, param->lineBuf1[1] = param->roundedBitsMask * 2 * code + (code >> 31) + sym; if (doCode) { - if (param->lineBuf0[2] > param->lineBuf0[1]) + if (param->lineBuf0[2] > param->lineBuf0[1]) { code = (param->lineBuf0[2] - param->lineBuf0[1] + param->roundedBitsMask - 1) >> param->roundedBits; - else + } else { code = -( (param->lineBuf0[1] - param->lineBuf0[2] + param->roundedBitsMask) >> param->roundedBits); + } param->kParam = crxPredictKParameter(param->kParam, (bitCode + 2 * _abs(code)) >> 1, 15); @@ -467,7 +468,7 @@ inline void crxDecodeSymbolL1Rounded(CrxBandParam* param, int32_t doSym = 1, ++param->lineBuf1; } -int crxDecodeLineRounded(CrxBandParam* param) { +static int crxDecodeLineRounded(CrxBandParam* param) { int32_t valueReached = 0; param->lineBuf0[0] = param->lineBuf0[1]; @@ -537,7 +538,7 @@ int crxDecodeLineRounded(CrxBandParam* param) { return 0; } -int crxDecodeLineNoRefPrevLine(CrxBandParam* param) { +static int crxDecodeLineNoRefPrevLine(CrxBandParam* param) { int32_t i = 0; for (; i < param->subbandWidth - 1; i++) { @@ -635,7 +636,7 @@ int crxDecodeLineNoRefPrevLine(CrxBandParam* param) { return 0; } -int crxDecodeTopLine(CrxBandParam* param) { +static int crxDecodeTopLine(CrxBandParam* param) { param->lineBuf1[0] = 0; int32_t length = param->subbandWidth; @@ -712,7 +713,7 @@ int crxDecodeTopLine(CrxBandParam* param) { return 0; } -int crxDecodeTopLineRounded(CrxBandParam* param) { +static int crxDecodeTopLineRounded(CrxBandParam* param) { param->lineBuf1[0] = 0; int32_t length = param->subbandWidth; @@ -791,7 +792,7 @@ int crxDecodeTopLineRounded(CrxBandParam* param) { return 0; } -int crxDecodeTopLineNoRefPrevLine(CrxBandParam* param) { +static int crxDecodeTopLineNoRefPrevLine(CrxBandParam* param) { param->lineBuf0[0] = 0; param->lineBuf1[0] = 0; int32_t length = param->subbandWidth; @@ -874,7 +875,7 @@ int crxDecodeTopLineNoRefPrevLine(CrxBandParam* param) { return 0; } -int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { +static int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { if (!param || !bandBuf) return -1; if (param->curLine >= param->subbandHeight) @@ -887,7 +888,7 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { param->kParam = 0; if (param->supportsPartial) { if (param->roundedBitsMask <= 0) { - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; int32_t* lineBuf = param->lineBuf1 + 1; if (crxDecodeTopLine(param)) @@ -900,7 +901,7 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { while (param->roundedBitsMask >> param->roundedBits) ++param->roundedBits; } - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; int32_t* lineBuf = param->lineBuf1 + 1; if (crxDecodeTopLineRounded(param)) @@ -909,8 +910,8 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { ++param->curLine; } } else { - param->lineBuf2 = (int32_t*)param->nonProgrData; - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf2 = param->nonProgrData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; int32_t* lineBuf = param->lineBuf1 + 1; if (crxDecodeTopLineNoRefPrevLine(param)) @@ -920,12 +921,12 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { } } else if (!param->supportsPartial) { int32_t lineLength = param->subbandWidth + 2; - param->lineBuf2 = (int32_t*)param->nonProgrData; + param->lineBuf2 = param->nonProgrData; if (param->curLine & 1) { - param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf1 = param->paramData; param->lineBuf0 = param->lineBuf1 + lineLength; } else { - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; } int32_t* lineBuf = param->lineBuf1 + 1; @@ -936,10 +937,10 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { } else if (param->roundedBitsMask <= 0) { int32_t lineLength = param->subbandWidth + 2; if (param->curLine & 1) { - param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf1 = param->paramData; param->lineBuf0 = param->lineBuf1 + lineLength; } else { - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; } int32_t* lineBuf = param->lineBuf1 + 1; @@ -950,10 +951,10 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { } else { int32_t lineLength = param->subbandWidth + 2; if (param->curLine & 1) { - param->lineBuf1 = (int32_t*)param->paramData; + param->lineBuf1 = param->paramData; param->lineBuf0 = param->lineBuf1 + lineLength; } else { - param->lineBuf0 = (int32_t*)param->paramData; + param->lineBuf0 = param->paramData; param->lineBuf1 = param->lineBuf0 + lineLength; } int32_t* lineBuf = param->lineBuf1 + 1; @@ -965,7 +966,7 @@ int crxDecodeLine(CrxBandParam* param, uint8_t* bandBuf) { return 0; } -int crxUpdateQparam(CrxSubband* subband) { +static int crxUpdateQparam(CrxSubband* subband) { uint32_t bitCode = crxBitstreamGetZeros(&subband->bandParam->bitStream); if (bitCode >= 23) bitCode = crxBitstreamGetBits(&subband->bandParam->bitStream, 8); @@ -989,7 +990,8 @@ inline int getSubbandRow(CrxSubband* band, int row) { : band->height - band->rowEndAddOn - band->rowStartAddOn - 1); } -int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { + +static int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { if (!band->dataSize) { memset(band->bandBuf, 0, band->bandSize); return 0; @@ -1004,7 +1006,7 @@ int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { return 0; // update band buffers - int32_t* bandBuf = (int32_t*)band->bandBuf; + int32_t* bandBuf = reinterpret_cast(band->bandBuf); if (qStep) { // new version uint32_t* qStepTblPtr = @@ -1047,7 +1049,7 @@ int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { return 0; } -void crxHorizontal53(int32_t* lineBufLA, int32_t* lineBufLB, +static void crxHorizontal53(int32_t* lineBufLA, int32_t* lineBufLB, CrxWaveletTransform* wavelet, uint32_t tileFlag) { int32_t* band0Buf = wavelet->subband0Buf; int32_t* band1Buf = wavelet->subband1Buf; @@ -1114,7 +1116,7 @@ void crxHorizontal53(int32_t* lineBufLA, int32_t* lineBufLB, } } -int32_t* crxIdwt53FilterGetLine(CrxPlaneComp* comp, int32_t level) { +static int32_t* crxIdwt53FilterGetLine(CrxPlaneComp* comp, int32_t level) { int32_t* result = comp->wvltTransform[level].lineBuf[(comp->wvltTransform[level].fltTapH - comp->wvltTransform[level].curH + 5) % @@ -1124,12 +1126,12 @@ int32_t* crxIdwt53FilterGetLine(CrxPlaneComp* comp, int32_t level) { return result; } -int crxIdwt53FilterDecode(CrxPlaneComp* comp, int32_t level, CrxQStep* qStep) { +static int crxIdwt53FilterDecode(CrxPlaneComp* comp, int32_t level, CrxQStep* qStep) { if (comp->wvltTransform[level].curH) return 0; CrxSubband* sband = comp->subBands + 3 * level; - CrxQStep* qStepLevel = qStep ? qStep + level : 0; + CrxQStep* qStepLevel = qStep ? qStep + level : nullptr; if (comp->wvltTransform[level].height - 3 <= comp->wvltTransform[level].curLine && @@ -1160,7 +1162,7 @@ int crxIdwt53FilterDecode(CrxPlaneComp* comp, int32_t level, CrxQStep* qStep) { return 0; } -int crxIdwt53FilterTransform(CrxPlaneComp* comp, uint32_t level) { +static int crxIdwt53FilterTransform(CrxPlaneComp* comp, uint32_t level) { CrxWaveletTransform* wavelet = comp->wvltTransform + level; if (wavelet->curH) @@ -1348,14 +1350,14 @@ int crxIdwt53FilterTransform(CrxPlaneComp* comp, uint32_t level) { return 0; } -int crxIdwt53FilterInitialize(CrxPlaneComp* comp, int32_t level, +static int crxIdwt53FilterInitialize(CrxPlaneComp* comp, int32_t level, CrxQStep* qStep) { if (level == 0) return 0; for (int curLevel = 0, curBand = 0; curLevel < level; curLevel++, curBand += 3) { - CrxQStep* qStepLevel = qStep ? qStep + curLevel : 0; + CrxQStep* qStepLevel = qStep ? qStep + curLevel : nullptr; CrxWaveletTransform* wavelet = comp->wvltTransform + curLevel; if (curLevel) wavelet[0].subband0Buf = crxIdwt53FilterGetLine(comp, curLevel - 1); @@ -1492,10 +1494,10 @@ int crxIdwt53FilterInitialize(CrxPlaneComp* comp, int32_t level, return 0; } -void crxFreeSubbandData(CrxImage* image, CrxPlaneComp* comp) { +static void crxFreeSubbandData(CrxImage* image, CrxPlaneComp* comp) { if (comp->compBuf) { free(comp->compBuf); - comp->compBuf = 0; + comp->compBuf = nullptr; } if (!comp->subBands) @@ -1504,16 +1506,16 @@ void crxFreeSubbandData(CrxImage* image, CrxPlaneComp* comp) { for (int32_t i = 0; i < image->subbandCount; i++) { if (comp->subBands[i].bandParam) { free(comp->subBands[i].bandParam); - comp->subBands[i].bandParam = 0LL; + comp->subBands[i].bandParam = nullptr; } - comp->subBands[i].bandBuf = 0; + comp->subBands[i].bandBuf = nullptr; comp->subBands[i].bandSize = 0; } } -void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, - int plane = 0, int32_t* lineData = 0, +static void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, + int plane = 0, int32_t* lineData = nullptr, int lineLength = 0) { if (lineData) { uint64_t rawOffset = 4 * img->planeWidth * imageRow + 2 * imageCol; @@ -1581,13 +1583,13 @@ void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, } } -int crxParamInit(CrxImage* img, CrxBandParam** param, +static int crxParamInit(CrxImage* img, CrxBandParam** param, uint64_t subbandMdatOffset, uint64_t subbandDataSize, uint32_t subbandWidth, uint32_t subbandHeight, bool supportsPartial, uint32_t roundedBitsMask) { int32_t progrDataSize = supportsPartial ? 0 : sizeof(int32_t) * subbandWidth; int32_t paramLength = 2 * subbandWidth + 4; - uint8_t* paramBuf = 0; + uint8_t* paramBuf = nullptr; paramBuf = (uint8_t*)calloc( 1, sizeof(CrxBandParam) + sizeof(int32_t) * paramLength + progrDataSize); @@ -1595,13 +1597,13 @@ int crxParamInit(CrxImage* img, CrxBandParam** param, if (!paramBuf) return -1; - *param = (CrxBandParam*)paramBuf; + *param = reinterpret_cast(paramBuf); paramBuf += sizeof(CrxBandParam); - (*param)->paramData = (int32_t*)paramBuf; + (*param)->paramData = reinterpret_cast(paramBuf); (*param)->nonProgrData = - progrDataSize ? (*param)->paramData + paramLength : 0; + progrDataSize ? (*param)->paramData + paramLength : nullptr; (*param)->subbandWidth = subbandWidth; (*param)->subbandHeight = subbandHeight; (*param)->roundedBits = 0; @@ -1621,11 +1623,11 @@ int crxParamInit(CrxImage* img, CrxBandParam** param, return 0; } -int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, +static int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, const CrxTile* tile, uint32_t mdatOffset) { - long compDataSize = 0; - long waveletDataOffset = 0; - long compCoeffDataOffset = 0; + int64_t compDataSize = 0; + int64_t waveletDataOffset = 0; + int64_t compCoeffDataOffset = 0; int32_t toSubbands = 3 * img->levels + 1; int32_t transformWidth = 0; @@ -1646,12 +1648,14 @@ int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, compCoeffDataOffset = compDataSize; // calc wavelet line buffer sizes (always at one level up from current) - for (int level = 0; level < img->levels; ++level) - if (level < img->levels - 1) + for (int level = 0; level < img->levels; ++level) { + if (level < img->levels - 1) { compDataSize += 8 * sizeof(int32_t) * planeComp->subBands[3 * (level + 1) + 2].width; - else + } else { compDataSize += 8 * sizeof(int32_t) * tile->width; + } + } } // buffer allocation @@ -1673,12 +1677,12 @@ int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, // wavelet data initialisation if (img->levels) { - CrxWaveletTransform* waveletTransforms = - (CrxWaveletTransform*)(planeComp->compBuf + waveletDataOffset); - int32_t* paramData = (int32_t*)(planeComp->compBuf + compCoeffDataOffset); + auto waveletTransforms = + reinterpret_cast(planeComp->compBuf + waveletDataOffset); + auto paramData = reinterpret_cast(planeComp->compBuf + compCoeffDataOffset); planeComp->wvltTransform = waveletTransforms; - waveletTransforms[0].subband0Buf = (int32_t*)subbands->bandBuf; + waveletTransforms[0].subband0Buf = reinterpret_cast(subbands->bandBuf); for (int level = 0; level < img->levels; ++level) { int32_t band = 3 * level + 1; @@ -1709,11 +1713,11 @@ int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, waveletTransforms[level].curLine = 0; waveletTransforms[level].curH = 0; waveletTransforms[level].fltTapH = 0; - waveletTransforms[level].subband1Buf = (int32_t*)subbands[band].bandBuf; + waveletTransforms[level].subband1Buf = reinterpret_cast(subbands[band].bandBuf); waveletTransforms[level].subband2Buf = - (int32_t*)subbands[band + 1].bandBuf; + reinterpret_cast(subbands[band + 1].bandBuf); waveletTransforms[level].subband3Buf = - (int32_t*)subbands[band + 2].bandBuf; + reinterpret_cast(subbands[band + 2].bandBuf); paramData = waveletTransforms[level].lineBuf[7] + transformWidth; } @@ -1742,7 +1746,7 @@ int crxSetupSubbandData(CrxImage* img, CrxPlaneComp* planeComp, } int CrxDecompressor::crxDecodePlane(void* p, uint32_t planeNumber) { - CrxImage* img = (CrxImage*)p; + auto img = static_cast(p); int imageRow = 0; for (int tRow = 0; tRow < img->tileRows; tRow++) { int imageCol = 0; @@ -1780,7 +1784,7 @@ int CrxDecompressor::crxDecodePlane(void* p, uint32_t planeNumber) { if (crxDecodeLine(planeComp->subBands->bandParam, planeComp->subBands->bandBuf)) return -1; - int32_t* lineData = (int32_t*)planeComp->subBands->bandBuf; + auto lineData = reinterpret_cast(planeComp->subBands->bandBuf); crxConvertPlaneLine(img, imageRow + i, imageCol, planeNumber, lineData, tile->width); } @@ -1793,7 +1797,7 @@ int CrxDecompressor::crxDecodePlane(void* p, uint32_t planeNumber) { return 0; } -uint32_t crxReadQP(CrxBitstream* bitStrm, int32_t kParam) { +static uint32_t crxReadQP(CrxBitstream* bitStrm, int32_t kParam) { uint32_t qp = crxBitstreamGetZeros(bitStrm); if (qp >= 23) qp = crxBitstreamGetBits(bitStrm, 8); @@ -1803,7 +1807,7 @@ uint32_t crxReadQP(CrxBitstream* bitStrm, int32_t kParam) { return qp; } -void crxDecodeGolombTop(CrxBitstream* bitStrm, int32_t width, int32_t* lineBuf, +static void crxDecodeGolombTop(CrxBitstream* bitStrm, int32_t width, int32_t* lineBuf, int32_t* kParam) { lineBuf[0] = 0; while (width-- > 0) { @@ -1816,7 +1820,7 @@ void crxDecodeGolombTop(CrxBitstream* bitStrm, int32_t width, int32_t* lineBuf, lineBuf[1] = lineBuf[0] + 1; } -void crxDecodeGolombNormal(CrxBitstream* bitStrm, int32_t width, +static void crxDecodeGolombNormal(CrxBitstream* bitStrm, int32_t width, int32_t* lineBuf0, int32_t* lineBuf1, int32_t* kParam) { lineBuf1[0] = lineBuf0[1]; @@ -1837,7 +1841,7 @@ void crxDecodeGolombNormal(CrxBitstream* bitStrm, int32_t width, lineBuf1[1] = lineBuf1[0] + 1; } -int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, +static int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, uint32_t totalQP) { if (img->levels > 3 || img->levels < 1) return -1; @@ -1856,7 +1860,7 @@ int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, if (!tile->qStep) return -1; - uint32_t* qStepTbl = (uint32_t*)(tile->qStep + img->levels); + auto qStepTbl = reinterpret_cast(tile->qStep + img->levels); CrxQStep* qStep = tile->qStep; uint8_t curr_level = img->levels; while (curr_level > 0) { @@ -1911,13 +1915,15 @@ int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, qStep->qStepTbl = qStepTbl; qStep->width = qpWidth; qStep->height = qpHeight; - for (int qpRow = 0; qpRow < qpHeight; ++qpRow) - for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl, ++qpTable) - if (*qpTable / 6 >= 6) + for (int qpRow = 0; qpRow < qpHeight; ++qpRow) { + for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl, ++qpTable) { + if (*qpTable / 6 >= 6) { *qStepTbl = q_step_tbl[*qpTable % 6] * (1 << (*qpTable / 6 + 26)); - else + } else { *qStepTbl = q_step_tbl[*qpTable % 6] >> (6 - *qpTable / 6); - + } + } + } --curr_level; break; default: @@ -1928,9 +1934,9 @@ int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, } inline void crxSetupSubbandIdx(const IsoMCanonCmp1Box* hdr, CrxImage* img, - CrxSubband* band, int level, short colStartIdx, - short bandWidthExCoef, short rowStartIdx, - short bandHeightExCoef) { + CrxSubband* band, int level, int16_t colStartIdx, + int16_t bandWidthExCoef, int16_t rowStartIdx, + int16_t bandHeightExCoef) { if (hdr->version == 0x200) { band->rowStartAddOn = rowStartIdx; band->rowEndAddOn = bandHeightExCoef; @@ -1946,7 +1952,7 @@ inline void crxSetupSubbandIdx(const IsoMCanonCmp1Box* hdr, CrxImage* img, } } -int crxProcessSubbands(const IsoMCanonCmp1Box* hdr, CrxImage* img, +static int crxProcessSubbands(const IsoMCanonCmp1Box* hdr, CrxImage* img, CrxTile* tile, CrxPlaneComp* comp) { CrxSubband* band = comp->subBands + img->subbandCount - 1; // set to last band uint32_t bandHeight = tile->height; @@ -2027,7 +2033,7 @@ int crxProcessSubbands(const IsoMCanonCmp1Box* hdr, CrxImage* img, return 0; } -int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, +static int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, CrxTile* tile, CrxPlaneComp* comp, const uint8_t** subbandMdatPtr, int32_t* mdatSize) { if (!img->subbandCount) @@ -2056,8 +2062,8 @@ int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, band->dataOffset = subbandOffset; band->kParam = 0; - band->bandParam = 0; - band->bandBuf = 0; + band->bandParam = nullptr; + band->bandBuf = nullptr; band->bandSize = 0; if (hdrSign == 0xFF03) { @@ -2080,9 +2086,7 @@ int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, band->qParam = 0; band->dataSize = subbandSize - sgetn(2, *subbandMdatPtr + 16); band->qStepBase = sgetn(4, *subbandMdatPtr + 12); - ; band->qStepMult = sgetn(2, *subbandMdatPtr + 10); - ; } subbandOffset += subbandSize; @@ -2094,26 +2098,26 @@ int crxReadSubbandHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img, return 0; } -int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { +static int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { int nTiles = img->tileRows * img->tileCols; if (!nTiles) ThrowRDE("Crx decompression error"); if (!img->tiles) { - img->tiles = (CrxTile*)calloc( + img->tiles = static_cast(calloc( sizeof(CrxTile) * nTiles + sizeof(CrxPlaneComp) * nTiles * img->nPlanes + sizeof(CrxSubband) * nTiles * img->nPlanes * img->subbandCount, - 1); + 1)); if (!img->tiles) ThrowRDE("Crx decompression error"); // memory areas in allocated chunk CrxTile* tile = img->tiles; - CrxPlaneComp* comps = (CrxPlaneComp*)(tile + nTiles); - CrxSubband* bands = (CrxSubband*)(comps + img->nPlanes * nTiles); + auto comps = reinterpret_cast(tile + nTiles); + auto bands = reinterpret_cast(comps + img->nPlanes * nTiles); for (int curTile = 0; curTile < nTiles; curTile++, tile++) { tile->tileFlag = 0; // tile neighbouring flags @@ -2159,14 +2163,14 @@ int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { comp->supportsPartial = true; comp->tileFlag = tile->tileFlag; comp->subBands = band; - comp->compBuf = 0; - comp->wvltTransform = 0; + comp->compBuf = nullptr; + comp->wvltTransform = nullptr; if (img->subbandCount) { for (int curBand = 0; curBand < img->subbandCount; curBand++, band++) { band->supportsPartial = false; band->qParam = 4; - band->bandParam = 0; + band->bandParam = nullptr; band->dataSize = 0; } } @@ -2202,7 +2206,7 @@ int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { tile->tileSize = sgetn(4, dataPtr + 4); tile->dataOffset = tileOffset; - tile->qStep = 0; + tile->qStep = nullptr; if (hdrSize == 16) { // extended header data - terminated by 0 bytes if (sgetn(2, dataPtr + 18) != 0) @@ -2278,9 +2282,9 @@ int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { crxFillBuffer(&bitStrm); - unsigned int qpWidth = (tile->width >> 3) + ((tile->width & 7) != 0); - unsigned int qpHeight = (tile->height >> 1) + (tile->height & 1); - unsigned long totalQP = qpHeight * qpWidth; + uint32_t qpWidth = (tile->width >> 3) + ((tile->width & 7) != 0); + uint32_t qpHeight = (tile->height >> 1) + (tile->height & 1); + uint64_t totalQP = static_cast(qpHeight) * static_cast(qpWidth); try { std::vector qpTable(totalQP + 2 * (qpWidth + 2)); @@ -2313,7 +2317,7 @@ int crxReadImageHeaders(const IsoMCanonCmp1Box* hdr, CrxImage* img) { return 0; } -int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, +static int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, int16_t* outBuf) { int IncrBitTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0}; @@ -2332,7 +2336,7 @@ int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, img->planeHeight - hdr->tileHeight * (img->tileRows - 1) < 0x16) ThrowRDE("Crx decompression error"); - img->tiles = 0; + img->tiles = nullptr; img->levels = hdr->imageLevels; img->subbandCount = 3 * img->levels + 1; // 3 bands per level + one last LL img->nPlanes = hdr->nPlanes; @@ -2341,8 +2345,8 @@ int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, img->samplePrecision = hdr->nBits + IncrBitTable[4 * hdr->encType + 2] + 1; img->mdatOffset = hdr->mdatHdrSize; // after header, plane data follows img->mdatHdrSize = hdr->mdatHdrSize; - img->planeBuf = 0; - img->outBufs[0] = img->outBufs[1] = img->outBufs[2] = img->outBufs[3] = 0; + img->planeBuf = nullptr; + img->outBufs[0] = img->outBufs[1] = img->outBufs[2] = img->outBufs[3] = nullptr; // The encoding type 3 needs all 4 planes to be decoded to generate row of // RGGB values. It seems to be using some other colour space for raw encoding @@ -2402,7 +2406,7 @@ int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, return crxReadImageHeaders(hdr, img); } -int crxFreeImageData(CrxImage* img) { +static int crxFreeImageData(CrxImage* img) { CrxTile* tile = img->tiles; int nTiles = img->tileRows * img->tileCols; @@ -2415,12 +2419,12 @@ int crxFreeImageData(CrxImage* img) { free(tile[curTile].qStep); } free(img->tiles); - img->tiles = 0; + img->tiles = nullptr; } if (img->planeBuf) { free(img->planeBuf); - img->planeBuf = 0; + img->planeBuf = nullptr; } return 0; @@ -2475,7 +2479,7 @@ void CrxDecompressor::decode(const IsoMCanonCmp1Box& cmp1Box, auto storage = rawspeed::Buffer::Create(bufLen); const rawspeed::Buffer outBuf(storage.get(), bufLen); - if (crxSetupImageData(&hdr, &img, (int16_t*)storage.get())) { + if (crxSetupImageData(&hdr, &img, reinterpret_cast(storage.get()))) { ThrowRDE("Crx image setup failed"); } crxLoadDecodeLoop(&img, hdr.nPlanes); From 9375745b2ba319d49017af498e216111cfd78d61 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Mon, 26 Jul 2021 21:59:09 +0200 Subject: [PATCH 19/27] Add support for missing Canon CR3 cameras * Canon EOS 250D * Canon EOS M6 Mark II * Canon EOS M200 * Canon EOS M50 Mark II * Canon PowerShot G5 X Mark II * Canon PowerShot G7 X Mark III --- data/cameras.xml | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index ca7a5bcc8..a091cfce3 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -154,6 +154,21 @@ + + Canon EOS 250D + + RED + GREEN + GREEN + BLUE + + + + + + + + Canon EOS 300D @@ -1741,6 +1756,24 @@ + + Canon EOS M6 Mark II + + RED + GREEN + GREEN + BLUE + + + + + + + + + + + Canon EOS M10 @@ -1794,6 +1827,23 @@ + + Canon EOS M200 + + RED + GREEN + GREEN + BLUE + + + + + + + + + + Canon EOS-1D @@ -2275,6 +2325,23 @@ + + Canon EOS M50 Mark II + + RED + GREEN + GREEN + BLUE + + + + + + + + + + Canon PowerShot Pro1 @@ -2451,6 +2518,19 @@ + + Canon PowerShot G5 X Mark II + + RED + GREEN + GREEN + BLUE + + + + + + Canon PowerShot G6 @@ -2527,6 +2607,19 @@ + + Canon PowerShot G7 X Mark III + + RED + GREEN + GREEN + BLUE + + + + + + Canon PowerShot G1 X From 5988c5b08e20156e3cb26da1c28ad8a4911c937a Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sun, 8 Aug 2021 02:00:45 -0400 Subject: [PATCH 20/27] Address static analisys errors. --- .clang-tidy | 2 +- src/librawspeed/decompressors/CrxDecompressor.cpp | 13 +++++++------ src/librawspeed/tiff/IsoMBox.h | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index adee37064..a10a9f5be 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -50,5 +50,5 @@ CheckOptions: - key: modernize-use-default-member-init.UseAssignment value: 1 - key: cppcoreguidelines-macro-usage.AllowedRegexp - value: 'DEBUG|_GLIBCXX_SANITIZE_VECTOR|RAWSPEED_SOURCE_DIR|STR|XSTR|BSWAP16|BSWAP32|BSWAP64|ThrowExceptionHelper|ThrowIOE|ThrowRSE|ThrowCME|ThrowRDE|ThrowRPE|ThrowTPE|ThrowFIE|ThrowCPE|ThrowFPE|DECODER|fuji_quant_gradient|JPEG_MEMSRC|RLVTABLE|PRECISION_MIN|PRECISION_MAX|MARKER_BAND_END|SQR|RS_CAMERAS_XML_PATH|FULLDECODE|IMPL|IMPL0|IMPL1|PUMP|DECODE|PARSER|GEN_E|GEN_PFS|GEN_PSS|BENCHMARK_CAPTURE_NAME|OMPSHAREDCLAUSE|RAWSPEED_UNLIKELY_FUNCTION|RAWSPEED_NOINLINE' + value: 'DEBUG|_GLIBCXX_SANITIZE_VECTOR|RAWSPEED_SOURCE_DIR|STR|XSTR|BSWAP16|BSWAP32|BSWAP64|ThrowExceptionHelper|ThrowIOE|ThrowIPE|ThrowRSE|ThrowCME|ThrowRDE|ThrowRPE|ThrowTPE|ThrowFIE|ThrowCPE|ThrowFPE|DECODER|fuji_quant_gradient|JPEG_MEMSRC|RLVTABLE|PRECISION_MIN|PRECISION_MAX|MARKER_BAND_END|SQR|RS_CAMERAS_XML_PATH|FULLDECODE|IMPL|IMPL0|IMPL1|PUMP|DECODE|PARSER|GEN_E|GEN_PFS|GEN_PSS|BENCHMARK_CAPTURE_NAME|OMPFIRSTPRIVATECLAUSE|OMPSHAREDCLAUSE|RAWSPEED_UNLIKELY_FUNCTION|RAWSPEED_NOINLINE' ... diff --git a/src/librawspeed/decompressors/CrxDecompressor.cpp b/src/librawspeed/decompressors/CrxDecompressor.cpp index dc85719ac..62d2bf76a 100644 --- a/src/librawspeed/decompressors/CrxDecompressor.cpp +++ b/src/librawspeed/decompressors/CrxDecompressor.cpp @@ -1855,8 +1855,9 @@ static int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, if (img->levels > 2) totalHeight += qpHeight8; - tile->qStep = (CrxQStep*)malloc(totalHeight * qpWidth * sizeof(uint32_t) + - img->levels * sizeof(CrxQStep)); + size_t const qStepSize = totalHeight * size_t(qpWidth) * sizeof(uint32_t) + + img->levels * sizeof(CrxQStep); + tile->qStep = (CrxQStep*)malloc(qStepSize); if (!tile->qStep) return -1; @@ -2355,9 +2356,9 @@ static int crxSetupImageData(const IsoMCanonCmp1Box* hdr, CrxImage* img, // intermediate plane buffer. At the moment though it's too many changes so // left as is. if (img->encType == 3 && img->nPlanes == 4 && img->nBits > 8) { - img->planeBuf = - (int16_t*)malloc(img->planeHeight * img->planeWidth * img->nPlanes * - ((img->samplePrecision + 7) >> 3)); + size_t const planeBufSize = img->planeHeight * img->planeWidth * img->nPlanes * + (size_t(img->samplePrecision + 7) >> 3); + img->planeBuf = (int16_t*)malloc(planeBufSize); if (!img->planeBuf) ThrowRDE("Crx decompression error"); } @@ -2466,7 +2467,7 @@ void CrxDecompressor::decode(const IsoMCanonCmp1Box& cmp1Box, // Bytes required for decompression output Buffer::size_type bufLen = - cmp1Box.f_height * cmp1Box.f_width * sizeof(uint16_t); + size_t(cmp1Box.f_height) * size_t(cmp1Box.f_width) * sizeof(uint16_t); // update sizes for the planes if (hdr.nPlanes == 4) { diff --git a/src/librawspeed/tiff/IsoMBox.h b/src/librawspeed/tiff/IsoMBox.h index 3cde4361f..bcecc2c79 100644 --- a/src/librawspeed/tiff/IsoMBox.h +++ b/src/librawspeed/tiff/IsoMBox.h @@ -73,7 +73,7 @@ struct IsoMMediaDataBox; // The most basic box. struct AbstractIsoMBox { - typedef std::array UuidType; + using UuidType = std::array; ByteStream data; From ef7d3992ab9503583662b08da11df7e0f5d27b22 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 10 Sep 2021 20:42:15 +0200 Subject: [PATCH 21/27] Use Buffer only as reference, not as pointer --- src/librawspeed/decoders/Cr3Decoder.h | 2 +- src/librawspeed/parsers/IsoMParser.cpp | 4 ++-- src/librawspeed/parsers/IsoMParser.h | 2 +- src/librawspeed/tiff/IsoMBox.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.h b/src/librawspeed/decoders/Cr3Decoder.h index 89826ea81..8924feb72 100644 --- a/src/librawspeed/decoders/Cr3Decoder.h +++ b/src/librawspeed/decoders/Cr3Decoder.h @@ -366,7 +366,7 @@ class Cr3Decoder final : public RawDecoder { public: static bool isAppropriateDecoder(const IsoMRootBox& box); - Cr3Decoder(std::unique_ptr rootBox_, const Buffer* file) + Cr3Decoder(std::unique_ptr rootBox_, const Buffer& file) : RawDecoder(file), rootBox(std::move(rootBox_)) {} RawImage decodeRawInternal() override; diff --git a/src/librawspeed/parsers/IsoMParser.cpp b/src/librawspeed/parsers/IsoMParser.cpp index eb8a2d78d..dde7c3c61 100644 --- a/src/librawspeed/parsers/IsoMParser.cpp +++ b/src/librawspeed/parsers/IsoMParser.cpp @@ -28,10 +28,10 @@ namespace rawspeed { -IsoMParser::IsoMParser(const Buffer* inputData) : RawParser(inputData) {} +IsoMParser::IsoMParser(const Buffer& inputData) : RawParser(inputData) {} void IsoMParser::parseData() { - ByteStream bs(DataBuffer(*mInput, Endianness::unknown)); + ByteStream bs(DataBuffer(mInput, Endianness::unknown)); // The 'ISO base media file format' is big-endian. bs.setByteOrder(Endianness::big); diff --git a/src/librawspeed/parsers/IsoMParser.h b/src/librawspeed/parsers/IsoMParser.h index 08d1c2ba5..677f858fc 100644 --- a/src/librawspeed/parsers/IsoMParser.h +++ b/src/librawspeed/parsers/IsoMParser.h @@ -39,7 +39,7 @@ class IsoMParser final : public RawParser { void parseData(); public: - explicit IsoMParser(const Buffer* input); + explicit IsoMParser(const Buffer& input); std::unique_ptr getDecoder(const CameraMetaData* meta = nullptr) override; diff --git a/src/librawspeed/tiff/IsoMBox.cpp b/src/librawspeed/tiff/IsoMBox.cpp index 286904999..116314cd3 100644 --- a/src/librawspeed/tiff/IsoMBox.cpp +++ b/src/librawspeed/tiff/IsoMBox.cpp @@ -546,7 +546,7 @@ void IsoMMediaDataBox::parse(IsoMRootBox* root) { if (!RangesAreNested(mData, chunk)) ThrowIPE("Chunk is not in the mdat box."); // Does it overlap with any previous chunk? - if (!clc.emplace(chunk).second) + if (!clc.insert(chunk)) ThrowIPE("Two chunks overlap."); // OK! chunks.emplace_back(chunk); From 0da9d1e73d32b578da367680a324e2c2d1d4e568 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 10 Sep 2021 20:42:50 +0200 Subject: [PATCH 22/27] FIX: Building issue with darktable --- src/librawspeed/io/Buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librawspeed/io/Buffer.h b/src/librawspeed/io/Buffer.h index 3e0bd1f37..f130ed7ae 100644 --- a/src/librawspeed/io/Buffer.h +++ b/src/librawspeed/io/Buffer.h @@ -216,7 +216,7 @@ class Buffer // WARNING: both buffers must belong to the same allocation, else this is UB! inline bool operator<(const Buffer& lhs, const Buffer& rhs) { - return std::pair(lhs.begin(), lhs.end()) < std::pair(rhs.begin(), rhs.end()); + return std::make_pair(lhs.begin(), lhs.end()) < std::make_pair(rhs.begin(), rhs.end()); } /* From 2f1fdfa40ea2727dd45e191098c6353c1d4a84b2 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Fri, 10 Sep 2021 20:43:26 +0200 Subject: [PATCH 23/27] CR3: Add missing compressor versions that are actually supported --- src/librawspeed/decoders/Cr3Decoder.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index 402debf8e..1fb46293a 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -559,7 +559,8 @@ RawImage Cr3Decoder::decodeRawInternal() { } bool Cr3Decoder::isCodecSupported(const std::string& compressorVersion) const { - if (compressorVersion == "CanonHEIF001/10.00.00/00.00.00") { + if (compressorVersion == "CanonHEIF001/10.00.00/00.00.00" + || compressorVersion == "CanonHEIF001/10.00.01/00.00.00") { writeLog(DEBUG_PRIO_WARNING, "HEIF CNCV: '%s' is not supported", compressorVersion.c_str()); } @@ -575,11 +576,16 @@ bool Cr3Decoder::isCodecSupported(const std::string& compressorVersion) const { return compressorVersion == "CanonCR3_001/00.10.00/00.00.00" // EOS R5, R6 and 1DX Mark III // (raw) + || compressorVersion == + "CanonCR3_003/00.10.00/00.00.00" // R6 (craw with HDR preview), + // R5 (craw HDR, FW 1.2.0) || compressorVersion == "CanonCR3_002/00.10.00/00.00.00" // CR3 of 1DX Mark III (craw) + || compressorVersion == + "CanonCR3_001/01.09.00/00.00.00" // SX70 HS, G5 Mark II and G7 Mark III || compressorVersion == "CanonCR3_001/00.09.00/00.00.00"; // EOS R, EOS RP, M50, 250D, - // 90D, M6 Mark II, M200 + // 90D, M6 Mark II, M200, M50m2 and 250D } void Cr3Decoder::checkSupportInternal(const CameraMetaData* meta) { From 0d194515717c789a2392fa964c4ddf5df9a771c9 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 15 Feb 2022 18:10:00 +0100 Subject: [PATCH 24/27] Restore RangeTest.h from develop branch --- test/librawspeed/common/RangeTest.h | 59 ----------------------------- 1 file changed, 59 deletions(-) diff --git a/test/librawspeed/common/RangeTest.h b/test/librawspeed/common/RangeTest.h index f12a219f0..45a7a3444 100644 --- a/test/librawspeed/common/RangeTest.h +++ b/test/librawspeed/common/RangeTest.h @@ -70,29 +70,6 @@ ::testing::AssertionResult RangeDoesntContain(const char* range_expr, << " (" << pos << ")"; } -template -::testing::AssertionResult RangesNest(const char* m_expr, const char* n_expr, - const T& outer, const T& inner) { - if (RangesAreNested(outer, inner)) - return ::testing::AssertionSuccess(); - - return ::testing::AssertionFailure() - << "Range " << m_expr << " (" << outer << ") does not contain range " - << n_expr << " (" << inner << ")"; -} - -template -::testing::AssertionResult RangesDontNest(const char* m_expr, - const char* n_expr, const T& outer, - const T& inner) { - if (!RangesAreNested(outer, inner)) - return ::testing::AssertionSuccess(); - - return ::testing::AssertionFailure() - << "Range " << m_expr << " (" << outer << ") does contain range " - << n_expr << " (" << inner << ")"; -} - template ::testing::AssertionResult RangesOverlap(const char* m_expr, const char* n_expr, const T& lhs, const T& rhs) { @@ -163,40 +140,4 @@ static const std::set AllOverlapped{ std::make_tuple(2, 2, 2, 2), }; -using threeRangesType = std::tuple; -class ThreeRangesTest : public ::testing::TestWithParam { -protected: - ThreeRangesTest() = default; - virtual void SetUp() { - r0 = Range(std::get<0>(GetParam()), std::get<1>(GetParam())); - r1 = Range(std::get<2>(GetParam()), std::get<3>(GetParam())); - r2 = Range(std::get<4>(GetParam()), std::get<5>(GetParam())); - } - - Range r0; - Range r1; - Range r2; -}; -INSTANTIATE_TEST_CASE_P( - Unsigned, ThreeRangesTest, - testing::Combine(testing::Range(0, 3), testing::Range(0U, 3U), - testing::Range(0, 3), testing::Range(0U, 3U), - testing::Range(0, 3), testing::Range(0U, 3U))); -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThreeRangesTest); - -static const std::set AllNested{ - std::make_tuple(0, 0, 0, 0), std::make_tuple(0, 1, 0, 0), - std::make_tuple(0, 1, 0, 1), std::make_tuple(0, 1, 1, 0), - std::make_tuple(0, 2, 0, 0), std::make_tuple(0, 2, 0, 1), - std::make_tuple(0, 2, 0, 2), std::make_tuple(0, 2, 1, 0), - std::make_tuple(0, 2, 1, 1), std::make_tuple(0, 2, 2, 0), - std::make_tuple(1, 0, 1, 0), std::make_tuple(1, 1, 1, 0), - std::make_tuple(1, 1, 1, 1), std::make_tuple(1, 1, 2, 0), - std::make_tuple(1, 2, 1, 0), std::make_tuple(1, 2, 1, 1), - std::make_tuple(1, 2, 1, 2), std::make_tuple(1, 2, 2, 0), - std::make_tuple(1, 2, 2, 1), std::make_tuple(2, 0, 2, 0), - std::make_tuple(2, 1, 2, 0), std::make_tuple(2, 1, 2, 1), - std::make_tuple(2, 2, 2, 0), std::make_tuple(2, 2, 2, 1), - std::make_tuple(2, 2, 2, 2)}; - } // namespace rawspeed_test From 33fa98f246d8b4b93552f3c13607994ab3eb12a3 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 15 Feb 2022 18:10:00 +0100 Subject: [PATCH 25/27] Make code compatible to latest develop branch --- src/librawspeed/decoders/Cr3Decoder.cpp | 32 +++++++++---------- .../decompressors/CrxDecompressor.cpp | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/librawspeed/decoders/Cr3Decoder.cpp b/src/librawspeed/decoders/Cr3Decoder.cpp index 1fb46293a..d45bd52f6 100644 --- a/src/librawspeed/decoders/Cr3Decoder.cpp +++ b/src/librawspeed/decoders/Cr3Decoder.cpp @@ -441,7 +441,7 @@ IsoMCanonIad1Box::IsoMCanonIad1Box(const AbstractIsoMBox& base) ThrowRDE("IAD1 box contains small image information, but big image expected"); } - writeLog(DEBUG_PRIO_EXTRA, + writeLog(DEBUG_PRIO::EXTRA, "IAD1 sensor width: %d, height: %d, crop: %u, %u, %u, %u, black " "area left: %u, top: %u", sensorWidth, sensorHeight, cropLeftOffset, cropTopOffset, @@ -561,15 +561,15 @@ RawImage Cr3Decoder::decodeRawInternal() { bool Cr3Decoder::isCodecSupported(const std::string& compressorVersion) const { if (compressorVersion == "CanonHEIF001/10.00.00/00.00.00" || compressorVersion == "CanonHEIF001/10.00.01/00.00.00") { - writeLog(DEBUG_PRIO_WARNING, "HEIF CNCV: '%s' is not supported", + writeLog(DEBUG_PRIO::WARNING, "HEIF CNCV: '%s' is not supported", compressorVersion.c_str()); } if (compressorVersion == "CanonCR3_001/01.09.00/01.00.00") { - writeLog(DEBUG_PRIO_WARNING, "Raw-burst roll CNCV: '%s' is not supported", + writeLog(DEBUG_PRIO::WARNING, "Raw-burst roll CNCV: '%s' is not supported", compressorVersion.c_str()); } if (compressorVersion == "CanonCRM0001/02.09.00/00.00.00") { - writeLog(DEBUG_PRIO_WARNING, "CRM movies CNCV: '%s' is not supported", + writeLog(DEBUG_PRIO::WARNING, "CRM movies CNCV: '%s' is not supported", compressorVersion.c_str()); } @@ -596,7 +596,7 @@ void Cr3Decoder::checkSupportInternal(const CameraMetaData* meta) { // Check compressor version string auto compressorVersion = canonBox->CNCV()->compressorVersion; - writeLog(DEBUG_PRIO_EXTRA, "Compressor Version: %s", + writeLog(DEBUG_PRIO::ERROR, "Compressor Version: %s", compressorVersion.c_str()); if (!isCodecSupported(compressorVersion)) { ThrowRDE("CR3 compressor version (CNCV: %s) is not supported", @@ -605,8 +605,8 @@ void Cr3Decoder::checkSupportInternal(const CameraMetaData* meta) { // CMT1 contains a TIFF file with EXIF information auto camId = canonBox->CMT1()->mRootIFD0->getID(); - writeLog(DEBUG_PRIO_EXTRA, "CMT1 EXIF make: %s", camId.make.c_str()); - writeLog(DEBUG_PRIO_EXTRA, "CMT1 EXIF model: %s", camId.model.c_str()); + writeLog(DEBUG_PRIO::EXTRA, "CMT1 EXIF make: %s", camId.make.c_str()); + writeLog(DEBUG_PRIO::EXTRA, "CMT1 EXIF model: %s", camId.model.c_str()); // Load CRAW box auto& stsd = rootBox->moov()->tracks[2].mdia->minf->stbl->stsd; @@ -619,17 +619,17 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { const auto camId = canonBox->CMT1()->mRootIFD0->getID(); uint32_t iso = 0; - if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(ISOSPEEDRATINGS)) { + if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(TiffTag::ISOSPEEDRATINGS)) { iso = canonBox->CMT2() - ->mRootIFD0->getEntryRecursive(ISOSPEEDRATINGS) + ->mRootIFD0->getEntryRecursive(TiffTag::ISOSPEEDRATINGS) ->getU32(); } if(65535 == iso) { // ISOSPEEDRATINGS is a SHORT EXIF value. For larger values, we have to look // at RECOMMENDED_EXPOSURE_INDEX (maybe Canon specific). - if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(RECOMMENDEDEXPOSUREINDEX)) + if (canonBox->CMT2()->mRootIFD0->hasEntryRecursive(TiffTag::RECOMMENDEDEXPOSUREINDEX)) iso = canonBox->CMT2() - ->mRootIFD0->getEntryRecursive(RECOMMENDEDEXPOSUREINDEX) + ->mRootIFD0->getEntryRecursive(TiffTag::RECOMMENDEDEXPOSUREINDEX) ->getU32(); } @@ -663,8 +663,8 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { TiffRootIFD IFD_ctmd_rec8(nullptr, &rs, DataBuffer(rec8, Endianness::little), 8); // skip TIFF header - if (IFD_ctmd_rec8.hasEntryRecursive(CANONCOLORDATA)) { - TiffEntry* wb = IFD_ctmd_rec8.getEntryRecursive(CANONCOLORDATA); + if (IFD_ctmd_rec8.hasEntryRecursive(TiffTag::CANONCOLORDATA)) { + TiffEntry* wb = IFD_ctmd_rec8.getEntryRecursive(TiffTag::CANONCOLORDATA); // this entry is a big table, and different cameras store used WB in // different parts, so find the offset, default is the most common one. // The wb_offset values in cameras.xml are extracted from: @@ -676,11 +676,11 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { wb_coeffs[2] = 0; // GG wb_coeffs[3] = static_cast(wb->getU16(offset + 3)) / 1024.0; - writeLog(DEBUG_PRIO_EXTRA, "wb_coeffs:, 0: %f, 1: %f, 2: %f, 3: %f\n", + writeLog(DEBUG_PRIO::EXTRA, "wb_coeffs:, 0: %f, 1: %f, 2: %f, 3: %f\n", wb_coeffs[0], wb_coeffs[1], wb_coeffs[2], wb_coeffs[3]); } else { - writeLog(DEBUG_PRIO_EXTRA, "no wb_coeffs found"); + writeLog(DEBUG_PRIO::EXTRA, "no wb_coeffs found"); } // No CR3 camera has swapped_wb so far, but who knows... @@ -695,7 +695,7 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) { } setMetaData(meta, camId.make, camId.model, mode, iso); - writeLog(DEBUG_PRIO_EXTRA, "blacklevel for ISO %d is %d", mRaw->metadata.isoSpeed, mRaw->blackLevel); + writeLog(DEBUG_PRIO::EXTRA, "blacklevel for ISO %d is %d", mRaw->metadata.isoSpeed, mRaw->blackLevel); // IAD1 describes sensor constraints const auto& iad1 = crawBox->CDI1()->IAD1(); diff --git a/src/librawspeed/decompressors/CrxDecompressor.cpp b/src/librawspeed/decompressors/CrxDecompressor.cpp index 62d2bf76a..1c2527ece 100644 --- a/src/librawspeed/decompressors/CrxDecompressor.cpp +++ b/src/librawspeed/decompressors/CrxDecompressor.cpp @@ -2500,7 +2500,7 @@ void CrxDecompressor::decode(const IsoMCanonCmp1Box& cmp1Box, CrxDecompressor::CrxDecompressor(const RawImage& img) : AbstractDecompressor(), mRaw(img) { - if (mRaw->getDataType() != TYPE_USHORT16) + if (mRaw->getDataType() != RawImageType::UINT16) ThrowRDE("Unexpected data type"); if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == sizeof(uint16_t)))) From c3fed2632163bd6852244d760c8bc39936e5a108 Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 15 Feb 2022 18:10:00 +0100 Subject: [PATCH 26/27] Add ColorMatrix tag for CR3 cameras --- data/cameras.xml | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/data/cameras.xml b/data/cameras.xml index a091cfce3..035c97658 100644 --- a/data/cameras.xml +++ b/data/cameras.xml @@ -168,6 +168,13 @@ + + + 9079 -1923 -1236 + -4677 12454 2492 + -922 2319 5565 + + Canon EOS 300D @@ -752,6 +759,13 @@ + + + 9079 -1923 -1236 + -4677 12454 2492 + -922 2319 5565 + + Canon EOS 760D @@ -1773,6 +1787,13 @@ + + + 11498 -3759 -1516 + -5073 12954 2349 + -892 1867 6118 + + Canon EOS M10 @@ -1843,6 +1864,13 @@ + + + 10463 -2173 -1437 + -4856 12635 2482 + -1216 2915 7237 + + Canon EOS-1D @@ -2238,6 +2266,13 @@ + + + 8971 -2022 -1242 + -5405 13249 2380 + -1280 2483 6072 + + Canon EOS R @@ -2255,6 +2290,13 @@ + + + 8293 -1789 -1094 + -5025 12925 2327 + -1199 2769 6108 + + Canon EOS RP @@ -2272,6 +2314,13 @@ + + + 8608 -2097 -1178 + -5425 13265 2383 + -1149 2238 5680 + + Canon EOS R5 @@ -2288,6 +2337,13 @@ + + + 9766 -2953 -1254 + -4276 12116 2433 + -437 1336 5131 + + Canon EOS R6 @@ -2303,6 +2359,13 @@ + + + 8293 -1611 -1132 + -4759 12711 2275 + -1013 2415 5509 + + Canon EOS M50 @@ -2324,6 +2387,13 @@ + + + 8532 -701 -1167 + -4095 11879 2508 + -797 2424 7010 + + Canon EOS M50 Mark II @@ -2341,6 +2411,13 @@ + + + 10463 -2173 -1437 + -4856 12635 2482 + -1216 2915 7237 + + Canon PowerShot Pro1 @@ -2530,6 +2607,13 @@ + + + 11629 -5713 -914 + -2706 11090 1842 + -206 1225 5515 + + Canon PowerShot G6 @@ -2619,6 +2703,13 @@ + + + 11629 -5713 -914 + -2706 11090 1842 + -206 1225 5515 + + Canon PowerShot G1 X From 09ca1dfb9b6c61194c3db2460cd53b37454ab69d Mon Sep 17 00:00:00 2001 From: Daniel Vogelbacher Date: Tue, 15 Feb 2022 18:10:00 +0100 Subject: [PATCH 27/27] Fix compiler errors for unused check() result Cleanup reserved macro and function names for llvm --- .../decompressors/CrxDecompressor.cpp | 77 +++++++++---------- src/librawspeed/tiff/IsoMBox.cpp | 4 +- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/librawspeed/decompressors/CrxDecompressor.cpp b/src/librawspeed/decompressors/CrxDecompressor.cpp index 1c2527ece..400535f2b 100644 --- a/src/librawspeed/decompressors/CrxDecompressor.cpp +++ b/src/librawspeed/decompressors/CrxDecompressor.cpp @@ -39,26 +39,23 @@ #include // for initializer_list // this should be divisible by 4 -#define CRX_BUF_SIZE 0x10000 +#define CRX_BUF_SIZE (uint64_t)0x10000 -// Definitions copied from libraw -#ifdef _abs -#undef _abs -#undef _min -#undef _constrain -#endif -#define _abs(x) (((x) ^ ((int32_t)(x) >> 31)) - ((int32_t)(x) >> 31)) -#define _min(a, b) ((a) < (b) ? (a) : (b)) -#define _constrain(x, l, u) ((x) < (l) ? (l) : ((x) > (u) ? (u) : (x))) +#define crx_constrain(x, l, u) ((x) < (l) ? (l) : ((x) > (u) ? (u) : (x))) #if !defined(_WIN32) || \ (defined(__GNUC__) && !defined(__INTRINSIC_SPECIAL__BitScanReverse)) /* __INTRINSIC_SPECIAL__BitScanReverse found in MinGW32-W64 v7.30 headers, may * be there is a better solution? */ typedef uint32_t DWORD; -inline void _BitScanReverse(DWORD* Index, unsigned long Mask) { +inline void crx_BitScanReverse(DWORD* Index, unsigned long Mask) { *Index = sizeof(unsigned long) * 8 - 1 - __builtin_clzl(Mask); } +#else +typedef uint32_t DWORD; +inline void crx_BitScanReverse(DWORD* Index, unsigned long Mask) { + _BitScanReverse(Index, Mask); +} #endif namespace rawspeed { @@ -217,7 +214,7 @@ static inline void crxFillBuffer(CrxBitstream* bitStrm) { auto sub = bitStrm->crxRawData.getSubView(bitStrm->curBufOffset); bitStrm->mdatBuf.resize(CRX_BUF_SIZE); - auto bytesToRead = _min(bitStrm->mdatSize, CRX_BUF_SIZE); + auto bytesToRead = std::min(bitStrm->mdatSize, CRX_BUF_SIZE); if (sub.getSize() >= bytesToRead) { auto data = sub.getData(0, bytesToRead); @@ -238,7 +235,7 @@ inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { int32_t result = 0; if (bitStrm->bitData) { - _BitScanReverse(static_cast(&nonZeroBit), static_cast(bitStrm->bitData)); + crx_BitScanReverse(static_cast(&nonZeroBit), static_cast(bitStrm->bitData)); result = 31 - nonZeroBit; bitStrm->bitData <<= 32 - nonZeroBit; bitStrm->bitsLeft -= 32 - nonZeroBit; @@ -251,7 +248,7 @@ inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { bitStrm->curPos += 4; crxFillBuffer(bitStrm); if (nextData) { - _BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); + crx_BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); result = bitsLeft + 31 - nonZeroBit; bitStrm->bitData = nextData << (32 - nonZeroBit); bitStrm->bitsLeft = nonZeroBit; @@ -267,7 +264,7 @@ inline int crxBitstreamGetZeros(CrxBitstream* bitStrm) { break; bitsLeft += 8; } - _BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); + crx_BitScanReverse(static_cast(&nonZeroBit), static_cast(nextData)); result = static_cast(bitsLeft + 7 - nonZeroBit); bitStrm->bitData = nextData << (32 - nonZeroBit); bitStrm->bitsLeft = nonZeroBit; @@ -356,7 +353,7 @@ inline void crxDecodeSymbolL1(CrxBandParam* param, int32_t doMedianPrediction, // for not end of the line - use one symbol ahead to estimate next K if (notEOL) { int32_t nextDelta = (param->lineBuf0[2] - param->lineBuf0[1]) << 1; - bitCode = (bitCode + _abs(nextDelta)) >> 1; + bitCode = (bitCode + std::abs(nextDelta)) >> 1; ++param->lineBuf0; } @@ -461,7 +458,7 @@ inline void crxDecodeSymbolL1Rounded(CrxBandParam* param, int32_t doSym = 1, } param->kParam = crxPredictKParameter(param->kParam, - (bitCode + 2 * _abs(code)) >> 1, 15); + (bitCode + 2 * std::abs(code)) >> 1, 15); } else param->kParam = crxPredictKParameter(param->kParam, bitCode, 15); @@ -476,12 +473,12 @@ static int crxDecodeLineRounded(CrxBandParam* param) { int32_t length = param->subbandWidth; for (; length > 1; --length) { - if (_abs(param->lineBuf0[2] - param->lineBuf0[1]) > + if (std::abs(param->lineBuf0[2] - param->lineBuf0[1]) > param->roundedBitsMask) { crxDecodeSymbolL1Rounded(param); ++param->lineBuf0; valueReached = 1; - } else if (valueReached || _abs(param->lineBuf0[0] - param->lineBuf1[0]) > + } else if (valueReached || std::abs(param->lineBuf0[0] - param->lineBuf1[0]) > param->roundedBitsMask) { crxDecodeSymbolL1Rounded(param); ++param->lineBuf0; @@ -524,7 +521,7 @@ static int crxDecodeLineRounded(CrxBandParam* param) { if (length > 1) { crxDecodeSymbolL1Rounded(param, 0); ++param->lineBuf0; - valueReached = _abs(param->lineBuf0[1] - param->lineBuf0[0]) > + valueReached = std::abs(param->lineBuf0[1] - param->lineBuf0[0]) > param->roundedBitsMask; } else if (length == 1) crxDecodeSymbolL1Rounded(param, 0, 0); @@ -720,7 +717,7 @@ static int crxDecodeTopLineRounded(CrxBandParam* param) { // read the line from bitstream for (; length > 1; --length) { - if (_abs(param->lineBuf1[0]) > param->roundedBitsMask) + if (std::abs(param->lineBuf1[0]) > param->roundedBitsMask) param->lineBuf1[1] = param->lineBuf1[0]; else { int nSyms = 0; @@ -1016,7 +1013,7 @@ static int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { for (int i = 0; i < band->colStartAddOn; ++i) { uint32_t quantVal = band->qStepBase + ((qStepTblPtr[0] * band->qStepMult) >> 3); - bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + bandBuf[i] *= crx_constrain(quantVal, 1, 0x168000); } for (int i = band->colStartAddOn; i < band->width - band->colEndAddOn; @@ -1026,14 +1023,14 @@ static int crxDecodeLineWithIQuantization(CrxSubband* band, CrxQStep* qStep) { ((qStepTblPtr[(i - band->colStartAddOn) >> band->levelShift] * band->qStepMult) >> 3); - bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + bandBuf[i] *= crx_constrain(quantVal, 1, 0x168000); } int lastIdx = (band->width - band->colEndAddOn - band->colStartAddOn - 1) >> band->levelShift; for (int i = band->width - band->colEndAddOn; i < band->width; ++i) { uint32_t quantVal = band->qStepBase + ((qStepTblPtr[lastIdx] * band->qStepMult) >> 3); - bandBuf[i] *= _constrain(quantVal, 1, 0x168000); + bandBuf[i] *= crx_constrain(quantVal, 1, 0x168000); } } else { // prev. version @@ -1525,7 +1522,7 @@ static void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, --maxVal; for (int i = 0; i < lineLength; i++) img->outBufs[plane][rawOffset + 2 * i] = - _constrain(lineData[i], minVal, maxVal); + crx_constrain(lineData[i], minVal, maxVal); } else if (img->encType == 3) { // copy to intermediate planeBuf rawOffset = plane * img->planeWidth * img->planeHeight + @@ -1537,14 +1534,14 @@ static void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, int32_t maxVal = (1 << img->nBits) - 1; for (int i = 0; i < lineLength; i++) img->outBufs[plane][rawOffset + 2 * i] = - _constrain(median + lineData[i], 0, maxVal); + crx_constrain(median + lineData[i], 0, maxVal); } else if (img->nPlanes == 1) { int32_t maxVal = (1 << img->nBits) - 1; int32_t median = 1 << (img->nBits - 1); rawOffset = img->planeWidth * imageRow + imageCol; for (int i = 0; i < lineLength; i++) img->outBufs[0][rawOffset + i] = - _constrain(median + lineData[i], 0, maxVal); + crx_constrain(median + lineData[i], 0, maxVal); } } else if (img->encType == 3 && img->planeBuf) { int32_t planeSize = img->planeWidth * img->planeHeight; @@ -1563,22 +1560,22 @@ static void crxConvertPlaneLine(CrxImage* img, int imageRow, int imageCol = 0, median + (plane0[i] << 10) - 168 * plane1[i] - 585 * plane3[i]; int32_t val = 0; if (gr < 0) - gr = -(((_abs(gr) + 512) >> 9) & ~1); + gr = -(((std::abs(gr) + 512) >> 9) & ~1); else - gr = ((_abs(gr) + 512) >> 9) & ~1; + gr = ((std::abs(gr) + 512) >> 9) & ~1; // Essentially R = round(median + P0 + 1.474*P3) val = (median + (plane0[i] << 10) + 1510 * plane3[i] + 512) >> 10; - img->outBufs[0][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + img->outBufs[0][rawLineOffset + 2 * i] = crx_constrain(val, 0, maxVal); // Essentially G1 = round(median + P0 + P2 - 0.164*P1 - 0.571*P3) val = (plane2[i] + gr + 1) >> 1; - img->outBufs[1][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + img->outBufs[1][rawLineOffset + 2 * i] = crx_constrain(val, 0, maxVal); // Essentially G2 = round(median + P0 - P2 - 0.164*P1 - 0.571*P3) val = (gr - plane2[i] + 1) >> 1; - img->outBufs[2][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + img->outBufs[2][rawLineOffset + 2 * i] = crx_constrain(val, 0, maxVal); // Essentially B = round(median + P0 + 1.881*P1) val = (median + (plane0[i] << 10) + 1927 * plane1[i] + 512) >> 10; - img->outBufs[3][rawLineOffset + 2 * i] = _constrain(val, 0, maxVal); + img->outBufs[3][rawLineOffset + 2 * i] = crx_constrain(val, 0, maxVal); } } } @@ -1832,7 +1829,7 @@ static void crxDecodeGolombNormal(CrxBitstream* bitStrm, int32_t width, lineBuf1[1] += -(qp & 1) ^ (qp >> 1); if (width) { deltaH = lineBuf0[2] - lineBuf0[1]; - *kParam = crxPredictKParameter(*kParam, (qp + 2 * _abs(deltaH)) >> 1, 7); + *kParam = crxPredictKParameter(*kParam, (qp + 2 * std::abs(deltaH)) >> 1, 7); ++lineBuf0; } else *kParam = crxPredictKParameter(*kParam, qp, 7); @@ -1871,10 +1868,10 @@ static int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, qStep->width = qpWidth; qStep->height = qpHeight8; for (int qpRow = 0; qpRow < qpHeight8; ++qpRow) { - int row0Idx = qpWidth * _min(4 * qpRow, qpHeight - 1); - int row1Idx = qpWidth * _min(4 * qpRow + 1, qpHeight - 1); - int row2Idx = qpWidth * _min(4 * qpRow + 2, qpHeight - 1); - int row3Idx = qpWidth * _min(4 * qpRow + 3, qpHeight - 1); + int row0Idx = qpWidth * std::min(4 * qpRow, qpHeight - 1); + int row1Idx = qpWidth * std::min(4 * qpRow + 1, qpHeight - 1); + int row2Idx = qpWidth * std::min(4 * qpRow + 2, qpHeight - 1); + int row3Idx = qpWidth * std::min(4 * qpRow + 3, qpHeight - 1); for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl) { int32_t quantVal = qpTable[row0Idx++] + qpTable[row1Idx++] + @@ -1897,8 +1894,8 @@ static int crxMakeQStep(CrxImage* img, CrxTile* tile, int32_t* qpTable, qStep->width = qpWidth; qStep->height = qpHeight4; for (int qpRow = 0; qpRow < qpHeight4; ++qpRow) { - int row0Idx = qpWidth * _min(2 * qpRow, qpHeight - 1); - int row1Idx = qpWidth * _min(2 * qpRow + 1, qpHeight - 1); + int row0Idx = qpWidth * std::min(2 * qpRow, qpHeight - 1); + int row1Idx = qpWidth * std::min(2 * qpRow + 1, qpHeight - 1); for (int qpCol = 0; qpCol < qpWidth; ++qpCol, ++qStepTbl) { int32_t quantVal = (qpTable[row0Idx++] + qpTable[row1Idx++]) / 2; diff --git a/src/librawspeed/tiff/IsoMBox.cpp b/src/librawspeed/tiff/IsoMBox.cpp index 116314cd3..0b9459743 100644 --- a/src/librawspeed/tiff/IsoMBox.cpp +++ b/src/librawspeed/tiff/IsoMBox.cpp @@ -217,7 +217,7 @@ IsoMSampleToChunkBox::IsoMSampleToChunkBox(const AbstractIsoMBox& base) : IsoMFullBox(base) { const auto entryCount = data.getU32(); - data.check(entryCount, 3 * 4); + (void)data.check(entryCount, 3 * 4); dscs.reserve(entryCount); std::generate_n(std::back_inserter(dscs), entryCount, [this]() { Dsc d; @@ -278,7 +278,7 @@ IsoMChunkLargeOffsetBox::operator bool() const { IsoMChunkLargeOffsetBox::IsoMChunkLargeOffsetBox(const AbstractIsoMBox& base) : IsoMFullBox(base) { const auto entryCount = data.getU32(); - data.check(entryCount, 8); + (void)data.check(entryCount, 8); if (entryCount != 1) ThrowIPE("Don't know how to handle co64 box with %u entries", entryCount);