[gnome-photos/wip/rishi/buffer-decoder: 11/11] Add Rawspeed
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/buffer-decoder: 11/11] Add Rawspeed
- Date: Sat, 1 Jun 2019 20:11:57 +0000 (UTC)
commit 5cbd3397248f00f3ed29df9094cc216548e78d74
Author: Debarshi Ray <debarshir gnome org>
Date: Sat Jun 1 15:39:13 2019 +0000
Add Rawspeed
https://gitlab.gnome.org/GNOME/gnome-photos/issues/63
meson.build | 3 +
src/meson.build | 1 +
subprojects/rawspeed/meson.build | 53 ++
subprojects/rawspeed/meson_options.txt | 6 +
.../rawspeed/src/external/AddressSanitizer.h | 87 +++
.../rawspeed/src/external/ThreadSafetyAnalysis.h | 130 ++++
subprojects/rawspeed/src/external/meson.build | 1 +
.../rawspeed/src/librawspeed/RawSpeed-API.h | 43 ++
.../rawspeed/src/librawspeed/common/Array2DRef.h | 87 +++
.../src/librawspeed/common/ChecksumFile.cpp | 94 +++
.../rawspeed/src/librawspeed/common/ChecksumFile.h | 41 +
.../rawspeed/src/librawspeed/common/Common.cpp | 62 ++
.../rawspeed/src/librawspeed/common/Common.h | 238 ++++++
.../rawspeed/src/librawspeed/common/Cpuid.cpp | 49 ++
.../rawspeed/src/librawspeed/common/Cpuid.h | 30 +
.../common/DefaultInitAllocatorAdaptor.h | 101 +++
.../rawspeed/src/librawspeed/common/DngOpcodes.cpp | 592 +++++++++++++++
.../rawspeed/src/librawspeed/common/DngOpcodes.h | 73 ++
.../rawspeed/src/librawspeed/common/ErrorLog.cpp | 50 ++
.../rawspeed/src/librawspeed/common/ErrorLog.h | 41 +
.../common/GetNumberOfProcessorCores.cpp | 39 +
.../rawspeed/src/librawspeed/common/Memory.cpp | 107 +++
.../rawspeed/src/librawspeed/common/Memory.h | 100 +++
.../rawspeed/src/librawspeed/common/Mutex.h | 117 +++
.../rawspeed/src/librawspeed/common/NORangesSet.h | 37 +
.../rawspeed/src/librawspeed/common/Optional.h | 52 ++
.../rawspeed/src/librawspeed/common/Point.h | 212 ++++++
.../rawspeed/src/librawspeed/common/Range.h | 75 ++
.../rawspeed/src/librawspeed/common/RawImage.cpp | 582 ++++++++++++++
.../rawspeed/src/librawspeed/common/RawImage.h | 307 ++++++++
.../src/librawspeed/common/RawImageDataFloat.cpp | 393 ++++++++++
.../src/librawspeed/common/RawImageDataU16.cpp | 512 +++++++++++++
.../src/librawspeed/common/RawspeedException.h | 91 +++
.../rawspeed/src/librawspeed/common/SimpleLUT.h | 67 ++
.../rawspeed/src/librawspeed/common/Spline.h | 178 +++++
.../src/librawspeed/common/TableLookUp.cpp | 81 ++
.../rawspeed/src/librawspeed/common/TableLookUp.h | 40 +
.../rawspeed/src/librawspeed/common/meson.build | 38 +
.../librawspeed/decoders/AbstractTiffDecoder.cpp | 51 ++
.../src/librawspeed/decoders/AbstractTiffDecoder.h | 70 ++
.../src/librawspeed/decoders/ArwDecoder.cpp | 465 ++++++++++++
.../rawspeed/src/librawspeed/decoders/ArwDecoder.h | 61 ++
.../src/librawspeed/decoders/Cr2Decoder.cpp | 323 ++++++++
.../rawspeed/src/librawspeed/decoders/Cr2Decoder.h | 54 ++
.../src/librawspeed/decoders/CrwDecoder.cpp | 222 ++++++
.../rawspeed/src/librawspeed/decoders/CrwDecoder.h | 50 ++
.../src/librawspeed/decoders/DcrDecoder.cpp | 117 +++
.../rawspeed/src/librawspeed/decoders/DcrDecoder.h | 51 ++
.../src/librawspeed/decoders/DcsDecoder.cpp | 77 ++
.../rawspeed/src/librawspeed/decoders/DcsDecoder.h | 51 ++
.../src/librawspeed/decoders/DngDecoder.cpp | 784 +++++++++++++++++++
.../rawspeed/src/librawspeed/decoders/DngDecoder.h | 65 ++
.../src/librawspeed/decoders/ErfDecoder.cpp | 77 ++
.../rawspeed/src/librawspeed/decoders/ErfDecoder.h | 51 ++
.../src/librawspeed/decoders/IiqDecoder.cpp | 443 +++++++++++
.../rawspeed/src/librawspeed/decoders/IiqDecoder.h | 75 ++
.../src/librawspeed/decoders/KdcDecoder.cpp | 163 ++++
.../rawspeed/src/librawspeed/decoders/KdcDecoder.h | 53 ++
.../src/librawspeed/decoders/MefDecoder.cpp | 59 ++
.../rawspeed/src/librawspeed/decoders/MefDecoder.h | 50 ++
.../src/librawspeed/decoders/MosDecoder.cpp | 181 +++++
.../rawspeed/src/librawspeed/decoders/MosDecoder.h | 51 ++
.../src/librawspeed/decoders/MrwDecoder.cpp | 200 +++++
.../rawspeed/src/librawspeed/decoders/MrwDecoder.h | 57 ++
.../src/librawspeed/decoders/NakedDecoder.cpp | 109 +++
.../src/librawspeed/decoders/NakedDecoder.h | 59 ++
.../src/librawspeed/decoders/NefDecoder.cpp | 783 +++++++++++++++++++
.../rawspeed/src/librawspeed/decoders/NefDecoder.h | 75 ++
.../src/librawspeed/decoders/OrfDecoder.cpp | 282 +++++++
.../rawspeed/src/librawspeed/decoders/OrfDecoder.h | 56 ++
.../src/librawspeed/decoders/PefDecoder.cpp | 128 ++++
.../rawspeed/src/librawspeed/decoders/PefDecoder.h | 48 ++
.../src/librawspeed/decoders/RafDecoder.cpp | 352 +++++++++
.../rawspeed/src/librawspeed/decoders/RafDecoder.h | 56 ++
.../src/librawspeed/decoders/RawDecoder.cpp | 304 ++++++++
.../rawspeed/src/librawspeed/decoders/RawDecoder.h | 166 ++++
.../src/librawspeed/decoders/RawDecoderException.h | 39 +
.../src/librawspeed/decoders/Rw2Decoder.cpp | 261 +++++++
.../rawspeed/src/librawspeed/decoders/Rw2Decoder.h | 54 ++
.../src/librawspeed/decoders/SimpleTiffDecoder.cpp | 56 ++
.../src/librawspeed/decoders/SimpleTiffDecoder.h | 51 ++
.../src/librawspeed/decoders/SrwDecoder.cpp | 180 +++++
.../rawspeed/src/librawspeed/decoders/SrwDecoder.h | 51 ++
.../src/librawspeed/decoders/ThreefrDecoder.cpp | 91 +++
.../src/librawspeed/decoders/ThreefrDecoder.h | 49 ++
.../rawspeed/src/librawspeed/decoders/meson.build | 55 ++
.../decompressors/AbstractDecompressor.h | 27 +
.../decompressors/AbstractDngDecompressor.cpp | 212 ++++++
.../decompressors/AbstractDngDecompressor.h | 145 ++++
.../decompressors/AbstractHuffmanTable.h | 247 ++++++
.../decompressors/AbstractLJpegDecompressor.cpp | 267 +++++++
.../decompressors/AbstractLJpegDecompressor.h | 199 +++++
.../decompressors/AbstractSamsungDecompressor.h | 36 +
.../librawspeed/decompressors/BinaryHuffmanTree.h | 240 ++++++
.../librawspeed/decompressors/Cr2Decompressor.cpp | 251 ++++++
.../librawspeed/decompressors/Cr2Decompressor.h | 84 +++
.../librawspeed/decompressors/CrwDecompressor.cpp | 335 +++++++++
.../librawspeed/decompressors/CrwDecompressor.h | 60 ++
.../decompressors/DeflateDecompressor.cpp | 257 +++++++
.../decompressors/DeflateDecompressor.h | 59 ++
.../librawspeed/decompressors/FujiDecompressor.cpp | 823 ++++++++++++++++++++
.../librawspeed/decompressors/FujiDecompressor.h | 219 ++++++
.../decompressors/HasselbladDecompressor.cpp | 108 +++
.../decompressors/HasselbladDecompressor.h | 46 ++
.../src/librawspeed/decompressors/HuffmanTable.h | 39 +
.../librawspeed/decompressors/HuffmanTableLUT.h | 257 +++++++
.../librawspeed/decompressors/HuffmanTableLookup.h | 171 +++++
.../librawspeed/decompressors/HuffmanTableTree.h | 165 ++++
.../librawspeed/decompressors/HuffmanTableVector.h | 155 ++++
.../librawspeed/decompressors/JpegDecompressor.cpp | 171 +++++
.../librawspeed/decompressors/JpegDecompressor.h | 57 ++
.../decompressors/KodakDecompressor.cpp | 143 ++++
.../librawspeed/decompressors/KodakDecompressor.h | 51 ++
.../decompressors/LJpegDecompressor.cpp | 232 ++++++
.../librawspeed/decompressors/LJpegDecompressor.h | 53 ++
.../decompressors/NikonDecompressor.cpp | 544 +++++++++++++
.../librawspeed/decompressors/NikonDecompressor.h | 64 ++
.../decompressors/OlympusDecompressor.cpp | 174 +++++
.../decompressors/OlympusDecompressor.h | 59 ++
.../decompressors/PanasonicDecompressor.cpp | 268 +++++++
.../decompressors/PanasonicDecompressor.h | 91 +++
.../decompressors/PanasonicDecompressorV5.cpp | 252 +++++++
.../decompressors/PanasonicDecompressorV5.h | 107 +++
.../decompressors/PentaxDecompressor.cpp | 174 +++++
.../librawspeed/decompressors/PentaxDecompressor.h | 50 ++
.../decompressors/PhaseOneDecompressor.cpp | 175 +++++
.../decompressors/PhaseOneDecompressor.h | 60 ++
.../decompressors/SamsungV0Decompressor.cpp | 220 ++++++
.../decompressors/SamsungV0Decompressor.h | 50 ++
.../decompressors/SamsungV1Decompressor.cpp | 137 ++++
.../decompressors/SamsungV1Decompressor.h | 48 ++
.../decompressors/SamsungV2Decompressor.cpp | 344 +++++++++
.../decompressors/SamsungV2Decompressor.h | 55 ++
.../decompressors/SonyArw1Decompressor.cpp | 88 +++
.../decompressors/SonyArw1Decompressor.h | 38 +
.../decompressors/SonyArw2Decompressor.cpp | 144 ++++
.../decompressors/SonyArw2Decompressor.h | 43 ++
.../decompressors/UncompressedDecompressor.cpp | 388 ++++++++++
.../decompressors/UncompressedDecompressor.h | 135 ++++
.../librawspeed/decompressors/VC5Decompressor.cpp | 837 +++++++++++++++++++++
.../librawspeed/decompressors/VC5Decompressor.h | 241 ++++++
.../src/librawspeed/decompressors/meson.build | 66 ++
.../interpolators/Cr2sRawInterpolator.cpp | 522 +++++++++++++
.../interpolators/Cr2sRawInterpolator.h | 57 ++
.../src/librawspeed/interpolators/meson.build | 10 +
.../rawspeed/src/librawspeed/io/BitPumpJPEG.h | 92 +++
.../rawspeed/src/librawspeed/io/BitPumpLSB.h | 53 ++
.../rawspeed/src/librawspeed/io/BitPumpMSB.h | 50 ++
.../rawspeed/src/librawspeed/io/BitPumpMSB16.h | 48 ++
.../rawspeed/src/librawspeed/io/BitPumpMSB32.h | 50 ++
.../rawspeed/src/librawspeed/io/BitStream.h | 234 ++++++
subprojects/rawspeed/src/librawspeed/io/Buffer.cpp | 129 ++++
subprojects/rawspeed/src/librawspeed/io/Buffer.h | 212 ++++++
.../rawspeed/src/librawspeed/io/ByteStream.h | 239 ++++++
.../rawspeed/src/librawspeed/io/Endianness.h | 136 ++++
subprojects/rawspeed/src/librawspeed/io/FileIO.h | 59 ++
.../rawspeed/src/librawspeed/io/FileIOException.h | 39 +
.../rawspeed/src/librawspeed/io/FileReader.cpp | 120 +++
.../rawspeed/src/librawspeed/io/FileReader.h | 39 +
.../rawspeed/src/librawspeed/io/FileWriter.cpp | 81 ++
.../rawspeed/src/librawspeed/io/FileWriter.h | 42 ++
.../rawspeed/src/librawspeed/io/IOException.h | 37 +
.../rawspeed/src/librawspeed/io/meson.build | 25 +
subprojects/rawspeed/src/librawspeed/meson.build | 40 +
.../rawspeed/src/librawspeed/metadata/BlackArea.h | 36 +
.../rawspeed/src/librawspeed/metadata/Camera.cpp | 335 +++++++++
.../rawspeed/src/librawspeed/metadata/Camera.h | 121 +++
.../src/librawspeed/metadata/CameraMetaData.cpp | 161 ++++
.../src/librawspeed/metadata/CameraMetaData.h | 76 ++
.../librawspeed/metadata/CameraMetadataException.h | 39 +
.../src/librawspeed/metadata/CameraSensorInfo.cpp | 43 ++
.../src/librawspeed/metadata/CameraSensorInfo.h | 40 +
.../src/librawspeed/metadata/ColorFilterArray.cpp | 225 ++++++
.../src/librawspeed/metadata/ColorFilterArray.h | 78 ++
.../rawspeed/src/librawspeed/metadata/meson.build | 18 +
.../src/librawspeed/parsers/CiffParser.cpp | 81 ++
.../rawspeed/src/librawspeed/parsers/CiffParser.h | 48 ++
.../src/librawspeed/parsers/CiffParserException.h | 41 +
.../src/librawspeed/parsers/FiffParser.cpp | 139 ++++
.../rawspeed/src/librawspeed/parsers/FiffParser.h | 46 ++
.../src/librawspeed/parsers/FiffParserException.h | 39 +
.../rawspeed/src/librawspeed/parsers/RawParser.cpp | 94 +++
.../rawspeed/src/librawspeed/parsers/RawParser.h | 45 ++
.../src/librawspeed/parsers/RawParserException.h | 38 +
.../src/librawspeed/parsers/TiffParser.cpp | 146 ++++
.../rawspeed/src/librawspeed/parsers/TiffParser.h | 60 ++
.../src/librawspeed/parsers/TiffParserException.h | 40 +
.../rawspeed/src/librawspeed/parsers/meson.build | 20 +
.../rawspeed/src/librawspeed/tiff/CiffEntry.cpp | 172 +++++
.../rawspeed/src/librawspeed/tiff/CiffEntry.h | 82 ++
.../rawspeed/src/librawspeed/tiff/CiffIFD.cpp | 286 +++++++
.../rawspeed/src/librawspeed/tiff/CiffIFD.h | 110 +++
.../rawspeed/src/librawspeed/tiff/CiffTag.h | 52 ++
.../rawspeed/src/librawspeed/tiff/TiffEntry.cpp | 238 ++++++
.../rawspeed/src/librawspeed/tiff/TiffEntry.h | 118 +++
.../rawspeed/src/librawspeed/tiff/TiffIFD.cpp | 362 +++++++++
.../rawspeed/src/librawspeed/tiff/TiffIFD.h | 153 ++++
.../rawspeed/src/librawspeed/tiff/TiffTag.h | 359 +++++++++
.../rawspeed/src/librawspeed/tiff/meson.build | 18 +
subprojects/rawspeed/src/meson.build | 2 +
200 files changed, 28252 insertions(+)
---
diff --git a/meson.build b/meson.build
index 6e2c1127..977f7ec1 100644
--- a/meson.build
+++ b/meson.build
@@ -149,6 +149,9 @@ libgd = subproject(
)
libgd_dep = libgd.get_variable('libgd_dep')
+rawspeed = subproject('rawspeed', default_options: ['pkglibdir=@0@'.format(photos_libdir)])
+rawspeed_dep = rawspeed.get_variable('rawspeed_dep')
+
babl_dep = dependency('babl', version: '>= 0.1.54')
cairo_dep = dependency('cairo', version: '>= 1.14.0')
gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0', version: '>= 2.36.8')
diff --git a/src/meson.build b/src/meson.build
index f681fc33..617671d8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -96,6 +96,7 @@ deps = [
libjpeg_dep,
libpng_dep,
m_dep,
+ rawspeed_dep,
]
libgnome_photos = shared_library(
diff --git a/subprojects/rawspeed/meson.build b/subprojects/rawspeed/meson.build
new file mode 100644
index 00000000..30d2b74d
--- /dev/null
+++ b/subprojects/rawspeed/meson.build
@@ -0,0 +1,53 @@
+project(
+ 'rawspeed',
+ 'cpp',
+ license: 'LGPLv2+',
+ meson_version: '>= 0.50.0',
+)
+
+cxx = meson.get_compiler('cpp')
+rawspeed_libdir = get_option('pkglibdir')
+
+config_h = configuration_data()
+
+cpp_thread_local_support_source = '''
+ static thread_local int tls;
+
+ int
+ main (void)
+ {
+ return 0;
+ }
+'''
+if cxx.compiles(cpp_thread_local_support_source, name: 'C++ thread-local storage support')
+ config_h.set('HAVE_CXX_THREAD_LOCAL', true)
+endif
+
+gcc_thread_local_support_source = '''
+ static __thread int tls;
+
+ int
+ main (void)
+ {
+ return 0;
+ }
+'''
+if cxx.compiles(gcc_thread_local_support_source, name: 'GCC thread-local storage support')
+ config_h.set('HAVE_GCC_THREAD_LOCAL', true)
+endif
+
+openmp_dep = dependency('openmp', version: '>= 4.0')
+config_h.set('HAVE_OPENMP', true)
+
+zlib_dep = dependency('zlib')
+config_h.set('HAVE_ZLIB', true)
+
+configure_file(
+ output: 'rawspeedconfig.h',
+ configuration: config_h,
+)
+
+rawspeed_include = include_directories('.')
+
+
+subdir('src')
diff --git a/subprojects/rawspeed/meson_options.txt b/subprojects/rawspeed/meson_options.txt
new file mode 100644
index 00000000..58b53fda
--- /dev/null
+++ b/subprojects/rawspeed/meson_options.txt
@@ -0,0 +1,6 @@
+option(
+ 'pkglibdir',
+ description: 'The private directory the shared library/typelib will be installed into.',
+ type: 'string',
+ value: '',
+)
diff --git a/subprojects/rawspeed/src/external/AddressSanitizer.h
b/subprojects/rawspeed/src/external/AddressSanitizer.h
new file mode 100644
index 00000000..8daf619f
--- /dev/null
+++ b/subprojects/rawspeed/src/external/AddressSanitizer.h
@@ -0,0 +1,87 @@
+/*
+ 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 <cstddef> // for size_t
+
+// see http://clang.llvm.org/docs/LanguageExtensions.html
+#ifndef __has_feature // Optional of course.
+#define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+#ifndef __has_extension
+#define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
+#endif
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+#include <sanitizer/asan_interface.h>
+#endif
+
+namespace rawspeed {
+
+struct ASan final {
+ // Do not instantiate.
+ ASan() = delete;
+ ASan(const ASan&) = delete;
+ ASan(ASan&&) = delete;
+ ASan& operator=(const ASan&) = delete;
+ ASan& operator=(ASan&&) = delete;
+ ~ASan() = delete;
+
+ // Marks memory region [addr, addr+size) as unaddressable.
+ static void PoisonMemoryRegion(void const volatile* addr, size_t size);
+ // Marks memory region [addr, addr+size) as addressable.
+ static void UnPoisonMemoryRegion(void const volatile* addr, size_t size);
+
+ // If at least one byte in [beg, beg+size) is poisoned, return true
+ // Otherwise return 0.
+ static bool RegionIsPoisoned(void const volatile* addr, size_t size);
+};
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+inline void ASan::PoisonMemoryRegion(void const volatile* addr, size_t size) {
+ __asan_poison_memory_region(addr, size);
+}
+inline void ASan::UnPoisonMemoryRegion(void const volatile* addr, size_t size) {
+ __asan_unpoison_memory_region(addr, size);
+}
+inline bool ASan::RegionIsPoisoned(void const volatile* addr, size_t size) {
+ auto* beg = const_cast<void*>(addr); // NOLINT
+ return nullptr != __asan_region_is_poisoned(beg, size);
+}
+#else
+inline void ASan::PoisonMemoryRegion(void const volatile* addr, size_t size) {
+ // If we are building without ASan, then there is no way to have a non-empty
+ // body of this function. It's better than to have a macros, or to use
+ // preprocessor in every place it is called.
+}
+inline void ASan::UnPoisonMemoryRegion(void const volatile* addr, size_t size) {
+ // If we are building without ASan, then there is no way to have a non-empty
+ // body of this function. It's better than to have a macros, or to use
+ // preprocessor in every place it is called.
+}
+inline bool ASan::RegionIsPoisoned(void const volatile* addr, size_t size) {
+ // If we are building without ASan, then there is no way to have a poisoned
+ // memory region.
+ return false;
+}
+#endif
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/external/ThreadSafetyAnalysis.h
b/subprojects/rawspeed/src/external/ThreadSafetyAnalysis.h
new file mode 100644
index 00000000..c990fddc
--- /dev/null
+++ b/subprojects/rawspeed/src/external/ThreadSafetyAnalysis.h
@@ -0,0 +1,130 @@
+#pragma once
+
+// see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader
+
+#ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H
+#define THREAD_SAFETY_ANALYSIS_MUTEX_H
+
+#pragma GCC system_header
+
+// Enable thread safety attributes only with clang.
+// The attributes can be safely erased when compiling with other compilers.
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+#ifdef USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
+// The original version of thread safety analysis the following attribute
+// definitions. These use a lock-based terminology. They are still in use
+// by existing thread safety code, and will continue to be supported.
+
+// Deprecated.
+#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var)
+
+// Deprecated.
+#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded_var)
+
+// Replaced by REQUIRES
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+// Replaced by REQUIRES_SHARED
+#define SHARED_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
+// Replaced by CAPABILITY
+#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
+
+// Replaced by SCOPED_CAPABILITY
+#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+// Replaced by ACQUIRE
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+// Replaced by ACQUIRE_SHARED
+#define SHARED_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+// Replaced by RELEASE and RELEASE_SHARED
+#define UNLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+// Replaced by TRY_ACQUIRE
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+// Replaced by TRY_ACQUIRE_SHARED
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+// Replaced by ASSERT_CAPABILITY
+#define ASSERT_EXCLUSIVE_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
+
+// Replaced by ASSERT_SHARED_CAPABILITY
+#define ASSERT_SHARED_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
+
+// Replaced by EXCLUDE_CAPABILITY.
+#define LOCKS_EXCLUDED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+// Replaced by RETURN_CAPABILITY
+#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#endif // USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
+
+#endif // THREAD_SAFETY_ANALYSIS_MUTEX_H
diff --git a/subprojects/rawspeed/src/external/meson.build b/subprojects/rawspeed/src/external/meson.build
new file mode 100644
index 00000000..e539e92a
--- /dev/null
+++ b/subprojects/rawspeed/src/external/meson.build
@@ -0,0 +1 @@
+rawspeed_external_include = include_directories('.')
diff --git a/subprojects/rawspeed/src/librawspeed/RawSpeed-API.h
b/subprojects/rawspeed/src/librawspeed/RawSpeed-API.h
new file mode 100644
index 00000000..0ac4418e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/RawSpeed-API.h
@@ -0,0 +1,43 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2011 Klaus Post
+ Copyright (C) 2017 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
+
+// IWYU pragma: begin_exports
+
+#include "rawspeedconfig.h"
+
+#include "common/Common.h"
+#include "common/Mutex.h"
+#include "common/Point.h"
+#include "common/RawImage.h"
+#include "common/RawspeedException.h"
+#include "decoders/RawDecoder.h"
+#include "io/Buffer.h"
+#include "io/Endianness.h"
+#include "io/FileReader.h"
+#include "metadata/BlackArea.h"
+#include "metadata/Camera.h"
+#include "metadata/CameraMetaData.h"
+#include "metadata/ColorFilterArray.h"
+#include "parsers/RawParser.h"
+
+// IWYU pragma: end_exports
diff --git a/subprojects/rawspeed/src/librawspeed/common/Array2DRef.h
b/subprojects/rawspeed/src/librawspeed/common/Array2DRef.h
new file mode 100644
index 00000000..ec3a6e16
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Array2DRef.h
@@ -0,0 +1,87 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Stefan Löffler
+ 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 <cassert> // for assert
+#include <type_traits>
+#include <vector> // for vector
+
+namespace rawspeed {
+
+template <class T> class Array2DRef {
+ int _pitch = 0;
+ T* _data = nullptr;
+
+ friend Array2DRef<const T>; // We need to be able to convert to const version.
+
+public:
+ using value_type = T;
+ using cvless_value_type = typename std::remove_cv<value_type>::type;
+
+ int width = 0, height = 0;
+
+ Array2DRef() = default;
+
+ Array2DRef(T* data, int dataWidth, int dataHeight, int dataPitch = 0);
+
+ // Conversion from Array2DRef<T> to Array2DRef<const T>.
+ template <class T2, typename = std::enable_if_t<std::is_same<
+ typename std::remove_const<T>::type, T2>::value>>
+ Array2DRef(Array2DRef<T2> RHS) { // NOLINT google-explicit-constructor
+ _data = RHS._data;
+ _pitch = RHS._pitch;
+ width = RHS.width;
+ height = RHS.height;
+ }
+
+ template <typename AllocatorType =
+ typename std::vector<cvless_value_type>::allocator_type>
+ static Array2DRef<T>
+ create(std::vector<cvless_value_type, AllocatorType>* storage, int width,
+ int height) {
+ storage->resize(width * height);
+ return {storage->data(), width, height};
+ }
+
+ inline T& operator()(int x, int y) const;
+};
+
+template <class T>
+Array2DRef<T>::Array2DRef(T* data, const int dataWidth, const int dataHeight,
+ const int dataPitch /* = 0 */)
+ : _data(data), width(dataWidth), height(dataHeight) {
+ assert(width >= 0);
+ assert(height >= 0);
+ _pitch = (dataPitch == 0 ? dataWidth : dataPitch);
+}
+
+template <class T>
+T& Array2DRef<T>::operator()(const int x, const int y) const {
+ assert(_data);
+ assert(x >= 0);
+ assert(y >= 0);
+ assert(x < width);
+ assert(y < height);
+ return _data[y * _pitch + x];
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.cpp
b/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.cpp
new file mode 100644
index 00000000..144e8c5f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.cpp
@@ -0,0 +1,94 @@
+/*
+ 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
+*/
+
+#include "common/ChecksumFile.h"
+#include "common/Common.h" // for splitString
+#include "common/RawspeedException.h" // for ThrowRSE
+#include "io/Buffer.h" // for Buffer
+#include "io/FileReader.h" // for FileReader
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+#include <string> // for string, allocator, operator+
+#include <vector> // for vector
+
+namespace rawspeed {
+
+namespace {
+
+// The lenght of the sha1 digest (160-bit, 40 hexadecimal chars).
+constexpr auto Sha1CheckSumLength = 40;
+// The separator after the digest and before filename.
+// Should be either " " or " b".
+constexpr auto CheckSumSeparatorWidth = 2;
+
+ChecksumFileEntry ParseChecksumFileLine(const std::string& Line,
+ const std::string& RootDir) {
+ ChecksumFileEntry Entry;
+
+ // We are just assuming that the checksum file is correct and valid.
+ // It is up to user to validate it first (via actually running `sha1sum -c`).
+
+ static constexpr auto Offset = Sha1CheckSumLength + CheckSumSeparatorWidth;
+
+ if (Line.size() <= Offset)
+ ThrowRSE("Malformed checksum line: \"%s\"", Line.c_str());
+
+ Entry.RelFileName = Line.substr(Offset);
+ assert(!Entry.RelFileName.empty());
+ assert(Entry.RelFileName.back() != '\n');
+
+ Entry.FullFileName = RootDir + "/" + Entry.RelFileName;
+
+ return Entry;
+}
+
+} // namespace
+
+std::vector<ChecksumFileEntry>
+ParseChecksumFileContent(const std::string& ChecksumFileContent,
+ const std::string& RootDir) {
+ std::vector<ChecksumFileEntry> Listing;
+
+ const std::vector<std::string> Lines = splitString(ChecksumFileContent, '\n');
+
+ Listing.reserve(Lines.size());
+
+ for (const auto& Line : Lines) {
+ assert(!Line.empty());
+ Listing.emplace_back(ParseChecksumFileLine(Line, RootDir));
+ }
+
+ return Listing;
+}
+
+std::vector<ChecksumFileEntry>
+ReadChecksumFile(const std::string& RootDir,
+ const std::string& ChecksumFileBasename) {
+ const std::string ChecksumFileName = RootDir + "/" + ChecksumFileBasename;
+ FileReader FR(ChecksumFileName.c_str());
+
+ std::unique_ptr<const Buffer> buf = FR.readFile();
+ const std::string ChecksumFileContent(
+ reinterpret_cast<const char*>(buf->begin()), buf->getSize());
+
+ return ParseChecksumFileContent(ChecksumFileContent, RootDir);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.h
b/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.h
new file mode 100644
index 00000000..79bed10a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/ChecksumFile.h
@@ -0,0 +1,41 @@
+/*
+ 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 <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+struct ChecksumFileEntry {
+ std::string FullFileName;
+ std::string RelFileName;
+};
+
+std::vector<ChecksumFileEntry>
+ParseChecksumFileContent(const std::string& ChecksumFileContent,
+ const std::string& RootDir);
+
+std::vector<ChecksumFileEntry>
+ReadChecksumFile(const std::string& RootDir,
+ const std::string& ChecksumFileBasename = "filelist.sha1");
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Common.cpp
b/subprojects/rawspeed/src/librawspeed/common/Common.cpp
new file mode 100644
index 00000000..8c8c5663
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Common.cpp
@@ -0,0 +1,62 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "common/Common.h"
+#include <cstdarg> // for va_end, va_list, va_start
+#include <cstdio> // for printf, vprintf
+
+// #define _DEBUG
+
+namespace rawspeed {
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(_DEBUG)
+
+void __attribute__((const))
+writeLog(DEBUG_PRIO priority, const char* format, ...) {
+ // When fuzzing, any output is really undesirable.
+}
+
+#else
+
+void writeLog(DEBUG_PRIO priority, const char* format, ...) {
+#ifndef _DEBUG
+ if (priority < DEBUG_PRIO_INFO)
+#endif // _DEBUG
+ fprintf(stdout, "%s", "RawSpeed:");
+
+ va_list args;
+ va_start(args, format);
+
+#ifndef _DEBUG
+ if (priority < DEBUG_PRIO_INFO)
+#endif // _DEBUG
+ vfprintf(stdout, format, args);
+
+ va_end(args);
+
+#ifndef _DEBUG
+ if (priority < DEBUG_PRIO_INFO)
+#endif // _DEBUG
+ fprintf(stdout, "%s", "\n");
+}
+
+#endif
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Common.h
b/subprojects/rawspeed/src/librawspeed/common/Common.h
new file mode 100644
index 00000000..c9a70fa6
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Common.h
@@ -0,0 +1,238 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h"
+
+#include <algorithm> // IWYU pragma: keep
+#include <cassert> // for assert
+#include <climits> // for CHAR_BIT
+#include <cstdint> // for uintptr_t
+#include <cstring> // for size_t, memcpy
+#include <initializer_list> // for initializer_list
+#include <string> // for string
+#include <type_traits> // for enable_if, is_pointer, is_signed, is_uns...
+#include <vector> // for vector
+
+extern "C" int rawspeed_get_number_of_processor_cores();
+
+namespace rawspeed {
+
+using char8 = signed char;
+using uchar8 = unsigned char;
+using uint32 = unsigned int;
+using int64 = long long;
+using uint64 = unsigned long long;
+using int32 = signed int;
+using ushort16 = unsigned short;
+using short16 = signed short;
+
+enum DEBUG_PRIO {
+ DEBUG_PRIO_ERROR = 0x10,
+ DEBUG_PRIO_WARNING = 0x100,
+ DEBUG_PRIO_INFO = 0x1000,
+ DEBUG_PRIO_EXTRA = 0x10000
+};
+
+void writeLog(DEBUG_PRIO priority, const char* format, ...)
+ __attribute__((format(printf, 2, 3)));
+
+inline void copyPixels(uchar8* dest, int dstPitch, const uchar8* src,
+ int srcPitch, int rowSize, int height)
+{
+ if (height == 1 || (dstPitch == srcPitch && srcPitch == rowSize))
+ memcpy(dest, src, static_cast<size_t>(rowSize) * height);
+ else {
+ for (int y = height; y > 0; --y) {
+ memcpy(dest, src, rowSize);
+ dest += dstPitch;
+ src += srcPitch;
+ }
+ }
+}
+
+// only works for positive values and zero
+template <typename T> inline constexpr bool isPowerOfTwo(T val) {
+ return (val & (~val+1)) == val;
+}
+
+constexpr inline size_t __attribute__((const))
+roundToMultiple(size_t value, size_t multiple, bool roundDown) {
+ if ((multiple == 0) || (value % multiple == 0))
+ return value;
+ // Drop remainder.
+ size_t roundedDown = value - (value % multiple);
+ if (roundDown) // If we were rounding down, then that's it.
+ return roundedDown;
+ // Else, just add one multiple.
+ return roundedDown + multiple;
+}
+
+constexpr inline size_t __attribute__((const))
+roundDown(size_t value, size_t multiple) {
+ return roundToMultiple(value, multiple, /*roundDown=*/true);
+}
+
+constexpr inline size_t __attribute__((const))
+roundUp(size_t value, size_t multiple) {
+ return roundToMultiple(value, multiple, /*roundDown=*/false);
+}
+
+constexpr inline size_t __attribute__((const))
+roundUpDivision(size_t value, size_t div) {
+ return (value != 0) ? (1 + ((value - 1) / div)) : 0;
+}
+
+template <class T>
+inline constexpr __attribute__((const)) bool
+isAligned(T value, size_t multiple,
+ typename std::enable_if<std::is_pointer<T>::value>::type* /*unused*/ =
+ nullptr) {
+ return (multiple == 0) ||
+ (reinterpret_cast<std::uintptr_t>(value) % multiple == 0);
+}
+
+template <class T>
+inline constexpr __attribute__((const)) bool isAligned(
+ T value, size_t multiple,
+ typename std::enable_if<!std::is_pointer<T>::value>::type* /*unused*/ =
+ nullptr) {
+ return (multiple == 0) ||
+ (static_cast<std::uintptr_t>(value) % multiple == 0);
+}
+
+template <typename T, typename T2>
+bool __attribute__((pure))
+isIn(const T value, const std::initializer_list<T2>& list) {
+ for (auto t : list)
+ if (t == value)
+ return true;
+ return false;
+}
+
+// Clamps the given unsigned value to the range 0 .. 2^n-1, with n <= 16
+template <class T>
+inline constexpr __attribute__((const)) ushort16 clampBits(
+ T value, unsigned int nBits,
+ typename std::enable_if<std::is_unsigned<T>::value>::type* /*unused*/ =
+ nullptr) {
+ // We expect to produce ushort16.
+ assert(nBits <= 16);
+ // Check that the clamp is not a no-op. Not of ushort16 to 16 bits e.g.
+ // (Well, not really, if we are called from clampBits<signed>, it's ok..).
+ constexpr auto BitWidthOfT = CHAR_BIT * sizeof(T);
+ (void)BitWidthOfT;
+ assert(BitWidthOfT > nBits); // If nBits >= BitWidthOfT, then shift is UB.
+ const T maxVal = (T(1) << nBits) - T(1);
+ return std::min(value, maxVal);
+}
+
+// Clamps the given signed value to the range 0 .. 2^n-1, with n <= 16
+template <typename T>
+inline constexpr ushort16 __attribute__((const))
+clampBits(T value, unsigned int nBits,
+ typename std::enable_if<std::is_signed<T>::value>::type* /*unused*/ =
+ nullptr) {
+ // If the value is negative, clamp it to zero.
+ value = std::max(value, T(0));
+ // Now, let the unsigned case clamp to the upper limit.
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ return clampBits<UnsignedT>(value, nBits);
+}
+
+// Trim both leading and trailing spaces from the string
+inline std::string trimSpaces(const std::string& str)
+{
+ // Find the first character position after excluding leading blank spaces
+ size_t startpos = str.find_first_not_of(" \t");
+
+ // Find the first character position from reverse af
+ size_t endpos = str.find_last_not_of(" \t");
+
+ // if all spaces or empty return an empty string
+ if ((startpos == std::string::npos) || (endpos == std::string::npos))
+ return "";
+
+ return str.substr(startpos, endpos - startpos + 1);
+}
+
+inline std::vector<std::string> splitString(const std::string& input,
+ char c = ' ')
+{
+ std::vector<std::string> result;
+ const char* str = input.c_str();
+
+ while (true) {
+ const char* begin = str;
+
+ while (*str != c && *str != '\0')
+ str++;
+
+ if (begin != str)
+ result.emplace_back(begin, str);
+
+ const bool isNullTerminator = (*str == '\0');
+ str++;
+
+ if (isNullTerminator)
+ break;
+ }
+
+ return result;
+}
+
+enum BitOrder {
+ BitOrder_LSB, /* Memory order */
+ BitOrder_MSB, /* Input is added to stack byte by byte, and output is lifted
+ from top */
+ BitOrder_MSB16, /* Same as above, but 16 bits at the time */
+ BitOrder_MSB32, /* Same as above, but 32 bits at the time */
+};
+
+// little 'forced' loop unrolling helper tool, example:
+// unroll_loop<N>([&](int i) {
+// func(i);
+// });
+// will translate to:
+// func(0); func(1); func(2); ... func(N-1);
+
+template <typename Lambda, size_t N>
+struct unroll_loop_t {
+ inline static void repeat(const Lambda& f) {
+ unroll_loop_t<Lambda, N-1>::repeat(f);
+ f(N-1);
+ }
+};
+
+template <typename Lambda>
+struct unroll_loop_t<Lambda, 0> {
+ inline static void repeat(const Lambda& f) {
+ // this method is correctly empty.
+ // only needed as part of compile time 'manual' branch unrolling
+ }
+};
+
+template <size_t N, typename Lambda>
+inline void unroll_loop(const Lambda& f) {
+ unroll_loop_t<Lambda, N>::repeat(f);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Cpuid.cpp
b/subprojects/rawspeed/src/librawspeed/common/Cpuid.cpp
new file mode 100644
index 00000000..2f468af9
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Cpuid.cpp
@@ -0,0 +1,49 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+*/
+
+#include "common/Cpuid.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+#include <cpuid.h> // for __get_cpuid, bit_SSE2
+#endif
+
+namespace rawspeed {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+bool Cpuid::SSE2() {
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+
+ if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx))
+ return false;
+
+ return edx & bit_SSE2;
+}
+
+#else
+
+bool Cpuid::SSE2() { return false; }
+
+#endif
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Cpuid.h
b/subprojects/rawspeed/src/librawspeed/common/Cpuid.h
new file mode 100644
index 00000000..fdce5d4f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Cpuid.h
@@ -0,0 +1,30 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+
+namespace rawspeed {
+
+class Cpuid final {
+public:
+ static bool __attribute__((const)) SSE2();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/DefaultInitAllocatorAdaptor.h
b/subprojects/rawspeed/src/librawspeed/common/DefaultInitAllocatorAdaptor.h
new file mode 100644
index 00000000..910f9adf
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/DefaultInitAllocatorAdaptor.h
@@ -0,0 +1,101 @@
+/*
+ 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 <memory>
+#include <type_traits>
+
+namespace rawspeed {
+
+template <typename T, typename ActualAllocator = std::allocator<T>,
+ typename = std::enable_if_t<std::is_pod<T>::value>>
+class DefaultInitAllocatorAdaptor {
+public:
+ using allocator_traits = std::allocator_traits<ActualAllocator>;
+
+ using value_type = typename allocator_traits::value_type;
+ using pointer = typename allocator_traits::pointer;
+ using const_pointer = typename allocator_traits::const_pointer;
+ using size_type = typename allocator_traits::size_type;
+ using difference_type = typename allocator_traits::difference_type;
+
+ static_assert(std::is_same<T, value_type>::value, "");
+
+ template <class To> struct rebind {
+ using other = DefaultInitAllocatorAdaptor<
+ To, typename allocator_traits::template rebind_alloc<To>>;
+ };
+
+ using allocator_type =
+ typename allocator_traits::template rebind_alloc<value_type>;
+
+ allocator_type allocator;
+
+ const allocator_type& get_allocator() const noexcept { return allocator; }
+
+ DefaultInitAllocatorAdaptor() noexcept = default;
+
+ explicit DefaultInitAllocatorAdaptor(
+ const allocator_type& allocator_) noexcept
+ : allocator(allocator_) {}
+
+ template <class To>
+ explicit DefaultInitAllocatorAdaptor(
+ const DefaultInitAllocatorAdaptor<
+ To, typename allocator_traits::template rebind_alloc<To>>&
+ allocator_) noexcept
+ : allocator(allocator_.get_allocator()) {}
+
+ pointer allocate(size_type n, const void* hint = nullptr) {
+ return allocator.allocate(n, hint);
+ }
+
+ void deallocate(pointer p, size_type n) noexcept {
+ allocator.deallocate(p, n);
+ }
+
+ template <typename U>
+ void
+ construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value) {
+ ::new (static_cast<void*>(ptr)) U; // start the life-time, but do not init.
+ }
+
+ using propagate_on_container_copy_assignment =
+ typename allocator_traits::propagate_on_container_copy_assignment;
+ using propagate_on_container_move_assignment =
+ typename allocator_traits::propagate_on_container_move_assignment;
+ using propagate_on_container_swap =
+ typename allocator_traits::propagate_on_container_swap;
+};
+
+template <typename T0, typename A0, typename T1, typename A1>
+bool operator==(DefaultInitAllocatorAdaptor<T0, A0> const& x,
+ DefaultInitAllocatorAdaptor<T1, A1> const& y) noexcept {
+ return x.get_allocator() == y.get_allocator();
+}
+
+template <typename T0, typename A0, typename T1, typename A1>
+bool operator!=(DefaultInitAllocatorAdaptor<T0, A0> const& x,
+ DefaultInitAllocatorAdaptor<T1, A1> const& y) noexcept {
+ return !(x == y);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.cpp
b/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.cpp
new file mode 100644
index 00000000..747027f2
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.cpp
@@ -0,0 +1,592 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+ Copyright (C) 2017 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
+*/
+
+#include "common/DngOpcodes.h"
+#include "common/Common.h" // for uint32, ushort16, clampBits
+#include "common/Mutex.h" // for MutexLocker
+#include "common/Point.h" // for iRectangle2D, iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianness::big
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include <algorithm> // for generate_n, fill_n
+#include <cassert> // for assert
+#include <cmath> // for pow
+#include <iterator> // for back_insert_iterator
+#include <limits> // for numeric_limits
+#include <stdexcept> // for out_of_range
+#include <tuple> // for tie, tuple
+// IWYU pragma: no_include <ext/alloc_traits.h>
+// IWYU pragma: no_include <type_traits>
+
+using std::vector;
+using std::fill_n;
+using std::make_pair;
+
+namespace rawspeed {
+
+class DngOpcodes::DngOpcode {
+public:
+ virtual ~DngOpcode() = default;
+
+ // Will be called once before processing.
+ // Can be used for preparing pre-calculated values, etc.
+ virtual void setup(const RawImage& ri) {
+ // NOP by default. child class shall override this if needed.
+ }
+
+ // Will be called for actual processing.
+ virtual void apply(const RawImage& ri) = 0;
+};
+
+// ****************************************************************************
+
+class DngOpcodes::FixBadPixelsConstant final : public DngOpcodes::DngOpcode {
+ uint32 value;
+
+public:
+ explicit FixBadPixelsConstant(const RawImage& ri, ByteStream* bs) {
+ value = bs->getU32();
+ bs->getU32(); // Bayer Phase not used
+ }
+
+ void setup(const RawImage& ri) override {
+ // These limitations are present within the DNG SDK as well.
+ if (ri->getDataType() != TYPE_USHORT16)
+ ThrowRDE("Only 16 bit images supported");
+
+ if (ri->getCpp() > 1)
+ ThrowRDE("Only 1 component images supported");
+ }
+
+ void apply(const RawImage& ri) override {
+ MutexLocker guard(&ri->mBadPixelMutex);
+ iPoint2D crop = ri->getCropOffset();
+ uint32 offset = crop.x | (crop.y << 16);
+ for (auto y = 0; y < ri->dim.y; ++y) {
+ auto* src = reinterpret_cast<ushort16*>(ri->getData(0, y));
+ for (auto x = 0; x < ri->dim.x; ++x) {
+ if (src[x] == value)
+ ri->mBadPixelPositions.push_back(offset + (y << 16 | x));
+ }
+ }
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::ROIOpcode : public DngOpcodes::DngOpcode {
+ iRectangle2D roi;
+
+protected:
+ explicit ROIOpcode(const RawImage& ri, ByteStream* bs, bool minusOne) {
+ const iRectangle2D fullImage =
+ minusOne ? iRectangle2D(0, 0, ri->dim.x - 1, ri->dim.y - 1)
+ : iRectangle2D(0, 0, ri->dim.x, ri->dim.y);
+
+ uint32 top = bs->getU32();
+ uint32 left = bs->getU32();
+ uint32 bottom = bs->getU32();
+ uint32 right = bs->getU32();
+
+ const iPoint2D topLeft(left, top);
+ const iPoint2D bottomRight(right, bottom);
+
+ if (!(fullImage.isPointInsideInclusive(topLeft) &&
+ fullImage.isPointInsideInclusive(bottomRight) &&
+ bottomRight >= topLeft)) {
+ ThrowRDE("Rectangle (%u, %u, %u, %u) not inside image (%u, %u, %u, %u).",
+ topLeft.x, topLeft.y, bottomRight.x, bottomRight.y,
+ fullImage.getTopLeft().x, fullImage.getTopLeft().y,
+ fullImage.getBottomRight().x, fullImage.getBottomRight().y);
+ }
+
+ roi.setTopLeft(topLeft);
+ roi.setBottomRightAbsolute(bottomRight);
+ assert(roi.isThisInside(fullImage));
+ }
+
+ const iRectangle2D& __attribute__((pure)) getRoi() const { return roi; }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::DummyROIOpcode final : public ROIOpcode {
+public:
+ explicit DummyROIOpcode(const RawImage& ri, ByteStream* bs)
+ : ROIOpcode(ri, bs, true) {}
+
+ const iRectangle2D& __attribute__((pure)) getRoi() const {
+ return ROIOpcode::getRoi();
+ }
+
+ [[noreturn]] void apply(const RawImage& ri) final {
+ assert(false && "You should not be calling this.");
+ __builtin_unreachable();
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::FixBadPixelsList final : public DngOpcodes::DngOpcode {
+ std::vector<uint32> badPixels;
+
+public:
+ explicit FixBadPixelsList(const RawImage& ri, ByteStream* bs) {
+ const iRectangle2D fullImage(0, 0, ri->getUncroppedDim().x - 1,
+ ri->getUncroppedDim().y - 1);
+
+ bs->getU32(); // Skip phase - we don't care
+ auto badPointCount = bs->getU32();
+ auto badRectCount = bs->getU32();
+
+ // first, check that we indeed have much enough data
+ const auto origPos = bs->getPosition();
+ bs->skipBytes(badPointCount, 2 * 4);
+ bs->skipBytes(badRectCount, 4 * 4);
+ bs->setPosition(origPos);
+
+ // Read points
+ badPixels.reserve(badPixels.size() + badPointCount);
+ for (auto i = 0U; i < badPointCount; ++i) {
+ auto y = bs->getU32();
+ auto x = bs->getU32();
+
+ const iPoint2D badPoint(x, y);
+ if (!fullImage.isPointInsideInclusive(badPoint))
+ ThrowRDE("Bad point not inside image.");
+
+ badPixels.emplace_back(y << 16 | x);
+ }
+
+ // Read rects
+ for (auto i = 0U; i < badRectCount; ++i) {
+ const DummyROIOpcode dummy(ri, bs);
+
+ const iRectangle2D badRect = dummy.getRoi();
+ assert(badRect.isThisInside(fullImage));
+
+ auto area = (1 + badRect.getHeight()) * (1 + badRect.getWidth());
+ badPixels.reserve(badPixels.size() + area);
+ for (auto y = badRect.getTop(); y <= badRect.getBottom(); ++y) {
+ for (auto x = badRect.getLeft(); x <= badRect.getRight(); ++x) {
+ badPixels.emplace_back(y << 16 | x);
+ }
+ }
+ }
+ }
+
+ void apply(const RawImage& ri) override {
+ MutexLocker guard(&ri->mBadPixelMutex);
+ ri->mBadPixelPositions.insert(ri->mBadPixelPositions.begin(),
+ badPixels.begin(), badPixels.end());
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::TrimBounds final : public ROIOpcode {
+public:
+ explicit TrimBounds(const RawImage& ri, ByteStream* bs)
+ : ROIOpcode(ri, bs, false) {}
+
+ void apply(const RawImage& ri) override { ri->subFrame(getRoi()); }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::PixelOpcode : public ROIOpcode {
+ uint32 firstPlane;
+ uint32 planes;
+ uint32 rowPitch;
+ uint32 colPitch;
+
+protected:
+ explicit PixelOpcode(const RawImage& ri, ByteStream* bs)
+ : ROIOpcode(ri, bs, false) {
+ firstPlane = bs->getU32();
+ planes = bs->getU32();
+
+ if (planes == 0 || firstPlane > ri->getCpp() || planes > ri->getCpp() ||
+ firstPlane + planes > ri->getCpp()) {
+ ThrowRDE("Bad plane params (first %u, num %u), got planes = %u",
+ firstPlane, planes, ri->getCpp());
+ }
+
+ rowPitch = bs->getU32();
+ colPitch = bs->getU32();
+
+ const iRectangle2D& ROI = getRoi();
+
+ if (rowPitch < 1 || rowPitch > static_cast<uint32>(ROI.getHeight()) ||
+ colPitch < 1 || colPitch > static_cast<uint32>(ROI.getWidth()))
+ ThrowRDE("Invalid pitch");
+ }
+
+ // traverses the current ROI and applies the operation OP to each pixel,
+ // i.e. each pixel value v is replaced by op(x, y, v), where x/y are the
+ // coordinates of the pixel value v.
+ template <typename T, typename OP> void applyOP(const RawImage& ri, OP op) {
+ int cpp = ri->getCpp();
+ const iRectangle2D& ROI = getRoi();
+ for (auto y = ROI.getTop(); y < ROI.getBottom(); y += rowPitch) {
+ auto* src = reinterpret_cast<T*>(ri->getData(0, y));
+ // Add offset, so this is always first plane
+ src += firstPlane;
+ // FIXME: is op() really supposed to receive global image coordinates,
+ // and not [0..ROI.getHeight()-1][0..ROI.getWidth()-1] ?
+ for (auto x = ROI.getLeft(); x < ROI.getRight(); x += colPitch) {
+ for (auto p = 0U; p < planes; ++p)
+ src[x * cpp + p] = op(x, y, src[x * cpp + p]);
+ }
+ }
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::LookupOpcode : public PixelOpcode {
+protected:
+ vector<ushort16> lookup;
+
+ explicit LookupOpcode(const RawImage& ri, ByteStream* bs)
+ : PixelOpcode(ri, bs), lookup(65536) {}
+
+ void setup(const RawImage& ri) override {
+ PixelOpcode::setup(ri);
+ if (ri->getDataType() != TYPE_USHORT16)
+ ThrowRDE("Only 16 bit images supported");
+ }
+
+ void apply(const RawImage& ri) override {
+ applyOP<ushort16>(
+ ri, [this](uint32 x, uint32 y, ushort16 v) { return lookup[v]; });
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::TableMap final : public LookupOpcode {
+public:
+ explicit TableMap(const RawImage& ri, ByteStream* bs) : LookupOpcode(ri, bs) {
+ auto count = bs->getU32();
+
+ if (count == 0 || count > 65536)
+ ThrowRDE("Invalid size of lookup table");
+
+ for (auto i = 0U; i < count; ++i)
+ lookup[i] = bs->getU16();
+
+ if (count < lookup.size())
+ fill_n(&lookup[count], lookup.size() - count, lookup[count - 1]);
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::PolynomialMap final : public LookupOpcode {
+public:
+ explicit PolynomialMap(const RawImage& ri, ByteStream* bs)
+ : LookupOpcode(ri, bs) {
+ vector<double> polynomial;
+
+ const auto polynomial_size = bs->getU32() + 1UL;
+ bs->check(8UL * polynomial_size);
+ if (polynomial_size > 9)
+ ThrowRDE("A polynomial with more than 8 degrees not allowed");
+
+ polynomial.reserve(polynomial_size);
+ std::generate_n(std::back_inserter(polynomial), polynomial_size,
+ [&bs]() { return bs->get<double>(); });
+
+ // Create lookup
+ lookup.resize(65536);
+ for (auto i = 0UL; i < lookup.size(); ++i) {
+ double val = polynomial[0];
+ for (auto j = 1UL; j < polynomial.size(); ++j)
+ val += polynomial[j] * pow(i / 65536.0, j);
+ lookup[i] = (clampBits(static_cast<int>(val * 65535.5), 16));
+ }
+ }
+};
+
+// ****************************************************************************
+
+class DngOpcodes::DeltaRowOrColBase : public PixelOpcode {
+public:
+ struct SelectX {
+ static inline uint32 select(uint32 x, uint32 /*y*/) { return x; }
+ };
+
+ struct SelectY {
+ static inline uint32 select(uint32 /*x*/, uint32 y) { return y; }
+ };
+
+protected:
+ DeltaRowOrColBase(const RawImage& ri, ByteStream* bs) : PixelOpcode(ri, bs) {}
+};
+
+template <typename S>
+class DngOpcodes::DeltaRowOrCol : public DeltaRowOrColBase {
+public:
+ void setup(const RawImage& ri) override {
+ PixelOpcode::setup(ri);
+
+ // If we are working on a float image, no need to convert to int
+ if (ri->getDataType() != TYPE_USHORT16)
+ return;
+
+ deltaI.reserve(deltaF.size());
+ for (const auto f : deltaF) {
+ if (!valueIsOk(f))
+ ThrowRDE("Got float %f which is unacceptable.", f);
+ deltaI.emplace_back(static_cast<int>(f2iScale * f));
+ }
+ }
+
+protected:
+ const float f2iScale;
+ vector<float> deltaF;
+ vector<int> deltaI;
+
+ // only meaningful for ushort16 images!
+ virtual bool valueIsOk(float value) = 0;
+
+ DeltaRowOrCol(const RawImage& ri, ByteStream* bs, float f2iScale_)
+ : DeltaRowOrColBase(ri, bs), f2iScale(f2iScale_) {
+ const auto deltaF_count = bs->getU32();
+ bs->check(deltaF_count, 4);
+
+ // See PixelOpcode::applyOP(). We will access deltaF/deltaI up to (excl.)
+ // either ROI.getRight() or ROI.getBottom() index. Thus, we need to have
+ // either ROI.getRight() or ROI.getBottom() elements in there.
+ // FIXME: i guess not strictly true with pitch != 1.
+ const auto expectedSize =
+ S::select(getRoi().getRight(), getRoi().getBottom());
+ if (expectedSize != deltaF_count) {
+ ThrowRDE("Got unexpected number of elements (%u), expected %u.",
+ expectedSize, deltaF_count);
+ }
+
+ deltaF.reserve(deltaF_count);
+ std::generate_n(std::back_inserter(deltaF), deltaF_count, [&bs]() {
+ const auto F = bs->get<float>();
+ if (!std::isfinite(F))
+ ThrowRDE("Got bad float %f.", F);
+ return F;
+ });
+ }
+};
+
+// ****************************************************************************
+
+template <typename S>
+class DngOpcodes::OffsetPerRowOrCol final : public DeltaRowOrCol<S> {
+ // We have pixel value in range of [0..65535]. We apply some offset X.
+ // For this to generate a value within the same range , the offset X needs
+ // to have an absolute value of 65535. Since the offset is multiplied
+ // by f2iScale before applying, we need to divide by f2iScale here.
+ const double absLimit;
+
+ bool valueIsOk(float value) final { return std::abs(value) <= absLimit; }
+
+public:
+ explicit OffsetPerRowOrCol(const RawImage& ri, ByteStream* bs)
+ : DeltaRowOrCol<S>(ri, bs, 65535.0F),
+ absLimit(double(std::numeric_limits<ushort16>::max()) /
+ this->f2iScale) {}
+
+ void apply(const RawImage& ri) override {
+ if (ri->getDataType() == TYPE_USHORT16) {
+ this->template applyOP<ushort16>(
+ ri, [this](uint32 x, uint32 y, ushort16 v) {
+ return clampBits(this->deltaI[S::select(x, y)] + v, 16);
+ });
+ } else {
+ this->template applyOP<float>(ri, [this](uint32 x, uint32 y, float v) {
+ return this->deltaF[S::select(x, y)] + v;
+ });
+ }
+ }
+};
+
+template <typename S>
+class DngOpcodes::ScalePerRowOrCol final : public DeltaRowOrCol<S> {
+ // We have pixel value in range of [0..65535]. We scale by float X.
+ // For this to generate a value within the same range, the scale X needs
+ // to be in the range [0..65535]. However, we are operating with 32-bit
+ // signed integer space, so the new value can not be larger than 2^31,
+ // else we'd have signed integer overflow. Since the offset is multiplied
+ // by f2iScale before applying, we need to divide by f2iScale here.
+ static constexpr const double minLimit = 0.0;
+ static constexpr int rounding = 512;
+ const double maxLimit;
+
+ bool valueIsOk(float value) final {
+ return value >= minLimit && value <= maxLimit;
+ }
+
+public:
+ explicit ScalePerRowOrCol(const RawImage& ri, ByteStream* bs)
+ : DeltaRowOrCol<S>(ri, bs, 1024.0F),
+ maxLimit((double(std::numeric_limits<int>::max() - rounding) /
+ double(std::numeric_limits<ushort16>::max())) /
+ this->f2iScale) {}
+
+ void apply(const RawImage& ri) override {
+ if (ri->getDataType() == TYPE_USHORT16) {
+ this->template applyOP<ushort16>(ri, [this](uint32 x, uint32 y,
+ ushort16 v) {
+ return clampBits((this->deltaI[S::select(x, y)] * v + 512) >> 10, 16);
+ });
+ } else {
+ this->template applyOP<float>(ri, [this](uint32 x, uint32 y, float v) {
+ return this->deltaF[S::select(x, y)] * v;
+ });
+ }
+ }
+};
+
+// ****************************************************************************
+
+DngOpcodes::DngOpcodes(const RawImage& ri, TiffEntry* entry) {
+ ByteStream bs = entry->getData();
+
+ // DNG opcodes are always stored in big-endian byte order.
+ bs.setByteOrder(Endianness::big);
+
+ const auto opcode_count = bs.getU32();
+ auto origPos = bs.getPosition();
+
+ // validate opcode count. we either have to do this, or we can't preallocate
+ for (auto i = 0U; i < opcode_count; i++) {
+ bs.skipBytes(4); // code
+ bs.skipBytes(4); // version
+ bs.skipBytes(4); // flags
+ const auto opcode_size = bs.getU32();
+ bs.skipBytes(opcode_size);
+ }
+
+ bs.setPosition(origPos);
+
+ // okay, we may indeed have that many opcodes in here. now let's reserve
+ opcodes.reserve(opcode_count);
+
+ for (auto i = 0U; i < opcode_count; i++) {
+ auto code = bs.getU32();
+ bs.skipBytes(4); // ignore version
+#ifdef DEBUG
+ bs.skipBytes(4); // ignore flags
+#else
+ auto flags = bs.getU32();
+#endif
+ const auto opcode_size = bs.getU32();
+ ByteStream opcode_bs = bs.getStream(opcode_size);
+
+ const char* opName = nullptr;
+ constructor_t opConstructor = nullptr;
+ try {
+ std::tie(opName, opConstructor) = Map.at(code);
+ } catch (std::out_of_range&) {
+ ThrowRDE("Unknown unhandled Opcode: %d", code);
+ }
+
+ if (opConstructor != nullptr)
+ opcodes.emplace_back(opConstructor(ri, &opcode_bs));
+ else {
+#ifndef DEBUG
+ // Throw Error if not marked as optional
+ if (!(flags & 1))
+#endif
+ ThrowRDE("Unsupported Opcode: %d (%s)", code, opName);
+ }
+
+ if (opcode_bs.getRemainSize() != 0)
+ ThrowRDE("Inconsistent length of opcode");
+ }
+
+#ifdef DEBUG
+ assert(opcodes.size() == opcode_count);
+#endif
+}
+
+// Defined here as empty destructor, otherwise we'd need a complete definition
+// of the DngOpcode type in DngOpcodes.h
+DngOpcodes::~DngOpcodes() = default;
+
+void DngOpcodes::applyOpCodes(const RawImage& ri) {
+ for (const auto& code : opcodes) {
+ code->setup(ri);
+ code->apply(ri);
+ }
+}
+
+template <class Opcode>
+std::unique_ptr<DngOpcodes::DngOpcode>
+DngOpcodes::constructor(const RawImage& ri, ByteStream* bs) {
+ return std::make_unique<Opcode>(ri, bs);
+}
+
+// ALL opcodes specified in DNG Specification MUST be listed here.
+// however, some of them might not be implemented.
+const std::map<uint32, std::pair<const char*, DngOpcodes::constructor_t>>
+ DngOpcodes::Map = {
+ {1U, make_pair("WarpRectilinear", nullptr)},
+ {2U, make_pair("WarpFisheye", nullptr)},
+ {3U, make_pair("FixVignetteRadial", nullptr)},
+ {4U,
+ make_pair("FixBadPixelsConstant",
+ &DngOpcodes::constructor<DngOpcodes::FixBadPixelsConstant>)},
+ {5U, make_pair("FixBadPixelsList",
+ &DngOpcodes::constructor<DngOpcodes::FixBadPixelsList>)},
+ {6U, make_pair("TrimBounds",
+ &DngOpcodes::constructor<DngOpcodes::TrimBounds>)},
+ {7U,
+ make_pair("MapTable", &DngOpcodes::constructor<DngOpcodes::TableMap>)},
+ {8U, make_pair("MapPolynomial",
+ &DngOpcodes::constructor<DngOpcodes::PolynomialMap>)},
+ {9U, make_pair("GainMap", nullptr)},
+ {10U,
+ make_pair(
+ "DeltaPerRow",
+ &DngOpcodes::constructor<
+ DngOpcodes::OffsetPerRowOrCol<DeltaRowOrColBase::SelectY>>)},
+ {11U,
+ make_pair(
+ "DeltaPerColumn",
+ &DngOpcodes::constructor<
+ DngOpcodes::OffsetPerRowOrCol<DeltaRowOrColBase::SelectX>>)},
+ {12U,
+ make_pair(
+ "ScalePerRow",
+ &DngOpcodes::constructor<
+ DngOpcodes::ScalePerRowOrCol<DeltaRowOrColBase::SelectY>>)},
+ {13U,
+ make_pair(
+ "ScalePerColumn",
+ &DngOpcodes::constructor<
+ DngOpcodes::ScalePerRowOrCol<DeltaRowOrColBase::SelectX>>)},
+
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.h
b/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.h
new file mode 100644
index 00000000..ce90ddec
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/DngOpcodes.h
@@ -0,0 +1,73 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 <map> // for map
+#include <memory> // for unique_ptr
+#include <utility> // for pair
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+class TiffEntry;
+
+class ByteStream;
+
+class DngOpcodes
+{
+public:
+ DngOpcodes(const RawImage& ri, TiffEntry* entry);
+ ~DngOpcodes();
+ void applyOpCodes(const RawImage& ri);
+
+private:
+ class DngOpcode;
+ std::vector<std::unique_ptr<DngOpcode>> opcodes;
+
+protected:
+ class FixBadPixelsConstant;
+ class FixBadPixelsList;
+ class ROIOpcode;
+ class DummyROIOpcode;
+ class TrimBounds;
+ class PixelOpcode;
+ class LookupOpcode;
+ class TableMap;
+ class PolynomialMap;
+ class DeltaRowOrColBase;
+ template <typename S> class DeltaRowOrCol;
+ template <typename S> class OffsetPerRowOrCol;
+ template <typename S> class ScalePerRowOrCol;
+
+ template <class Opcode>
+ static std::unique_ptr<DngOpcode> constructor(const RawImage& ri,
+ ByteStream* bs);
+
+ using constructor_t = std::unique_ptr<DngOpcode> (*)(const RawImage& ri,
+ ByteStream* bs);
+ static const std::map<uint32, std::pair<const char*, constructor_t>> Map;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/ErrorLog.cpp
b/subprojects/rawspeed/src/librawspeed/common/ErrorLog.cpp
new file mode 100644
index 00000000..80167481
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/ErrorLog.cpp
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+*/
+
+#include "ErrorLog.h"
+#include "common/Mutex.h" // for MutexLocker
+#include <utility> // for move
+
+namespace rawspeed {
+
+void ErrorLog::setError(const std::string& err) {
+ MutexLocker guard(&mutex);
+ errors.push_back(err);
+}
+
+bool ErrorLog::isTooManyErrors(unsigned many, std::string* firstErr) {
+ MutexLocker guard(&mutex);
+
+ if (errors.size() < many)
+ return false;
+
+ if (!firstErr)
+ return true;
+
+ *firstErr = errors[0];
+ return true;
+}
+
+std::vector<std::string>&& ErrorLog::getErrors() {
+ MutexLocker guard(&mutex);
+ return std::move(errors);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/ErrorLog.h
b/subprojects/rawspeed/src/librawspeed/common/ErrorLog.h
new file mode 100644
index 00000000..71a9b8df
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/ErrorLog.h
@@ -0,0 +1,41 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 "ThreadSafetyAnalysis.h" // for REQUIRES, GUARDED_BY
+#include "common/Mutex.h" // for Mutex
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class ErrorLog {
+ Mutex mutex;
+ std::vector<std::string> errors GUARDED_BY(mutex);
+
+public:
+ void setError(const std::string& err) REQUIRES(!mutex);
+ bool isTooManyErrors(unsigned many, std::string* firstErr = nullptr)
+ REQUIRES(!mutex);
+ std::vector<std::string>&& getErrors() REQUIRES(!mutex);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/GetNumberOfProcessorCores.cpp
b/subprojects/rawspeed/src/librawspeed/common/GetNumberOfProcessorCores.cpp
new file mode 100644
index 00000000..2dc09fae
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/GetNumberOfProcessorCores.cpp
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2016-2019 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
+*/
+
+#include "rawspeedconfig.h" // for HAVE_OPENMP
+#include "common/Common.h" // for rawspeed_get_number_of_processor_cores
+
+#ifdef HAVE_OPENMP
+#include <omp.h>
+#endif
+
+// define this function, it is only declared in rawspeed:
+#ifdef HAVE_OPENMP
+extern "C" int __attribute__((visibility("default")))
+rawspeed_get_number_of_processor_cores() {
+ return omp_get_max_threads();
+}
+#else
+extern "C" int __attribute__((const, visibility("default")))
+rawspeed_get_number_of_processor_cores() {
+ return 1;
+}
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/common/Memory.cpp
b/subprojects/rawspeed/src/librawspeed/common/Memory.cpp
new file mode 100644
index 00000000..1f8350b3
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Memory.cpp
@@ -0,0 +1,107 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+*/
+
+#include "rawspeedconfig.h"
+
+#include "common/Memory.h"
+
+#ifndef NDEBUG
+#include "common/Common.h" // for isPowerOfTwo, isAligned
+#endif
+
+#include <cassert> // for assert
+#include <cstddef> // for size_t, uintptr_t
+
+#if defined(HAVE_MM_MALLOC)
+// for _mm_malloc, _mm_free
+#include <xmmintrin.h>
+#elif defined(HAVE_ALIGNED_MALLOC)
+extern "C" {
+#include <malloc.h> // for _aligned_malloc, _aligned_free
+}
+#else
+#include <cstdlib> // for posix_memalign / aligned_alloc / malloc; free
+#endif
+
+namespace rawspeed {
+
+void* alignedMalloc(size_t size, size_t alignment) {
+ assert(isPowerOfTwo(alignment)); // for posix_memalign, _aligned_malloc
+ assert(isAligned(alignment, sizeof(void*))); // for posix_memalign
+ assert(isAligned(size, alignment)); // for aligned_alloc
+
+ void* ptr = nullptr;
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // workaround ASAN's broken allocator_may_return_null option
+ // plus, avoidance of libFuzzer's rss_limit_mb option
+ // if trying to alloc more than 2GB, just return null.
+ // else it would abort() the whole program...
+ if (size > 2UL << 30UL)
+ return ptr;
+#endif
+
+#if defined(HAVE_POSIX_MEMALIGN)
+ if (0 != posix_memalign(&ptr, alignment, size))
+ return nullptr;
+#elif defined(HAVE_ALIGNED_ALLOC)
+ ptr = aligned_alloc(alignment, size);
+#elif defined(HAVE_MM_MALLOC)
+ ptr = _mm_malloc(size, alignment);
+#elif defined(HAVE_ALIGNED_MALLOC)
+ ptr = _aligned_malloc(size, alignment);
+#else
+#pragma message "No aligned malloc() implementation available!"
+ assert(alignment <= alignof(std::max_align_t));
+#ifdef __APPLE__
+ // apple malloc() aligns to 16 by default
+ assert(alignment <= 16);
+#endif
+
+ ptr = malloc(size); // NOLINT
+#endif
+
+ assert(isAligned(ptr, alignment));
+
+ return ptr;
+}
+
+void alignedFree(void* ptr) {
+#if defined(HAVE_MM_MALLOC)
+ _mm_free(ptr);
+#elif defined(HAVE_ALIGNED_MALLOC)
+ _aligned_free(ptr);
+#else
+ free(ptr); // NOLINT
+#endif
+}
+
+void alignedFreeConstPtr(const void* ptr) {
+// an exception, specified by EXP05-C-EX1 and EXP55-CPP-EX1
+#if defined(HAVE_MM_MALLOC)
+ _mm_free(const_cast<void*>(ptr));
+#elif defined(HAVE_ALIGNED_MALLOC)
+ _aligned_free(const_cast<void*>(ptr));
+#else
+ free(const_cast<void*>(ptr)); // NOLINT
+#endif
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Memory.h
b/subprojects/rawspeed/src/librawspeed/common/Memory.h
new file mode 100644
index 00000000..76c6abf7
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Memory.h
@@ -0,0 +1,100 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 "rawspeedconfig.h"
+
+#include "common/Common.h" // for isPowerOfTwo
+#include <cstddef> // for size_t
+#include <cstdint> // for SIZE_MAX
+
+namespace rawspeed {
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wunknown-attributes"
+#pragma GCC diagnostic ignored "-Wattributes"
+
+// coverity[+alloc]
+void* alignedMalloc(size_t size, size_t alignment)
+ __attribute__((malloc, warn_unused_result, alloc_size(1), alloc_align(2),
+ deprecated("use alignedMalloc<alignment>(size)")));
+
+template <typename T, size_t alignment>
+// coverity[+alloc]
+inline T* __attribute__((malloc, warn_unused_result, alloc_size(1)))
+alignedMalloc(size_t size) {
+ static_assert(alignment >= alignof(T), "unsufficient alignment");
+ static_assert(isPowerOfTwo(alignment), "not power-of-two");
+ static_assert(isAligned(alignment, sizeof(void*)),
+ "not multiple of sizeof(void*)");
+
+#if !(defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_ALIGNED_ALLOC) || \
+ defined(HAVE_MM_MALLOC) || defined(HAVE_ALIGNED_MALLOC))
+ static_assert(alignment <= alignof(std::max_align_t), "too high alignment");
+#if defined(__APPLE__)
+ // apple malloc() aligns to 16 by default. can not expect any more
+ static_assert(alignment <= 16, "on OSX, plain malloc() aligns to 16");
+#endif
+#endif
+
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ return reinterpret_cast<T*>(alignedMalloc(size, alignment));
+}
+
+#pragma GCC diagnostic pop
+
+template <typename T, size_t alignment, bool doRoundUp = false>
+// coverity[+alloc]
+inline T* __attribute__((malloc, warn_unused_result))
+alignedMallocArray(size_t nmemb, size_t size) {
+ // Check for size_t overflow
+ if (size && nmemb > SIZE_MAX / size)
+ return nullptr;
+
+ size *= nmemb;
+
+ if (doRoundUp)
+ size = roundUp(size, alignment);
+
+ return alignedMalloc<T, alignment>(size);
+}
+
+template <typename T, size_t alignment, typename T2, bool doRoundUp = false>
+// coverity[+alloc]
+inline T* __attribute__((malloc, warn_unused_result))
+alignedMallocArray(size_t nmemb) {
+ static_assert(sizeof(T), "???");
+ static_assert(sizeof(T2), "???");
+ static_assert(alignment >= alignof(T), "unsufficient alignment");
+ static_assert(alignment >= alignof(T2), "unsufficient alignment");
+ static_assert(isPowerOfTwo(sizeof(T2)), "not power-of-two");
+
+ return alignedMallocArray<T, alignment, doRoundUp>(nmemb, sizeof(T2));
+}
+
+// coverity[+free : arg-0]
+void alignedFree(void* ptr);
+
+// coverity[+free : arg-0]
+void alignedFreeConstPtr(const void* ptr);
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Mutex.h
b/subprojects/rawspeed/src/librawspeed/common/Mutex.h
new file mode 100644
index 00000000..707cd651
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Mutex.h
@@ -0,0 +1,117 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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 "rawspeedconfig.h"
+#include "ThreadSafetyAnalysis.h"
+
+#ifdef HAVE_OPENMP
+#include <omp.h>
+#endif
+
+namespace rawspeed {
+
+// Defines an annotated interface for mutexes.
+// These methods can be implemented to use any internal mutex implementation.
+#ifdef HAVE_OPENMP
+
+class CAPABILITY("mutex") Mutex final {
+ omp_lock_t mutex;
+
+public:
+ explicit Mutex() { omp_init_lock(&mutex); }
+
+ Mutex(const Mutex&) = delete;
+ Mutex(Mutex&&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
+ Mutex& operator=(Mutex&&) = delete;
+
+ ~Mutex() { omp_destroy_lock(&mutex); }
+
+ // Acquire/lock this mutex exclusively. Only one thread can have exclusive
+ // access at any one time. Write operations to guarded data require an
+ // exclusive lock.
+ void Lock() ACQUIRE() { omp_set_lock(&mutex); }
+
+ // Release/unlock an exclusive mutex.
+ void Unlock() RELEASE() { omp_unset_lock(&mutex); }
+
+ // Try to acquire the mutex. Returns true on success, and false on failure.
+ bool TryLock() TRY_ACQUIRE(true) { return omp_test_lock(&mutex); }
+
+ // For negative capabilities.
+ const Mutex& operator!() const { return *this; }
+};
+
+#else
+
+class CAPABILITY("mutex") Mutex final {
+public:
+ explicit Mutex() = default;
+
+ Mutex(const Mutex&) = delete;
+ Mutex(Mutex&&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
+ Mutex& operator=(Mutex&&) = delete;
+
+ ~Mutex() = default;
+
+ // Acquire/lock this mutex exclusively. Only one thread can have exclusive
+ // access at any one time. Write operations to guarded data require an
+ // exclusive lock.
+ void __attribute__((const)) Lock() const ACQUIRE() {
+ // NOP, since there is no mutex. only here to still check for proper locking
+ }
+
+ // Release/unlock an exclusive mutex.
+ void __attribute__((const)) Unlock() const RELEASE() {
+ // NOP, since there is no mutex. only here to still check for proper locking
+ }
+
+ // Try to acquire the mutex. Returns true on success, and false on failure.
+ bool __attribute__((const)) TryLock() const TRY_ACQUIRE(true) {
+ // NOP, since there is no mutex. only here to still check for proper locking
+ return true;
+ }
+
+ // For negative capabilities.
+ const Mutex& operator!() const { return *this; }
+};
+
+#endif
+
+// MutexLocker is an RAII class that acquires a mutex in its constructor, and
+// releases it in its destructor.
+class SCOPED_CAPABILITY MutexLocker final {
+ Mutex* mut;
+
+public:
+ explicit MutexLocker(Mutex* mu) ACQUIRE(mu) : mut(mu) { mu->Lock(); }
+
+ MutexLocker(const MutexLocker&) = delete;
+ MutexLocker(MutexLocker&&) = delete;
+ MutexLocker& operator=(const MutexLocker&) = delete;
+ MutexLocker& operator=(MutexLocker&&) = delete;
+
+ ~MutexLocker() RELEASE() { mut->Unlock(); }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/NORangesSet.h
b/subprojects/rawspeed/src/librawspeed/common/NORangesSet.h
new file mode 100644
index 00000000..af1e208f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/NORangesSet.h
@@ -0,0 +1,37 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/Range.h" // for RangesOverlap
+#include <set> // IWYU pragma: export
+// IWYU pragma: no_include <bits/stl_set.h>
+
+namespace rawspeed {
+
+template <typename T> struct RangesOverlapCmp final {
+ constexpr bool operator()(const T& lhs, const T& rhs) const {
+ return !RangesOverlap(lhs, rhs);
+ }
+};
+
+template <typename T> using NORangesSet = std::set<T, RangesOverlapCmp<T>>;
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Optional.h
b/subprojects/rawspeed/src/librawspeed/common/Optional.h
new file mode 100644
index 00000000..78a03d53
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Optional.h
@@ -0,0 +1,52 @@
+/*
+ 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 <utility> // for move
+
+namespace rawspeed {
+
+template <class T> class Optional final {
+ T data;
+ bool hasData = false;
+
+public:
+ Optional() = default;
+
+ explicit Optional(T RHS) : data(RHS), hasData(true) {}
+
+ Optional& operator=(T RHS) {
+ Optional tmp(RHS);
+ *this = std::move(tmp);
+ return *this;
+ }
+
+ bool hasValue() const { return hasData; }
+
+ void reset() { hasData = false; }
+
+ T getValue() const {
+ assert(hasValue());
+ return data;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Point.h
b/subprojects/rawspeed/src/librawspeed/common/Point.h
new file mode 100644
index 00000000..b77cd358
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Point.h
@@ -0,0 +1,212 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 <algorithm> // for max, min
+#include <cassert> // for assert
+#include <type_traits> // for make_signed
+
+namespace rawspeed {
+
+class iPoint2D {
+public:
+ using value_type = int;
+ using area_type = unsigned long long;
+
+ constexpr iPoint2D() = default;
+ constexpr iPoint2D(value_type a, value_type b) : x(a), y(b) {}
+
+ constexpr iPoint2D operator+(const iPoint2D& rhs) const {
+ return {x + rhs.x, y + rhs.y};
+ }
+ constexpr iPoint2D operator-(const iPoint2D& rhs) const {
+ return {x - rhs.x, y - rhs.y};
+ }
+
+ iPoint2D& operator+=(const iPoint2D& rhs) {
+ *this = operator+(rhs);
+ return *this;
+ }
+ iPoint2D& operator-=(const iPoint2D& rhs) {
+ *this = operator-(rhs);
+ return *this;
+ }
+
+ constexpr bool operator==(const iPoint2D& rhs) const {
+ return x == rhs.x && y == rhs.y;
+ }
+ constexpr bool operator!=(const iPoint2D& rhs) const {
+ return !operator==(rhs);
+ }
+
+ constexpr bool operator>(const iPoint2D& rhs) const {
+ return x > rhs.x && y > rhs.y;
+ }
+ constexpr bool operator<(const iPoint2D& rhs) const {
+ return x < rhs.x && y < rhs.y;
+ }
+
+ constexpr bool operator>=(const iPoint2D& rhs) const {
+ return x >= rhs.x && y >= rhs.y;
+ }
+ constexpr bool operator<=(const iPoint2D& rhs) const {
+ return x <= rhs.x && y <= rhs.y;
+ }
+
+ bool hasPositiveArea() const { return operator>({0, 0}); }
+
+ area_type __attribute__((pure)) area() const {
+ using signed_area = std::make_signed<area_type>::type;
+
+ if (x >= 0 && y >= 0)
+ return static_cast<area_type>(x) * static_cast<area_type>(y);
+ if (x >= 0 && y < 0)
+ return static_cast<area_type>(x) * (-1 * static_cast<signed_area>(y));
+ if (y >= 0 && x < 0)
+ return static_cast<area_type>(y) * (-1 * static_cast<signed_area>(x));
+
+ assert(x < 0 && y < 0);
+ return static_cast<signed_area>(x) * static_cast<signed_area>(y);
+ }
+
+ constexpr bool isThisInside(const iPoint2D& rhs) const {
+ return operator<=(rhs);
+ }
+
+ constexpr iPoint2D getSmallest(const iPoint2D& rhs) const {
+ return {x < rhs.x ? x : rhs.x, y < rhs.y ? y : rhs.y};
+ }
+
+ value_type x = 0;
+ value_type y = 0;
+};
+
+/* Helper class for managing a rectangle in 2D space. */
+class iRectangle2D {
+public:
+ constexpr iRectangle2D() = default;
+ constexpr iRectangle2D(const iPoint2D& pos_, const iPoint2D& dim_)
+ : pos(pos_), dim(dim_) {}
+ constexpr iRectangle2D(iPoint2D&& pos_, iPoint2D&& dim_)
+ : pos(pos_), dim(dim_) {}
+
+ constexpr iRectangle2D(int w, int h) : dim({w, h}) {}
+ constexpr iRectangle2D(int x_pos, int y_pos, int w, int h)
+ : pos({x_pos, y_pos}), dim({w, h}) {}
+
+ constexpr int getTop() const { return pos.y; }
+ constexpr int getBottom() const { return pos.y + dim.y; }
+ constexpr int getLeft() const { return pos.x; }
+ constexpr int getRight() const { return pos.x + dim.x; }
+ constexpr int getWidth() const { return dim.x; }
+ constexpr int getHeight() const { return dim.y; }
+ constexpr iPoint2D getTopLeft() const { return pos; }
+ constexpr iPoint2D getBottomRight() const { return dim + pos; }
+ constexpr bool hasPositiveArea() const { return (dim.x > 0) && (dim.y > 0); }
+
+ constexpr bool isThisInside(const iRectangle2D& otherPoint) const {
+ return pos >= otherPoint.pos &&
+ getBottomRight() <= otherPoint.getBottomRight();
+ }
+
+ constexpr bool isPointInsideInclusive(const iPoint2D& checkPoint) const {
+ return pos <= checkPoint && getBottomRight() >= checkPoint;
+ }
+
+ unsigned int area() const { return dim.area(); }
+
+ void offset(const iPoint2D& offset_) { pos += offset_; }
+
+ /* Retains size */
+ void setTopLeft(const iPoint2D& top_left) { pos = top_left; }
+ void setTopLeft(iPoint2D&& top_left) { pos = top_left; }
+
+ /* Set BR */
+ void setBottomRightAbsolute(const iPoint2D& bottom_right) {
+ dim = bottom_right - pos;
+ }
+
+ void setAbsolute(const iPoint2D& top_left, const iPoint2D& bottom_right) {
+ pos = top_left;
+ setBottomRightAbsolute(bottom_right);
+ }
+ void setAbsolute(iPoint2D&& top_left, iPoint2D&& bottom_right) {
+ pos = top_left;
+ setBottomRightAbsolute(bottom_right);
+ }
+ void setAbsolute(int x1, int y1, int x2, int y2) {
+ setAbsolute({x1, y1}, {x2, y2});
+ }
+
+ void setSize(const iPoint2D& size) { dim = size; }
+ void setSize(iPoint2D&& size) { dim = size; }
+
+ /* Crop, so area is postitive, and return true, if there is any area left */
+ /* This will ensure that bottomright is never on the left/top of the offset */
+ bool cropArea() {
+ dim.x = std::max(0, dim.x);
+ dim.y = std::max(0, dim.y);
+ return hasPositiveArea();
+ }
+
+ /* This will make sure that offset is positive, and make the area smaller if
+ * needed */
+ /* This will return true if there is any area left */
+ bool cropOffsetToZero() {
+ iPoint2D crop_pixels;
+ if (pos.x < 0) {
+ crop_pixels.x = -(pos.x);
+ pos.x = 0;
+ }
+ if (pos.y < 0) {
+ crop_pixels.y = -pos.y;
+ pos.y = 0;
+ }
+ dim -= crop_pixels;
+ return cropArea();
+ }
+
+ iRectangle2D getOverlap(const iRectangle2D& other) const {
+ iRectangle2D overlap;
+ iPoint2D br1 = getBottomRight();
+ iPoint2D br2 = other.getBottomRight();
+ overlap.setAbsolute(std::max(pos.x, other.pos.x),
+ std::max(pos.y, other.pos.y), std::min(br1.x, br2.x),
+ std::min(br1.y, br2.y));
+ return overlap;
+ }
+
+ iRectangle2D combine(const iRectangle2D& other) const {
+ iRectangle2D combined;
+ iPoint2D br1 = getBottomRight();
+ iPoint2D br2 = other.getBottomRight();
+ combined.setAbsolute(std::min(pos.x, other.pos.x),
+ std::min(pos.y, other.pos.y), std::max(br1.x, br2.x),
+ std::max(br2.y, br2.y));
+ return combined;
+ }
+
+ iPoint2D pos{0, 0};
+ iPoint2D dim{0, 0};
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Range.h
b/subprojects/rawspeed/src/librawspeed/common/Range.h
new file mode 100644
index 00000000..d9e2b84a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Range.h
@@ -0,0 +1,75 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 <algorithm> // for min
+#include <cassert> // for assert
+#include <type_traits> // for enable_if, is_pointer
+#include <utility> // for pair
+
+namespace rawspeed {
+
+template <typename T> class Range final {
+ T base;
+ typename std::make_unsigned<T>::type size;
+
+public:
+ constexpr Range() = default;
+
+ template <typename T2,
+ typename = std::enable_if_t<std::is_unsigned<T2>::value>>
+ constexpr Range(T base_, T2 size_) : base(base_), size(size_) {}
+
+ constexpr T __attribute__((const)) begin() const { return base; }
+
+ constexpr T __attribute__((const)) end() const { return base + T(size); }
+};
+
+template <typename Tr, typename Tv>
+inline constexpr bool __attribute__((const))
+RangeContains(const Tr& r, Tv pos) {
+ if (pos < r.begin())
+ return false;
+
+ assert(pos >= r.begin());
+ return r.end() > pos;
+}
+
+template <typename T>
+inline constexpr bool __attribute__((const))
+RangesOverlap(const T& lhs, const T& rhs) {
+ if (&lhs == &rhs)
+ return true;
+
+ if (lhs.begin() == rhs.begin())
+ return true;
+
+ const std::pair<const T&, const T&> ordered =
+ std::minmax(lhs, rhs, [](const T& r0, const T& r1) {
+ assert(r0.begin() != r1.begin());
+ return r0.begin() < r1.begin();
+ });
+
+ assert(ordered.first.begin() < ordered.second.begin());
+ return RangeContains(ordered.first, ordered.second.begin());
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/RawImage.cpp
b/subprojects/rawspeed/src/librawspeed/common/RawImage.cpp
new file mode 100644
index 00000000..ee499433
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/RawImage.cpp
@@ -0,0 +1,582 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h"
+#include "common/RawImage.h"
+#include "MemorySanitizer.h" // for MSan
+#include "common/Memory.h" // for alignedFree, alignedMalloc...
+#include "decoders/RawDecoderException.h" // for ThrowRDE, RawDecoderException
+#include "io/IOException.h" // for IOException
+#include "parsers/TiffParserException.h" // for TiffParserException
+#include <algorithm> // for fill_n, min
+#include <cassert> // for assert
+#include <cmath> // for NAN
+#include <cstdlib> // for size_t
+#include <cstring> // for memcpy, memset
+#include <limits> // for numeric_limits
+#include <memory> // for unique_ptr, make_unique
+#include <utility> // for move, swap
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+#include "AddressSanitizer.h" // for ASan::...
+#endif
+
+using std::string;
+
+namespace rawspeed {
+
+RawImageData::RawImageData() : cfa(iPoint2D(0, 0)) {
+ blackLevelSeparate.fill(-1);
+}
+
+RawImageData::RawImageData(const iPoint2D& _dim, uint32 _bpc, uint32 _cpp)
+ : dim(_dim), isCFA(_cpp == 1), cfa(iPoint2D(0, 0)), cpp(_cpp) {
+ assert(_bpc > 0);
+
+ if (cpp > std::numeric_limits<decltype(bpp)>::max() / _bpc)
+ ThrowRDE("Components-per-pixel is too large.");
+
+ bpp = _bpc * _cpp;
+ blackLevelSeparate.fill(-1);
+ createData();
+}
+
+ImageMetaData::ImageMetaData() {
+ subsampling.x = subsampling.y = 1;
+ isoSpeed = 0;
+ pixelAspectRatio = 1;
+ fujiRotationPos = 0;
+ wbCoeffs.fill(NAN);
+}
+
+RawImageData::~RawImageData() {
+ assert(dataRefCount == 0);
+ mOffset = iPoint2D(0, 0);
+
+ destroyData();
+}
+
+
+void RawImageData::createData() {
+ static constexpr const auto alignment = 16;
+
+ if (dim.x > 65535 || dim.y > 65535)
+ ThrowRDE("Dimensions too large for allocation.");
+ if (dim.x <= 0 || dim.y <= 0)
+ ThrowRDE("Dimension of one sides is less than 1 - cannot allocate image.");
+ if (data)
+ ThrowRDE("Duplicate data allocation in createData.");
+
+ // want each line to start at 16-byte aligned address
+ pitch = roundUp(static_cast<size_t>(dim.x) * bpp, alignment);
+ assert(isAligned(pitch, alignment));
+
+#if defined(DEBUG) || __has_feature(address_sanitizer) || \
+ defined(__SANITIZE_ADDRESS__)
+ // want to ensure that we have some padding
+ pitch += alignment * alignment;
+ assert(isAligned(pitch, alignment));
+#endif
+
+ padding = pitch - dim.x * bpp;
+
+#if defined(DEBUG) || __has_feature(address_sanitizer) || \
+ defined(__SANITIZE_ADDRESS__)
+ assert(padding > 0);
+#endif
+
+ data = alignedMallocArray<uchar8, alignment>(dim.y, pitch);
+
+ if (!data)
+ ThrowRDE("Memory Allocation failed.");
+
+ uncropped_dim = dim;
+
+#ifndef NDEBUG
+ if (dim.y > 1) {
+ // padding is the size of the area after last pixel of line n
+ // and before the first pixel of line n+1
+ assert(getData(dim.x - 1, 0) + bpp + padding == getData(0, 1));
+ }
+
+ for (int j = 0; j < dim.y; j++) {
+ const uchar8* const line = getData(0, j);
+ // each line is indeed 16-byte aligned
+ assert(isAligned(line, alignment));
+ }
+#endif
+
+ poisonPadding();
+}
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+void RawImageData::poisonPadding() {
+ if (padding <= 0)
+ return;
+
+ for (int j = 0; j < uncropped_dim.y; j++) {
+ const uchar8* const curr_line_end =
+ getDataUncropped(uncropped_dim.x - 1, j) + bpp;
+
+ // and now poison the padding.
+ ASan::PoisonMemoryRegion(curr_line_end, padding);
+ }
+}
+#else
+void RawImageData::poisonPadding() {
+ // if we are building without ASAN, then there is no need/way to poison.
+ // however, i think it is better to have such an empty function rather
+ // than making this whole function not exist in ASAN-less builds
+}
+#endif
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+void RawImageData::unpoisonPadding() {
+ if (padding <= 0)
+ return;
+
+ for (int j = 0; j < uncropped_dim.y; j++) {
+ const uchar8* const curr_line_end =
+ getDataUncropped(uncropped_dim.x - 1, j) + bpp;
+
+ // and now unpoison the padding.
+ ASan::UnPoisonMemoryRegion(curr_line_end, padding);
+ }
+}
+#else
+void RawImageData::unpoisonPadding() {
+ // if we are building without ASAN, then there is no need/way to poison.
+ // however, i think it is better to have such an empty function rather
+ // than making this whole function not exist in ASAN-less builds
+}
+#endif
+
+void RawImageData::checkRowIsInitialized(int row) {
+ const auto rowsize = bpp * uncropped_dim.x;
+
+ const uchar8* const curr_line = getDataUncropped(0, row);
+
+ // and check that image line is initialized.
+ // do note that we are avoiding padding here.
+ MSan::CheckMemIsInitialized(curr_line, rowsize);
+}
+
+#if __has_feature(memory_sanitizer) || defined(__SANITIZE_MEMORY__)
+void RawImageData::checkMemIsInitialized() {
+ for (int j = 0; j < uncropped_dim.y; j++)
+ checkRowIsInitialized(j);
+}
+#else
+void RawImageData::checkMemIsInitialized() {
+ // While we could use the same version for non-MSAN build, even though it
+ // does not do anything, i don't think it will be fully optimized away,
+ // the getDataUncropped() call may still be there. To be re-evaluated.
+}
+#endif
+
+void RawImageData::destroyData() {
+ if (data)
+ alignedFree(data);
+ if (mBadPixelMap)
+ alignedFree(mBadPixelMap);
+ data = nullptr;
+ mBadPixelMap = nullptr;
+}
+
+void RawImageData::setCpp(uint32 val) {
+ if (data)
+ ThrowRDE("Attempted to set Components per pixel after data allocation");
+ if (val > 4) {
+ ThrowRDE(
+ "Only up to 4 components per pixel is support - attempted to set: %d",
+ val);
+ }
+
+ bpp /= cpp;
+ cpp = val;
+ bpp *= val;
+}
+
+uchar8* RawImageData::getData() const {
+ if (!data)
+ ThrowRDE("Data not yet allocated.");
+ return &data[mOffset.y*pitch+mOffset.x*bpp];
+}
+
+uchar8* RawImageData::getData(uint32 x, uint32 y) {
+ if (x >= static_cast<unsigned>(uncropped_dim.x))
+ ThrowRDE("X Position outside image requested.");
+ if (y >= static_cast<unsigned>(uncropped_dim.y))
+ ThrowRDE("Y Position outside image requested.");
+
+ x += mOffset.x;
+ y += mOffset.y;
+
+ if (!data)
+ ThrowRDE("Data not yet allocated.");
+
+ return &data[static_cast<size_t>(y) * pitch + x * bpp];
+}
+
+uchar8* RawImageData::getDataUncropped(uint32 x, uint32 y) {
+ if (x >= static_cast<unsigned>(uncropped_dim.x))
+ ThrowRDE("X Position outside image requested.");
+ if (y >= static_cast<unsigned>(uncropped_dim.y))
+ ThrowRDE("Y Position outside image requested.");
+
+ if (!data)
+ ThrowRDE("Data not yet allocated.");
+
+ return &data[static_cast<size_t>(y) * pitch + x * bpp];
+}
+
+iPoint2D __attribute__((pure)) rawspeed::RawImageData::getUncroppedDim() const {
+ return uncropped_dim;
+}
+
+iPoint2D __attribute__((pure)) RawImageData::getCropOffset() const {
+ return mOffset;
+}
+
+void RawImageData::subFrame(iRectangle2D crop) {
+ if (!crop.dim.isThisInside(dim - crop.pos)) {
+ writeLog(DEBUG_PRIO_WARNING, "WARNING: RawImageData::subFrame - Attempted "
+ "to create new subframe larger than original "
+ "size. Crop skipped.");
+ return;
+ }
+ if (crop.pos.x < 0 || crop.pos.y < 0 || !crop.hasPositiveArea()) {
+ writeLog(DEBUG_PRIO_WARNING, "WARNING: RawImageData::subFrame - Negative "
+ "crop offset. Crop skipped.");
+ return;
+ }
+
+ // if CFA, and not X-Trans, adjust.
+ if (isCFA && cfa.getDcrawFilter() != 1 && cfa.getDcrawFilter() != 9) {
+ cfa.shiftLeft(crop.pos.x);
+ cfa.shiftDown(crop.pos.y);
+ }
+
+ mOffset += crop.pos;
+ dim = crop.dim;
+}
+
+void RawImageData::createBadPixelMap()
+{
+ if (!isAllocated())
+ ThrowRDE("(internal) Bad pixel map cannot be allocated before image.");
+ mBadPixelMapPitch = roundUp(roundUpDivision(uncropped_dim.x, 8), 16);
+ mBadPixelMap =
+ alignedMallocArray<uchar8, 16>(uncropped_dim.y, mBadPixelMapPitch);
+ memset(mBadPixelMap, 0,
+ static_cast<size_t>(mBadPixelMapPitch) * uncropped_dim.y);
+ if (!mBadPixelMap)
+ ThrowRDE("Memory Allocation failed.");
+}
+
+RawImage::RawImage(RawImageData* p) : p_(p) {
+ MutexLocker guard(&p_->mymutex);
+ ++p_->dataRefCount;
+}
+
+RawImage::RawImage(const RawImage& p) : p_(p.p_) {
+ MutexLocker guard(&p_->mymutex);
+ ++p_->dataRefCount;
+}
+
+RawImage::~RawImage() {
+ p_->mymutex.Lock();
+
+ --p_->dataRefCount;
+
+ if (p_->dataRefCount == 0) {
+ p_->mymutex.Unlock();
+ delete p_;
+ return;
+ }
+
+ p_->mymutex.Unlock();
+}
+
+void RawImageData::transferBadPixelsToMap()
+{
+ MutexLocker guard(&mBadPixelMutex);
+ if (mBadPixelPositions.empty())
+ return;
+
+ if (!mBadPixelMap)
+ createBadPixelMap();
+
+ for (unsigned int pos : mBadPixelPositions) {
+ ushort16 pos_x = pos & 0xffff;
+ ushort16 pos_y = pos >> 16;
+
+ assert(pos_x < static_cast<ushort16>(uncropped_dim.x));
+ assert(pos_y < static_cast<ushort16>(uncropped_dim.y));
+
+ mBadPixelMap[mBadPixelMapPitch * pos_y + (pos_x >> 3)] |= 1 << (pos_x&7);
+ }
+ mBadPixelPositions.clear();
+}
+
+void RawImageData::fixBadPixels()
+{
+#if !defined (EMULATE_DCRAW_BAD_PIXELS)
+
+ /* Transfer if not already done */
+ transferBadPixelsToMap();
+
+#if 0 // For testing purposes
+ if (!mBadPixelMap)
+ createBadPixelMap();
+ for (int y = 400; y < 700; y++){
+ for (int x = 1200; x < 1700; x++) {
+ mBadPixelMap[mBadPixelMapPitch * y + (x >> 3)] |= 1 << (x&7);
+ }
+ }
+#endif
+
+ /* Process bad pixels, if any */
+ if (mBadPixelMap)
+ startWorker(RawImageWorker::FIX_BAD_PIXELS, false);
+
+#else // EMULATE_DCRAW_BAD_PIXELS - not recommended, testing purposes only
+
+ for (vector<uint32>::iterator i=mBadPixelPositions.begin(); i != mBadPixelPositions.end(); ++i) {
+ uint32 pos = *i;
+ uint32 pos_x = pos&0xffff;
+ uint32 pos_y = pos>>16;
+ uint32 total = 0;
+ uint32 div = 0;
+ // 0 side covered by unsignedness.
+ for (uint32 r=pos_x-2; r<=pos_x+2 && r<(uint32)uncropped_dim.x; r+=2) {
+ for (uint32 c=pos_y-2; c<=pos_y+2 && c<(uint32)uncropped_dim.y; c+=2) {
+ ushort16* pix = (ushort16*)getDataUncropped(r,c);
+ if (*pix) {
+ total += *pix;
+ div++;
+ }
+ }
+ }
+ ushort16* pix = (ushort16*)getDataUncropped(pos_x,pos_y);
+ if (div) {
+ pix[0] = total / div;
+ }
+ }
+#endif
+
+}
+
+void RawImageData::startWorker(const RawImageWorker::RawImageWorkerTask task,
+ bool cropped) {
+ const int height = [&]() {
+ int h = (cropped) ? dim.y : uncropped_dim.y;
+ if (task & RawImageWorker::FULL_IMAGE) {
+ h = uncropped_dim.y;
+ }
+ return h;
+ }();
+
+ const int threads = rawspeed_get_number_of_processor_cores();
+ const int y_per_thread = (height + threads - 1) / threads;
+
+#ifdef HAVE_OPENMP
+#pragma omp parallel for default(none) \
+ OMPFIRSTPRIVATECLAUSE(threads, y_per_thread, height, task) \
+ num_threads(threads) schedule(static)
+#endif
+ for (int i = 0; i < threads; i++) {
+ int y_offset = std::min(i * y_per_thread, height);
+ int y_end = std::min((i + 1) * y_per_thread, height);
+
+ RawImageWorker worker(this, task, y_offset, y_end);
+ }
+}
+
+void RawImageData::fixBadPixelsThread(int start_y, int end_y) {
+ int gw = (uncropped_dim.x + 15) / 32;
+
+ for (int y = start_y; y < end_y; y++) {
+ auto* bad_map =
+ reinterpret_cast<const uint32*>(&mBadPixelMap[y * mBadPixelMapPitch]);
+ for (int x = 0; x < gw; x++) {
+ // Test if there is a bad pixel within these 32 pixels
+ if (bad_map[x] == 0)
+ continue;
+ auto* bad = reinterpret_cast<const uchar8*>(&bad_map[x]);
+ // Go through each pixel
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 8; j++) {
+ if (1 != ((bad[i] >> j) & 1))
+ continue;
+
+ fixBadPixel(x * 32 + i * 8 + j, y, 0);
+ }
+ }
+ }
+ }
+}
+
+void RawImageData::blitFrom(const RawImage& src, const iPoint2D& srcPos,
+ const iPoint2D& size, const iPoint2D& destPos) {
+ iRectangle2D src_rect(srcPos, size);
+ iRectangle2D dest_rect(destPos, size);
+ src_rect = src_rect.getOverlap(iRectangle2D(iPoint2D(0,0), src->dim));
+ dest_rect = dest_rect.getOverlap(iRectangle2D(iPoint2D(0,0), dim));
+
+ iPoint2D blitsize = src_rect.dim.getSmallest(dest_rect.dim);
+ if (blitsize.area() <= 0)
+ return;
+
+ // TODO: Move offsets after crop.
+ copyPixels(getData(dest_rect.pos.x, dest_rect.pos.y), pitch,
+ src->getData(src_rect.pos.x, src_rect.pos.y), src->pitch,
+ blitsize.x * bpp, blitsize.y);
+}
+
+/* Does not take cfa into consideration */
+void RawImageData::expandBorder(iRectangle2D validData)
+{
+ validData = validData.getOverlap(iRectangle2D(0,0,dim.x, dim.y));
+ if (validData.pos.x > 0) {
+ for (int y = 0; y < dim.y; y++ ) {
+ uchar8* src_pos = getData(validData.pos.x, y);
+ uchar8* dst_pos = getData(validData.pos.x-1, y);
+ for (int x = validData.pos.x; x >= 0; x--) {
+ for (uint32 i = 0; i < bpp; i++) {
+ dst_pos[i] = src_pos[i];
+ }
+ dst_pos -= bpp;
+ }
+ }
+ }
+
+ if (validData.getRight() < dim.x) {
+ int pos = validData.getRight();
+ for (int y = 0; y < dim.y; y++ ) {
+ uchar8* src_pos = getData(pos-1, y);
+ uchar8* dst_pos = getData(pos, y);
+ for (int x = pos; x < dim.x; x++) {
+ for (uint32 i = 0; i < bpp; i++) {
+ dst_pos[i] = src_pos[i];
+ }
+ dst_pos += bpp;
+ }
+ }
+ }
+
+ if (validData.pos.y > 0) {
+ uchar8* src_pos = getData(0, validData.pos.y);
+ for (int y = 0; y < validData.pos.y; y++ ) {
+ uchar8* dst_pos = getData(0, y);
+ memcpy(dst_pos, src_pos, static_cast<size_t>(dim.x) * bpp);
+ }
+ }
+ if (validData.getBottom() < dim.y) {
+ uchar8* src_pos = getData(0, validData.getBottom()-1);
+ for (int y = validData.getBottom(); y < dim.y; y++ ) {
+ uchar8* dst_pos = getData(0, y);
+ memcpy(dst_pos, src_pos, static_cast<size_t>(dim.x) * bpp);
+ }
+ }
+}
+
+void RawImageData::clearArea( iRectangle2D area, uchar8 val /*= 0*/ )
+{
+ area = area.getOverlap(iRectangle2D(iPoint2D(0,0), dim));
+
+ if (area.area() <= 0)
+ return;
+
+ for (int y = area.getTop(); y < area.getBottom(); y++)
+ memset(getData(area.getLeft(), y), val,
+ static_cast<size_t>(area.getWidth()) * bpp);
+}
+
+RawImage& RawImage::operator=(RawImage&& rhs) noexcept {
+ if (this == &rhs)
+ return *this;
+
+ std::swap(p_, rhs.p_);
+
+ return *this;
+}
+
+RawImage& RawImage::operator=(const RawImage& rhs) noexcept {
+ if (this == &rhs)
+ return *this;
+
+ RawImage tmp(rhs);
+ *this = std::move(tmp);
+
+ return *this;
+}
+
+RawImageWorker::RawImageWorker(RawImageData* _img, RawImageWorkerTask _task,
+ int _start_y, int _end_y) noexcept
+ : data(_img), task(_task), start_y(_start_y), end_y(_end_y) {
+ performTask();
+}
+
+void RawImageWorker::performTask() noexcept {
+ try {
+ switch(task)
+ {
+ case SCALE_VALUES:
+ data->scaleValues(start_y, end_y);
+ break;
+ case FIX_BAD_PIXELS:
+ data->fixBadPixelsThread(start_y, end_y);
+ break;
+ case APPLY_LOOKUP:
+ data->doLookup(start_y, end_y);
+ break;
+ default:
+ assert(false);
+ }
+ } catch (RawDecoderException &e) {
+ data->setError(e.what());
+ } catch (TiffParserException &e) {
+ data->setError(e.what());
+ } catch (IOException &e) {
+ data->setError(e.what());
+ }
+}
+
+void RawImageData::sixteenBitLookup() {
+ if (table == nullptr) {
+ return;
+ }
+ startWorker(RawImageWorker::APPLY_LOOKUP, true);
+}
+
+void RawImageData::setTable(std::unique_ptr<TableLookUp> t) {
+ table = std::move(t);
+}
+
+void RawImageData::setTable(const std::vector<ushort16>& table_, bool dither) {
+ assert(!table_.empty());
+
+ auto t = std::make_unique<TableLookUp>(1, dither);
+ t->setTable(0, table_);
+ this->setTable(std::move(t));
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/RawImage.h
b/subprojects/rawspeed/src/librawspeed/common/RawImage.h
new file mode 100644
index 00000000..dc24f83c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/RawImage.h
@@ -0,0 +1,307 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h"
+#include "ThreadSafetyAnalysis.h" // for GUARDED_BY, REQUIRES
+#include "common/Common.h" // for uint32, uchar8, ushort16, wri...
+#include "common/ErrorLog.h" // for ErrorLog
+#include "common/Mutex.h" // for Mutex
+#include "common/Point.h" // for iPoint2D, iRectangle2D (ptr o...
+#include "common/TableLookUp.h" // for TableLookUp
+#include "metadata/BlackArea.h" // for BlackArea
+#include "metadata/ColorFilterArray.h" // for ColorFilterArray
+#include <array> // for array
+#include <memory> // for unique_ptr, operator==
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+class RawImageData;
+
+enum RawImageType { TYPE_USHORT16, TYPE_FLOAT32 };
+
+class RawImageWorker {
+public:
+ enum RawImageWorkerTask {
+ SCALE_VALUES = 1, FIX_BAD_PIXELS = 2, APPLY_LOOKUP = 3 | 0x1000, FULL_IMAGE = 0x1000
+ };
+
+private:
+ RawImageData* data;
+ RawImageWorkerTask task;
+ int start_y;
+ int end_y;
+
+ void performTask() noexcept;
+
+public:
+ RawImageWorker(RawImageData* img, RawImageWorkerTask task, int start_y,
+ int end_y) noexcept;
+};
+
+class ImageMetaData {
+public:
+ ImageMetaData();
+
+ // Aspect ratio of the pixels, usually 1 but some cameras need scaling
+ // <1 means the image needs to be stretched vertically, (0.5 means 2x)
+ // >1 means the image needs to be stretched horizontally (2 mean 2x)
+ double pixelAspectRatio;
+
+ // White balance coefficients of the image
+ std::array<float, 4> wbCoeffs;
+
+ // How many pixels far down the left edge and far up the right edge the image
+ // corners are when the image is rotated 45 degrees in Fuji rotated sensors.
+ uint32 fujiRotationPos;
+
+ iPoint2D subsampling;
+ std::string make;
+ std::string model;
+ std::string mode;
+
+ std::string canonical_make;
+ std::string canonical_model;
+ std::string canonical_alias;
+ std::string canonical_id;
+
+ // ISO speed. If known the value is set, otherwise it will be '0'.
+ int isoSpeed;
+};
+
+class RawImageData : public ErrorLog {
+ friend class RawImageWorker;
+public:
+ virtual ~RawImageData();
+ uint32 getCpp() const { return cpp; }
+ uint32 getBpp() const { return bpp; }
+ void setCpp(uint32 val);
+ void createData();
+ void poisonPadding();
+ void unpoisonPadding();
+ void checkRowIsInitialized(int row);
+ void checkMemIsInitialized();
+ void destroyData();
+ void blitFrom(const RawImage& src, const iPoint2D& srcPos,
+ const iPoint2D& size, const iPoint2D& destPos);
+ rawspeed::RawImageType getDataType() const { return dataType; }
+ uchar8* getData() const;
+ uchar8* getData(uint32 x, uint32 y); // Not super fast, but safe. Don't use per pixel.
+ uchar8* getDataUncropped(uint32 x, uint32 y);
+ void subFrame(iRectangle2D cropped);
+ void clearArea(iRectangle2D area, uchar8 value = 0);
+ iPoint2D __attribute__((pure)) getUncroppedDim() const;
+ iPoint2D __attribute__((pure)) getCropOffset() const;
+ virtual void scaleBlackWhite() = 0;
+ virtual void calculateBlackAreas() = 0;
+ virtual void setWithLookUp(ushort16 value, uchar8* dst, uint32* random) = 0;
+ void sixteenBitLookup();
+ void transferBadPixelsToMap() REQUIRES(!mBadPixelMutex);
+ void fixBadPixels() REQUIRES(!mBadPixelMutex);
+ void expandBorder(iRectangle2D validData);
+ void setTable(const std::vector<ushort16>& table_, bool dither);
+ void setTable(std::unique_ptr<TableLookUp> t);
+
+ bool isAllocated() {return !!data;}
+ void createBadPixelMap();
+ iPoint2D dim;
+ uint32 pitch = 0;
+
+ // padding is the size of the area after last pixel of line n
+ // and before the first pixel of line n+1
+ uint32 padding = 0;
+
+ bool isCFA{true};
+ ColorFilterArray cfa;
+ int blackLevel = -1;
+ std::array<int, 4> blackLevelSeparate;
+ int whitePoint = 65536;
+ std::vector<BlackArea> blackAreas;
+
+ /* Vector containing the positions of bad pixels */
+ /* Format is x | (y << 16), so maximum pixel position is 65535 */
+ // Positions of zeroes that must be interpolated
+ std::vector<uint32> mBadPixelPositions GUARDED_BY(mBadPixelMutex);
+ uchar8* mBadPixelMap = nullptr;
+ uint32 mBadPixelMapPitch = 0;
+ bool mDitherScale =
+ true; // Should upscaling be done with dither to minimize banding?
+ ImageMetaData metadata;
+
+ Mutex mBadPixelMutex; // Mutex for 'mBadPixelPositions, must be used if more
+ // than 1 thread is accessing vector
+
+private:
+ uint32 dataRefCount GUARDED_BY(mymutex) = 0;
+
+protected:
+ RawImageType dataType;
+ RawImageData();
+ RawImageData(const iPoint2D &dim, uint32 bpp, uint32 cpp = 1);
+ virtual void scaleValues(int start_y, int end_y) = 0;
+ virtual void doLookup(int start_y, int end_y) = 0;
+ virtual void fixBadPixel( uint32 x, uint32 y, int component = 0) = 0;
+ void fixBadPixelsThread(int start_y, int end_y);
+ void startWorker(RawImageWorker::RawImageWorkerTask task, bool cropped );
+ uchar8* data = nullptr;
+ uint32 cpp = 1; // Components per pixel
+ uint32 bpp = 0; // Bytes per pixel.
+ friend class RawImage;
+ iPoint2D mOffset;
+ iPoint2D uncropped_dim;
+ std::unique_ptr<TableLookUp> table;
+ Mutex mymutex;
+};
+
+class RawImageDataU16 final : public RawImageData {
+public:
+ void scaleBlackWhite() override;
+ void calculateBlackAreas() override;
+ void setWithLookUp(ushort16 value, uchar8* dst, uint32* random) override;
+
+protected:
+ void scaleValues_plain(int start_y, int end_y);
+#ifdef WITH_SSE2
+ void scaleValues_SSE2(int start_y, int end_y);
+#endif
+ void scaleValues(int start_y, int end_y) override;
+ void fixBadPixel(uint32 x, uint32 y, int component = 0) override;
+ void doLookup(int start_y, int end_y) override;
+
+ RawImageDataU16();
+ explicit RawImageDataU16(const iPoint2D& dim_, uint32 cpp_ = 1);
+ friend class RawImage;
+};
+
+class RawImageDataFloat final : public RawImageData {
+public:
+ void scaleBlackWhite() override;
+ void calculateBlackAreas() override;
+ void setWithLookUp(ushort16 value, uchar8 *dst, uint32 *random) override;
+
+protected:
+ void scaleValues(int start_y, int end_y) override;
+ void fixBadPixel(uint32 x, uint32 y, int component = 0) override;
+ [[noreturn]] void doLookup(int start_y, int end_y) override;
+ RawImageDataFloat();
+ explicit RawImageDataFloat(const iPoint2D& dim_, uint32 cpp_ = 1);
+ friend class RawImage;
+};
+
+ class RawImage {
+ public:
+ static RawImage create(RawImageType type = TYPE_USHORT16);
+ static RawImage create(const iPoint2D &dim,
+ RawImageType type = TYPE_USHORT16,
+ uint32 componentsPerPixel = 1);
+ RawImageData* operator->() const { return p_; }
+ RawImageData& operator*() const { return *p_; }
+ explicit RawImage(RawImageData* p); // p must not be NULL
+ ~RawImage();
+ RawImage(const RawImage& p);
+ RawImage& operator=(const RawImage& p) noexcept;
+ RawImage& operator=(RawImage&& p) noexcept;
+
+ RawImageData* get() { return p_; }
+ private:
+ RawImageData* p_; // p_ is never NULL
+ };
+
+inline RawImage RawImage::create(RawImageType type) {
+ switch (type)
+ {
+ case TYPE_USHORT16:
+ return RawImage(new RawImageDataU16());
+ case TYPE_FLOAT32:
+ return RawImage(new RawImageDataFloat());
+ default:
+ writeLog(DEBUG_PRIO_ERROR, "RawImage::create: Unknown Image type!");
+ __builtin_unreachable();
+
+ }
+}
+
+inline RawImage RawImage::create(const iPoint2D& dim, RawImageType type, uint32 componentsPerPixel) {
+ switch (type) {
+ case TYPE_USHORT16:
+ return RawImage(new RawImageDataU16(dim, componentsPerPixel));
+ case TYPE_FLOAT32:
+ return RawImage(new RawImageDataFloat(dim, componentsPerPixel));
+ default:
+ writeLog(DEBUG_PRIO_ERROR, "RawImage::create: Unknown Image type!");
+ __builtin_unreachable();
+ }
+}
+
+// setWithLookUp will set a single pixel by using the lookup table if supplied,
+// You must supply the destination where the value should be written, and a pointer to
+// a value that will be used to store a random counter that can be reused between calls.
+// this needs to be inline to speed up tight decompressor loops
+inline void RawImageDataU16::setWithLookUp(ushort16 value, uchar8* dst, uint32* random) {
+ auto* dest = reinterpret_cast<ushort16*>(dst);
+ if (table == nullptr) {
+ *dest = value;
+ return;
+ }
+ if (table->dither) {
+ auto* t = reinterpret_cast<const uint32*>(table->tables.data());
+ uint32 lookup = t[value];
+ uint32 base = lookup & 0xffff;
+ uint32 delta = lookup >> 16;
+ uint32 r = *random;
+
+ uint32 pix = base + ((delta * (r&2047) + 1024) >> 12);
+ *random = 15700 *(r & 65535) + (r >> 16);
+ *dest = pix;
+ return;
+ }
+ *dest = table->tables[value];
+}
+
+class RawImageCurveGuard final {
+ RawImage* mRaw;
+ const std::vector<ushort16>& curve;
+ const bool uncorrectedRawValues;
+
+public:
+ RawImageCurveGuard(RawImage* raw, const std::vector<ushort16>& curve_,
+ bool uncorrectedRawValues_)
+ : mRaw(raw), curve(curve_), uncorrectedRawValues(uncorrectedRawValues_) {
+ if (uncorrectedRawValues)
+ return;
+
+ (*mRaw)->setTable(curve, true);
+ }
+
+ ~RawImageCurveGuard() {
+ // Set the table, if it should be needed later.
+ if (uncorrectedRawValues)
+ (*mRaw)->setTable(curve, false);
+ else
+ (*mRaw)->setTable(nullptr);
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/RawImageDataFloat.cpp
b/subprojects/rawspeed/src/librawspeed/common/RawImageDataFloat.cpp
new file mode 100644
index 00000000..06423a20
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/RawImageDataFloat.cpp
@@ -0,0 +1,393 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "common/RawImage.h" // for RawImageDataFloat, TYPE_FL...
+#include "common/Common.h" // for uchar8, uint32, writeLog
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "metadata/BlackArea.h" // for BlackArea
+#include <algorithm> // for max, min
+#include <array> // for array
+#include <memory> // for operator==, unique_ptr
+#include <vector> // for vector
+
+using std::min;
+using std::max;
+
+namespace rawspeed {
+
+RawImageDataFloat::RawImageDataFloat() {
+ bpp = 4;
+ dataType = TYPE_FLOAT32;
+ }
+
+ RawImageDataFloat::RawImageDataFloat(const iPoint2D &_dim, uint32 _cpp)
+ : RawImageData(_dim, 4, _cpp) {
+ dataType = TYPE_FLOAT32;
+ }
+
+
+ void RawImageDataFloat::calculateBlackAreas() {
+ std::array<float, 4> accPixels;
+ accPixels.fill(0);
+ int totalpixels = 0;
+
+ for (auto area : blackAreas) {
+ /* Make sure area sizes are multiple of two,
+ so we have the same amount of pixels for each CFA group */
+ area.size = area.size - (area.size&1);
+
+ /* Process horizontal area */
+ if (!area.isVertical) {
+ if (static_cast<int>(area.offset) + static_cast<int>(area.size) >
+ uncropped_dim.y)
+ ThrowRDE("Offset + size is larger than height of image");
+ for (uint32 y = area.offset; y < area.offset+area.size; y++) {
+ auto* pixel =
+ reinterpret_cast<float*>(getDataUncropped(mOffset.x, y));
+
+ for (int x = mOffset.x; x < dim.x + mOffset.x; x++) {
+ accPixels[((y & 1) << 1) | (x & 1)] += *pixel;
+ pixel++;
+ }
+ }
+ totalpixels += area.size * dim.x;
+ }
+
+ /* Process vertical area */
+ if (area.isVertical) {
+ if (static_cast<int>(area.offset) + static_cast<int>(area.size) >
+ uncropped_dim.x)
+ ThrowRDE("Offset + size is larger than width of image");
+ for (int y = mOffset.y; y < dim.y+mOffset.y; y++) {
+ auto* pixel =
+ reinterpret_cast<float*>(getDataUncropped(area.offset, y));
+
+ for (uint32 x = area.offset; x < area.size + area.offset; x++) {
+ accPixels[((y & 1) << 1) | (x & 1)] += *pixel;
+ pixel++;
+ }
+ }
+ totalpixels += area.size * dim.y;
+ }
+ }
+
+ if (!totalpixels) {
+ for (int &i : blackLevelSeparate)
+ i = blackLevel;
+ return;
+ }
+
+ /* Calculate median value of black areas for each component */
+ /* Adjust the number of total pixels so it is the same as the median of each histogram */
+ totalpixels /= 4;
+
+ for (int i = 0 ; i < 4; i++) {
+ blackLevelSeparate[i] =
+ static_cast<int>(65535.0F * accPixels[i] / totalpixels);
+ }
+
+ /* If this is not a CFA image, we do not use separate blacklevels, use average */
+ if (!isCFA) {
+ int total = 0;
+ for (int i : blackLevelSeparate)
+ total += i;
+ for (int &i : blackLevelSeparate)
+ i = (total + 2) >> 2;
+ }
+ }
+
+ void RawImageDataFloat::scaleBlackWhite() {
+ const int skipBorder = 150;
+ int gw = (dim.x - skipBorder) * cpp;
+ if ((blackAreas.empty() && blackLevelSeparate[0] < 0 && blackLevel < 0) || whitePoint == 65536) { //
Estimate
+ float b = 100000000;
+ float m = -10000000;
+ for (int row = skipBorder*cpp;row < (dim.y - skipBorder);row++) {
+ auto* pixel = reinterpret_cast<float*>(getData(skipBorder, row));
+ for (int col = skipBorder ; col < gw ; col++) {
+ b = min(*pixel, b);
+ m = max(*pixel, m);
+ pixel++;
+ }
+ }
+ if (blackLevel < 0)
+ blackLevel = static_cast<int>(b);
+ if (whitePoint == 65536)
+ whitePoint = static_cast<int>(m);
+ writeLog(DEBUG_PRIO_INFO, "Estimated black:%d, Estimated white: %d",
+ blackLevel, whitePoint);
+ }
+
+ /* If filter has not set separate blacklevel, compute or fetch it */
+ if (blackLevelSeparate[0] < 0)
+ calculateBlackAreas();
+
+ startWorker(RawImageWorker::SCALE_VALUES, true);
+}
+
+#if 0 // def WITH_SSE2
+
+ void RawImageDataFloat::scaleValues(int start_y, int end_y) {
+ bool WITH_SSE2;
+#ifdef _MSC_VER
+ int info[4];
+ __cpuid(info, 1);
+ WITH_SSE2 = !!(info[3]&(1 << 26));
+#else
+ WITH_SSE2 = true;
+#endif
+
+ float app_scale = 65535.0F / (whitePoint - blackLevelSeparate[0]);
+ // Check SSE2
+ if (WITH_SSE2 && app_scale < 63) {
+
+ __m128i sseround;
+ __m128i ssesub2;
+ __m128i ssesign;
+ auto* sub_mul = alignedMallocArray<uint32, 16, __m128i>(4);
+ if (!sub_mul)
+ ThrowRDE("Out of memory, failed to allocate 128 bytes");
+
+ uint32 gw = pitch / 16;
+ // 10 bit fraction
+ uint32 mul = (int)(1024.0F * 65535.0F / (float)(whitePoint - blackLevelSeparate[mOffset.x&1]));
+ mul |= ((int)(1024.0F * 65535.0F / (float)(whitePoint - blackLevelSeparate[(mOffset.x+1)&1])))<<16;
+ uint32 b = blackLevelSeparate[mOffset.x&1] | (blackLevelSeparate[(mOffset.x+1)&1]<<16);
+
+ for (int i = 0; i< 4; i++) {
+ sub_mul[i] = b; // Subtract even lines
+ sub_mul[4+i] = mul; // Multiply even lines
+ }
+
+ mul = (int)(1024.0F * 65535.0F / (float)(whitePoint - blackLevelSeparate[2+(mOffset.x&1)]));
+ mul |= ((int)(1024.0F * 65535.0F / (float)(whitePoint - blackLevelSeparate[2+((mOffset.x+1)&1)])))<<16;
+ b = blackLevelSeparate[2+(mOffset.x&1)] | (blackLevelSeparate[2+((mOffset.x+1)&1)]<<16);
+
+ for (int i = 0; i< 4; i++) {
+ sub_mul[8+i] = b; // Subtract odd lines
+ sub_mul[12+i] = mul; // Multiply odd lines
+ }
+
+ sseround = _mm_set_epi32(512, 512, 512, 512);
+ ssesub2 = _mm_set_epi32(32768, 32768, 32768, 32768);
+ ssesign = _mm_set_epi32(0x80008000, 0x80008000, 0x80008000, 0x80008000);
+
+ for (int y = start_y; y < end_y; y++) {
+ __m128i* pixel = (__m128i*) & data[(mOffset.y+y)*pitch];
+ __m128i ssescale, ssesub;
+ if (((y+mOffset.y)&1) == 0) {
+ ssesub = _mm_load_si128((__m128i*)&sub_mul[0]);
+ ssescale = _mm_load_si128((__m128i*)&sub_mul[4]);
+ } else {
+ ssesub = _mm_load_si128((__m128i*)&sub_mul[8]);
+ ssescale = _mm_load_si128((__m128i*)&sub_mul[12]);
+ }
+
+ for (uint32 x = 0 ; x < gw; x++) {
+ __m128i pix_high;
+ __m128i temp;
+ _mm_prefetch((char*)(pixel+1), _MM_HINT_T0);
+ __m128i pix_low = _mm_load_si128(pixel);
+ // Subtract black
+ pix_low = _mm_subs_epu16(pix_low, ssesub);
+ // Multiply the two unsigned shorts and combine it to 32 bit result
+ pix_high = _mm_mulhi_epu16(pix_low, ssescale);
+ temp = _mm_mullo_epi16(pix_low, ssescale);
+ pix_low = _mm_unpacklo_epi16(temp, pix_high);
+ pix_high = _mm_unpackhi_epi16(temp, pix_high);
+ // Add rounder
+ pix_low = _mm_add_epi32(pix_low, sseround);
+ pix_high = _mm_add_epi32(pix_high, sseround);
+ // Shift down
+ pix_low = _mm_srai_epi32(pix_low, 10);
+ pix_high = _mm_srai_epi32(pix_high, 10);
+ // Subtract to avoid clipping
+ pix_low = _mm_sub_epi32(pix_low, ssesub2);
+ pix_high = _mm_sub_epi32(pix_high, ssesub2);
+ // Pack
+ pix_low = _mm_packs_epi32(pix_low, pix_high);
+ // Shift sign off
+ pix_low = _mm_xor_si128(pix_low, ssesign);
+ _mm_store_si128(pixel, pix_low);
+ pixel++;
+ }
+ }
+ alignedFree(sub_mul);
+ } else {
+ // Not SSE2
+ int gw = dim.x * cpp;
+ int mul[4];
+ int sub[4];
+ for (int i = 0; i < 4; i++) {
+ int v = i;
+ if ((mOffset.x&1) != 0)
+ v ^= 1;
+ if ((mOffset.y&1) != 0)
+ v ^= 2;
+ mul[i] = (int)(16384.0F * 65535.0F / (float)(whitePoint - blackLevelSeparate[v]));
+ sub[i] = blackLevelSeparate[v];
+ }
+ for (int y = start_y; y < end_y; y++) {
+ ushort16 *pixel = (ushort16*)getData(0, y);
+ int *mul_local = &mul[2*(y&1)];
+ int *sub_local = &sub[2*(y&1)];
+ for (int x = 0 ; x < gw; x++) {
+ pixel[x] = clampBits(((pixel[x] - sub_local[x&1]) * mul_local[x&1] + 8192) >> 14, 16);
+ }
+ }
+ }
+ }
+
+#else
+
+ void RawImageDataFloat::scaleValues(int start_y, int end_y) {
+ int gw = dim.x * cpp;
+ std::array<float, 4> mul;
+ std::array<float, 4> sub;
+ for (int i = 0; i < 4; i++) {
+ int v = i;
+ if ((mOffset.x&1) != 0)
+ v ^= 1;
+ if ((mOffset.y&1) != 0)
+ v ^= 2;
+ mul[i] =
+ 65535.0F / static_cast<float>(whitePoint - blackLevelSeparate[v]);
+ sub[i] = static_cast<float>(blackLevelSeparate[v]);
+ }
+ for (int y = start_y; y < end_y; y++) {
+ auto* pixel = reinterpret_cast<float*>(getData(0, y));
+ float *mul_local = &mul[2*(y&1)];
+ float *sub_local = &sub[2*(y&1)];
+ for (int x = 0 ; x < gw; x++) {
+ pixel[x] = (pixel[x] - sub_local[x&1]) * mul_local[x&1];
+ }
+ }
+ }
+
+#endif
+
+ /* This performs a 4 way interpolated pixel */
+ /* The value is interpolated from the 4 closest valid pixels in */
+ /* the horizontal and vertical direction. Pixels found further away */
+ /* are weighed less */
+
+void RawImageDataFloat::fixBadPixel( uint32 x, uint32 y, int component )
+{
+ std::array<float, 4> values;
+ values.fill(-1);
+ std::array<float, 4> dist = {{}};
+ std::array<float, 4> weight;
+
+ uchar8* bad_line = &mBadPixelMap[y*mBadPixelMapPitch];
+
+ // Find pixel to the left
+ int x_find = static_cast<int>(x) - 2;
+ int curr = 0;
+ while (x_find >= 0 && values[curr] < 0) {
+ if (0 == ((bad_line[x_find>>3] >> (x_find&7)) & 1)) {
+ values[curr] = (reinterpret_cast<float*>(getData(x_find, y)))[component];
+ dist[curr] = static_cast<float>(static_cast<int>(x) - x_find);
+ }
+ x_find-=2;
+ }
+ // Find pixel to the right
+ x_find = static_cast<int>(x) + 2;
+ curr = 1;
+ while (x_find < uncropped_dim.x && values[curr] < 0) {
+ if (0 == ((bad_line[x_find>>3] >> (x_find&7)) & 1)) {
+ values[curr] = (reinterpret_cast<float*>(getData(x_find, y)))[component];
+ dist[curr] = static_cast<float>(x_find - static_cast<int>(x));
+ }
+ x_find+=2;
+ }
+
+ bad_line = &mBadPixelMap[x>>3];
+ // Find pixel upwards
+ int y_find = static_cast<int>(y) - 2;
+ curr = 2;
+ while (y_find >= 0 && values[curr] < 0) {
+ if (0 == ((bad_line[y_find*mBadPixelMapPitch] >> (x&7)) & 1)) {
+ values[curr] = (reinterpret_cast<float*>(getData(x, y_find)))[component];
+ dist[curr] = static_cast<float>(static_cast<int>(y) - y_find);
+ }
+ y_find-=2;
+ }
+ // Find pixel downwards
+ y_find = static_cast<int>(y) + 2;
+ curr = 3;
+ while (y_find < uncropped_dim.y && values[curr] < 0) {
+ if (0 == ((bad_line[y_find*mBadPixelMapPitch] >> (x&7)) & 1)) {
+ values[curr] = (reinterpret_cast<float*>(getData(x, y_find)))[component];
+ dist[curr] = static_cast<float>(y_find - static_cast<int>(y));
+ }
+ y_find+=2;
+ }
+ // Find x weights
+ float total_dist_x = dist[0] + dist[1];
+
+ float total_div = 0.000001F;
+ if (total_dist_x) {
+ weight[0] = dist[0] > 0.0F ? (total_dist_x - dist[0]) / total_dist_x : 0;
+ weight[1] = 1.0F - weight[0];
+ total_div += 1;
+ }
+
+ // Find y weights
+ float total_dist_y = dist[2] + dist[3];
+ if (total_dist_y) {
+ weight[2] = dist[2] > 0.0F ? (total_dist_y - dist[2]) / total_dist_y : 0;
+ weight[3] = 1.0F - weight[2];
+ total_div += 1;
+ }
+
+
+ float total_pixel = 0;
+ for (int i = 0; i < 4; i++)
+ if (values[i] >= 0)
+ total_pixel += values[i] * dist[i];
+
+ total_pixel /= total_div;
+ auto* pix = reinterpret_cast<float*>(getDataUncropped(x, y));
+ pix[component] = total_pixel;
+
+ /* Process other pixels - could be done inline, since we have the weights */
+ if (cpp > 1 && component == 0)
+ for (int i = 1; i < static_cast<int>(cpp); i++)
+ fixBadPixel(x,y,i);
+
+}
+
+
+void RawImageDataFloat::doLookup( int start_y, int end_y ) {
+ ThrowRDE("Float point lookup tables not implemented");
+}
+
+void RawImageDataFloat::setWithLookUp(ushort16 value, uchar8* dst, uint32* random) {
+ auto* dest = reinterpret_cast<float*>(dst);
+ if (table == nullptr) {
+ *dest = static_cast<float>(value) * (1.0F / 65535);
+ return;
+ }
+
+ ThrowRDE("Float point lookup tables not implemented");
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/RawImageDataU16.cpp
b/subprojects/rawspeed/src/librawspeed/common/RawImageDataU16.cpp
new file mode 100644
index 00000000..d4755130
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/RawImageDataU16.cpp
@@ -0,0 +1,512 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h" // for WITH_SSE2
+#include "common/RawImage.h" // for RawImageDataU16, TableLookUp
+#include "common/Common.h" // for ushort16, uint32, uchar8
+#include "common/Memory.h" // for alignedFree, alignedMalloc...
+#include "common/Point.h" // for iPoint2D
+#include "common/TableLookUp.h" // for TableLookUp
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "metadata/BlackArea.h" // for BlackArea
+#include <algorithm> // for fill, max, min
+#include <array> // for array
+#include <cassert> // for assert
+#include <memory> // for operator==, unique_ptr
+#include <vector> // for vector
+
+#ifdef WITH_SSE2
+#include "common/Cpuid.h" // for Cpuid
+#include <emmintrin.h> // for __m128i, _mm_load_si128
+#include <xmmintrin.h> // for _MM_HINT_T0, _mm_prefetch
+#endif
+
+using std::vector;
+using std::min;
+using std::max;
+using std::array;
+
+namespace rawspeed {
+
+RawImageDataU16::RawImageDataU16() {
+ dataType = TYPE_USHORT16;
+ bpp = 2;
+}
+
+RawImageDataU16::RawImageDataU16(const iPoint2D &_dim, uint32 _cpp)
+ : RawImageData(_dim, 2, _cpp) {
+ dataType = TYPE_USHORT16;
+}
+
+
+void RawImageDataU16::calculateBlackAreas() {
+ vector<unsigned int> histogram(4 * 65536);
+ fill(histogram.begin(), histogram.end(), 0);
+
+ int totalpixels = 0;
+
+ for (auto area : blackAreas) {
+ /* Make sure area sizes are multiple of two,
+ so we have the same amount of pixels for each CFA group */
+ area.size = area.size - (area.size&1);
+
+ /* Process horizontal area */
+ if (!area.isVertical) {
+ if (static_cast<int>(area.offset) + static_cast<int>(area.size) >
+ uncropped_dim.y)
+ ThrowRDE("Offset + size is larger than height of image");
+ for (uint32 y = area.offset; y < area.offset+area.size; y++) {
+ auto* pixel =
+ reinterpret_cast<ushort16*>(getDataUncropped(mOffset.x, y));
+ auto* localhist = &histogram[(y & 1) * (65536UL * 2UL)];
+ for (int x = mOffset.x; x < dim.x+mOffset.x; x++) {
+ const auto hBin = ((x & 1) << 16) + *pixel;
+ localhist[hBin]++;
+ }
+ }
+ totalpixels += area.size * dim.x;
+ }
+
+ /* Process vertical area */
+ if (area.isVertical) {
+ if (static_cast<int>(area.offset) + static_cast<int>(area.size) >
+ uncropped_dim.x)
+ ThrowRDE("Offset + size is larger than width of image");
+ for (int y = mOffset.y; y < dim.y+mOffset.y; y++) {
+ auto* pixel =
+ reinterpret_cast<ushort16*>(getDataUncropped(area.offset, y));
+ auto* localhist = &histogram[(y & 1) * (65536UL * 2UL)];
+ for (uint32 x = area.offset; x < area.size+area.offset; x++) {
+ const auto hBin = ((x & 1) << 16) + *pixel;
+ localhist[hBin]++;
+ }
+ }
+ totalpixels += area.size * dim.y;
+ }
+ }
+
+ if (!totalpixels) {
+ for (int &i : blackLevelSeparate)
+ i = blackLevel;
+ return;
+ }
+
+ /* Calculate median value of black areas for each component */
+ /* Adjust the number of total pixels so it is the same as the median of each histogram */
+ totalpixels /= 4*2;
+
+ for (int i = 0 ; i < 4; i++) {
+ auto* localhist = &histogram[i * 65536UL];
+ int acc_pixels = localhist[0];
+ int pixel_value = 0;
+ while (acc_pixels <= totalpixels && pixel_value < 65535) {
+ pixel_value++;
+ acc_pixels += localhist[pixel_value];
+ }
+ blackLevelSeparate[i] = pixel_value;
+ }
+
+ /* If this is not a CFA image, we do not use separate blacklevels, use average */
+ if (!isCFA) {
+ int total = 0;
+ for (int i : blackLevelSeparate)
+ total += i;
+ for (int &i : blackLevelSeparate)
+ i = (total + 2) >> 2;
+ }
+}
+
+void RawImageDataU16::scaleBlackWhite() {
+ const int skipBorder = 250;
+ int gw = (dim.x - skipBorder) * cpp;
+ if ((blackAreas.empty() && blackLevelSeparate[0] < 0 && blackLevel < 0) || whitePoint >= 65536) { //
Estimate
+ int b = 65536;
+ int m = 0;
+ for (int row = skipBorder; row < (dim.y - skipBorder);row++) {
+ auto* pixel = reinterpret_cast<ushort16*>(getData(skipBorder, row));
+ for (int col = skipBorder ; col < gw ; col++) {
+ b = min(static_cast<int>(*pixel), b);
+ m = max(static_cast<int>(*pixel), m);
+ pixel++;
+ }
+ }
+ if (blackLevel < 0)
+ blackLevel = b;
+ if (whitePoint >= 65536)
+ whitePoint = m;
+ writeLog(DEBUG_PRIO_INFO, "ISO:%d, Estimated black:%d, Estimated white: %d",
+ metadata.isoSpeed, blackLevel, whitePoint);
+ }
+
+ /* Skip, if not needed */
+ if ((blackAreas.empty() && blackLevel == 0 && whitePoint == 65535 &&
+ blackLevelSeparate[0] < 0) ||
+ dim.area() <= 0)
+ return;
+
+ /* If filter has not set separate blacklevel, compute or fetch it */
+ if (blackLevelSeparate[0] < 0)
+ calculateBlackAreas();
+
+ startWorker(RawImageWorker::SCALE_VALUES, true);
+}
+
+void RawImageDataU16::scaleValues(int start_y, int end_y) {
+#ifndef WITH_SSE2
+
+ return scaleValues_plain(start_y, end_y);
+
+#else
+
+ int depth_values = whitePoint - blackLevelSeparate[0];
+ float app_scale = 65535.0F / depth_values;
+
+ // Check SSE2
+ if (Cpuid::SSE2() && app_scale < 63) {
+ scaleValues_SSE2(start_y, end_y);
+ } else {
+ scaleValues_plain(start_y, end_y);
+ }
+
+#endif
+}
+
+#ifdef WITH_SSE2
+void RawImageDataU16::scaleValues_SSE2(int start_y, int end_y) {
+ int depth_values = whitePoint - blackLevelSeparate[0];
+ float app_scale = 65535.0F / depth_values;
+
+ // Scale in 30.2 fp
+ auto full_scale_fp = static_cast<int>(app_scale * 4.0F);
+ // Half Scale in 18.14 fp
+ auto half_scale_fp = static_cast<int>(app_scale * 4095.0F);
+
+ __m128i sseround;
+ __m128i ssesub2;
+ __m128i ssesign;
+ __m128i rand_mul;
+ __m128i rand_mask;
+ __m128i sse_full_scale_fp;
+ __m128i sse_half_scale_fp;
+
+ auto* sub_mul = alignedMallocArray<uint32, 16, __m128i>(4);
+ if (!sub_mul)
+ ThrowRDE("Out of memory, failed to allocate 128 bytes");
+
+ assert(sub_mul != nullptr);
+
+ uint32 gw = pitch / 16;
+ // 10 bit fraction
+ uint32 mul = static_cast<int>(
+ 1024.0F * 65535.0F /
+ static_cast<float>(whitePoint - blackLevelSeparate[mOffset.x & 1]));
+ mul |= (static_cast<int>(
+ 1024.0F * 65535.0F /
+ static_cast<float>(whitePoint -
+ blackLevelSeparate[(mOffset.x + 1) & 1])))
+ << 16;
+ uint32 b = blackLevelSeparate[mOffset.x & 1] |
+ (blackLevelSeparate[(mOffset.x + 1) & 1] << 16);
+
+ for (int i = 0; i < 4; i++) {
+ sub_mul[i] = b; // Subtract even lines
+ sub_mul[4 + i] = mul; // Multiply even lines
+ }
+
+ mul = static_cast<int>(
+ 1024.0F * 65535.0F /
+ static_cast<float>(whitePoint - blackLevelSeparate[2 + (mOffset.x & 1)]));
+ mul |= (static_cast<int>(
+ 1024.0F * 65535.0F /
+ static_cast<float>(whitePoint -
+ blackLevelSeparate[2 + ((mOffset.x + 1) & 1)])))
+ << 16;
+ b = blackLevelSeparate[2 + (mOffset.x & 1)] |
+ (blackLevelSeparate[2 + ((mOffset.x + 1) & 1)] << 16);
+
+ for (int i = 0; i < 4; i++) {
+ sub_mul[8 + i] = b; // Subtract odd lines
+ sub_mul[12 + i] = mul; // Multiply odd lines
+ }
+
+ sseround = _mm_set_epi32(512, 512, 512, 512);
+ ssesub2 = _mm_set_epi32(32768, 32768, 32768, 32768);
+ ssesign = _mm_set_epi32(0x80008000, 0x80008000, 0x80008000, 0x80008000);
+ sse_full_scale_fp = _mm_set1_epi32(full_scale_fp | (full_scale_fp << 16));
+ sse_half_scale_fp = _mm_set1_epi32(half_scale_fp >> 4);
+
+ if (mDitherScale) {
+ rand_mul = _mm_set1_epi32(0x4d9f1d32);
+ } else {
+ rand_mul = _mm_set1_epi32(0);
+ }
+ rand_mask = _mm_set1_epi32(0x00ff00ff); // 8 random bits
+
+ for (int y = start_y; y < end_y; y++) {
+ __m128i sserandom;
+ if (mDitherScale) {
+ sserandom =
+ _mm_set_epi32(dim.x * 1676 + y * 18000, dim.x * 2342 + y * 34311,
+ dim.x * 4272 + y * 12123, dim.x * 1234 + y * 23464);
+ } else {
+ sserandom = _mm_setzero_si128();
+ }
+ auto* pixel = reinterpret_cast<__m128i*>(&data[(mOffset.y + y) * pitch]);
+ __m128i ssescale;
+ __m128i ssesub;
+ if (((y + mOffset.y) & 1) == 0) {
+ ssesub = _mm_load_si128(reinterpret_cast<__m128i*>(&sub_mul[0]));
+ ssescale = _mm_load_si128(reinterpret_cast<__m128i*>(&sub_mul[4]));
+ } else {
+ ssesub = _mm_load_si128(reinterpret_cast<__m128i*>(&sub_mul[8]));
+ ssescale = _mm_load_si128(reinterpret_cast<__m128i*>(&sub_mul[12]));
+ }
+
+ for (uint32 x = 0; x < gw; x++) {
+ __m128i pix_high;
+ __m128i temp;
+ _mm_prefetch(reinterpret_cast<char*>(pixel + 1), _MM_HINT_T0);
+ __m128i pix_low = _mm_load_si128(pixel);
+ // Subtract black
+ pix_low = _mm_subs_epu16(pix_low, ssesub);
+ // Multiply the two unsigned shorts and combine it to 32 bit result
+ pix_high = _mm_mulhi_epu16(pix_low, ssescale);
+ temp = _mm_mullo_epi16(pix_low, ssescale);
+ pix_low = _mm_unpacklo_epi16(temp, pix_high);
+ pix_high = _mm_unpackhi_epi16(temp, pix_high);
+ // Add rounder
+ pix_low = _mm_add_epi32(pix_low, sseround);
+ pix_high = _mm_add_epi32(pix_high, sseround);
+
+ sserandom = _mm_xor_si128(_mm_mulhi_epi16(sserandom, rand_mul),
+ _mm_mullo_epi16(sserandom, rand_mul));
+ __m128i rand_masked =
+ _mm_and_si128(sserandom, rand_mask); // Get 8 random bits
+ rand_masked = _mm_mullo_epi16(rand_masked, sse_full_scale_fp);
+
+ __m128i zero = _mm_setzero_si128();
+ __m128i rand_lo = _mm_sub_epi32(sse_half_scale_fp,
+ _mm_unpacklo_epi16(rand_masked, zero));
+ __m128i rand_hi = _mm_sub_epi32(sse_half_scale_fp,
+ _mm_unpackhi_epi16(rand_masked, zero));
+
+ pix_low = _mm_add_epi32(pix_low, rand_lo);
+ pix_high = _mm_add_epi32(pix_high, rand_hi);
+
+ // Shift down
+ pix_low = _mm_srai_epi32(pix_low, 10);
+ pix_high = _mm_srai_epi32(pix_high, 10);
+ // Subtract to avoid clipping
+ pix_low = _mm_sub_epi32(pix_low, ssesub2);
+ pix_high = _mm_sub_epi32(pix_high, ssesub2);
+ // Pack
+ pix_low = _mm_packs_epi32(pix_low, pix_high);
+ // Shift sign off
+ pix_low = _mm_xor_si128(pix_low, ssesign);
+ _mm_store_si128(pixel, pix_low);
+ pixel++;
+ }
+ }
+ alignedFree(sub_mul);
+}
+#endif
+
+void RawImageDataU16::scaleValues_plain(int start_y, int end_y) {
+ int depth_values = whitePoint - blackLevelSeparate[0];
+ float app_scale = 65535.0F / depth_values;
+
+ // Scale in 30.2 fp
+ auto full_scale_fp = static_cast<int>(app_scale * 4.0F);
+ // Half Scale in 18.14 fp
+ auto half_scale_fp = static_cast<int>(app_scale * 4095.0F);
+
+ // Not SSE2
+ int gw = dim.x * cpp;
+ std::array<int, 4> mul;
+ std::array<int, 4> sub;
+ for (int i = 0; i < 4; i++) {
+ int v = i;
+ if ((mOffset.x & 1) != 0)
+ v ^= 1;
+ if ((mOffset.y & 1) != 0)
+ v ^= 2;
+ mul[i] = static_cast<int>(
+ 16384.0F * 65535.0F /
+ static_cast<float>(whitePoint - blackLevelSeparate[v]));
+ sub[i] = blackLevelSeparate[v];
+ }
+ for (int y = start_y; y < end_y; y++) {
+ int v = dim.x + y * 36969;
+ auto* pixel = reinterpret_cast<ushort16*>(getData(0, y));
+ int* mul_local = &mul[2 * (y & 1)];
+ int* sub_local = &sub[2 * (y & 1)];
+ for (int x = 0; x < gw; x++) {
+ int rand;
+ if (mDitherScale) {
+ v = 18000 * (v & 65535) + (v >> 16);
+ rand = half_scale_fp - (full_scale_fp * (v & 2047));
+ } else {
+ rand = 0;
+ }
+ pixel[x] = clampBits(
+ ((pixel[x] - sub_local[x & 1]) * mul_local[x & 1] + 8192 + rand) >>
+ 14,
+ 16);
+ }
+ }
+}
+
+/* This performs a 4 way interpolated pixel */
+/* The value is interpolated from the 4 closest valid pixels in */
+/* the horizontal and vertical direction. Pixels found further away */
+/* are weighed less */
+
+void RawImageDataU16::fixBadPixel( uint32 x, uint32 y, int component )
+{
+ array<int, 4> values;
+ array<int, 4> dist;
+ array<int, 4> weight;
+
+ values.fill(-1);
+ dist.fill(0);
+ weight.fill(0);
+
+ uchar8* bad_line = &mBadPixelMap[y*mBadPixelMapPitch];
+ int step = isCFA ? 2 : 1;
+
+ // Find pixel to the left
+ int x_find = static_cast<int>(x) - step;
+ int curr = 0;
+ while (x_find >= 0 && values[curr] < 0) {
+ if (0 == ((bad_line[x_find>>3] >> (x_find&7)) & 1)) {
+ values[curr] =
+ (reinterpret_cast<ushort16*>(getDataUncropped(x_find, y)))[component];
+ dist[curr] = static_cast<int>(x) - x_find;
+ }
+ x_find -= step;
+ }
+ // Find pixel to the right
+ x_find = static_cast<int>(x) + step;
+ curr = 1;
+ while (x_find < uncropped_dim.x && values[curr] < 0) {
+ if (0 == ((bad_line[x_find>>3] >> (x_find&7)) & 1)) {
+ values[curr] =
+ (reinterpret_cast<ushort16*>(getDataUncropped(x_find, y)))[component];
+ dist[curr] = x_find - static_cast<int>(x);
+ }
+ x_find += step;
+ }
+
+ bad_line = &mBadPixelMap[x>>3];
+ // Find pixel upwards
+ int y_find = static_cast<int>(y) - step;
+ curr = 2;
+ while (y_find >= 0 && values[curr] < 0) {
+ if (0 == ((bad_line[y_find*mBadPixelMapPitch] >> (x&7)) & 1)) {
+ values[curr] =
+ (reinterpret_cast<ushort16*>(getDataUncropped(x, y_find)))[component];
+ dist[curr] = static_cast<int>(y) - y_find;
+ }
+ y_find -= step;
+ }
+ // Find pixel downwards
+ y_find = static_cast<int>(y) + step;
+ curr = 3;
+ while (y_find < uncropped_dim.y && values[curr] < 0) {
+ if (0 == ((bad_line[y_find*mBadPixelMapPitch] >> (x&7)) & 1)) {
+ values[curr] =
+ (reinterpret_cast<ushort16*>(getDataUncropped(x, y_find)))[component];
+ dist[curr] = y_find - static_cast<int>(y);
+ }
+ y_find += step;
+ }
+
+ // Find x weights
+ int total_dist_x = dist[0] + dist[1];
+
+ int total_shifts = 7;
+ if (total_dist_x) {
+ weight[0] = dist[0] ? (total_dist_x - dist[0]) * 256 / total_dist_x : 0;
+ weight[1] = 256 - weight[0];
+ total_shifts++;
+ }
+
+ // Find y weights
+ int total_dist_y = dist[2] + dist[3];
+ if (total_dist_y) {
+ weight[2] = dist[2] ? (total_dist_y - dist[2]) * 256 / total_dist_y : 0;
+ weight[3] = 256-weight[2];
+ total_shifts++;
+ }
+
+ int total_pixel = 0;
+ for (int i = 0; i < 4; i++)
+ if (values[i] >= 0)
+ total_pixel += values[i] * weight[i];
+
+ total_pixel >>= total_shifts;
+ auto* pix = reinterpret_cast<ushort16*>(getDataUncropped(x, y));
+ pix[component] = clampBits(total_pixel, 16);
+
+ /* Process other pixels - could be done inline, since we have the weights */
+ if (cpp > 1 && component == 0)
+ for (int i = 1; i < static_cast<int>(cpp); i++)
+ fixBadPixel(x,y,i);
+}
+
+// TODO: Could be done with SSE2
+void RawImageDataU16::doLookup( int start_y, int end_y )
+{
+ if (table->ntables == 1) {
+ if (table->dither) {
+ int gw = uncropped_dim.x * cpp;
+ auto* t = reinterpret_cast<uint32*>(table->getTable(0));
+ for (int y = start_y; y < end_y; y++) {
+ uint32 v = (uncropped_dim.x + y * 13) ^ 0x45694584;
+ auto* pixel = reinterpret_cast<ushort16*>(getDataUncropped(0, y));
+ for (int x = 0 ; x < gw; x++) {
+ ushort16 p = *pixel;
+ uint32 lookup = t[p];
+ uint32 base = lookup & 0xffff;
+ uint32 delta = lookup >> 16;
+ v = 15700 *(v & 65535) + (v >> 16);
+ uint32 pix = base + ((delta * (v & 2047) + 1024) >> 12);
+ *pixel = clampBits(pix, 16);
+ pixel++;
+ }
+ }
+ return;
+ }
+
+ int gw = uncropped_dim.x * cpp;
+ ushort16 *t = table->getTable(0);
+ for (int y = start_y; y < end_y; y++) {
+ auto* pixel = reinterpret_cast<ushort16*>(getDataUncropped(0, y));
+ for (int x = 0 ; x < gw; x++) {
+ *pixel = t[*pixel];
+ pixel ++;
+ }
+ }
+ return;
+ }
+ ThrowRDE("Table lookup with multiple components not implemented");
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/RawspeedException.h
b/subprojects/rawspeed/src/librawspeed/common/RawspeedException.h
new file mode 100644
index 00000000..53a81885
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/RawspeedException.h
@@ -0,0 +1,91 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 "rawspeedconfig.h"
+
+#include "common/Common.h"
+#include <array>
+#include <cstdarg>
+#include <cstdio>
+#include <stdexcept>
+#include <string>
+
+namespace rawspeed {
+
+template <typename T>
+[[noreturn]] void __attribute__((noreturn, noinline, format(printf, 1, 2)))
+ThrowException(const char* fmt, ...) {
+ static constexpr size_t bufSize = 8192;
+#if defined(HAVE_CXX_THREAD_LOCAL)
+ static thread_local std::array<char, bufSize> buf;
+#elif defined(HAVE_GCC_THREAD_LOCAL)
+ static __thread char buf[bufSize];
+#else
+#pragma message \
+ "Don't have thread-local-storage! Exception text may be garbled if used multithreaded"
+ static char buf[bufSize];
+#endif
+
+ va_list val;
+ va_start(val, fmt);
+ vsnprintf(buf.data(), sizeof(buf), fmt, val);
+ va_end(val);
+ writeLog(DEBUG_PRIO_EXTRA, "EXCEPTION: %s", buf.data());
+ throw T(buf.data());
+}
+
+class RawspeedException : public std::runtime_error {
+private:
+ void log(const char* msg) {
+ writeLog(DEBUG_PRIO_EXTRA, "EXCEPTION: %s", msg);
+ }
+
+public:
+ explicit RawspeedException(const std::string& msg) : std::runtime_error(msg) {
+ log(msg.c_str());
+ }
+ explicit RawspeedException(const char* msg) : std::runtime_error(msg) {
+ log(msg);
+ }
+};
+
+#undef XSTR
+#define XSTR(a) #a
+
+#undef STR
+#define STR(a) XSTR(a)
+
+#ifndef DEBUG
+#define ThrowExceptionHelper(CLASS, fmt, ...) \
+ rawspeed::ThrowException<CLASS>("%s, line " STR(__LINE__) ": " fmt, \
+ __PRETTY_FUNCTION__, ##__VA_ARGS__)
+#else
+#define ThrowExceptionHelper(CLASS, fmt, ...) \
+ rawspeed::ThrowException<CLASS>(__FILE__ ":" STR(__LINE__) ": %s: " fmt, \
+ __PRETTY_FUNCTION__, ##__VA_ARGS__)
+#endif
+
+#define ThrowRSE(...) \
+ ThrowExceptionHelper(rawspeed::RawspeedException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/SimpleLUT.h
b/subprojects/rawspeed/src/librawspeed/common/SimpleLUT.h
new file mode 100644
index 00000000..ccc64de5
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/SimpleLUT.h
@@ -0,0 +1,67 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Stefan Löffler
+ 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/Common.h" // for clampBits
+#include <algorithm> // for generate_n
+#include <cassert> // for assert
+#include <functional> // for function
+#include <iterator> // for back_inserter
+#include <type_traits> // for enable_if, is_convertible
+#include <vector> // for vector
+
+namespace rawspeed {
+
+template <typename T, int TableBitWidth> class SimpleLUT final {
+public:
+ using value_type = T;
+
+ SimpleLUT() = default;
+
+private:
+ std::vector<value_type> table;
+
+public:
+ template <typename F,
+ typename = std::enable_if<std::is_convertible<
+ F, std::function<value_type(
+ typename decltype(table)::size_type,
+ typename decltype(table)::size_type)>>::value>>
+ explicit SimpleLUT(F&& f) {
+ const auto fullTableSize = 1U << TableBitWidth;
+ table.reserve(fullTableSize);
+ std::generate_n(std::back_inserter(table), fullTableSize,
+ [&f, table = &table]() {
+ // which row [0..fullTableSize) are we filling?
+ const auto i = table->size();
+ return f(i, fullTableSize);
+ });
+ assert(table.size() == fullTableSize);
+ }
+
+ inline value_type operator[](int x) const {
+ unsigned clampedX = clampBits(x, TableBitWidth);
+ return table[clampedX];
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/Spline.h
b/subprojects/rawspeed/src/librawspeed/common/Spline.h
new file mode 100644
index 00000000..bcfa97e2
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/Spline.h
@@ -0,0 +1,178 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Robert Bieber
+ 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/Common.h" // for ushort16
+#include "common/Point.h" // for iPoint2D
+#include <algorithm> // for max, min, adjacent_find, for_each
+#include <cassert> // for assert
+#include <functional> // for greater_equal
+#include <limits> // for numeric_limits
+#include <memory> // for allocator_traits<>::value_type
+#include <type_traits> // for is_floating_point, enable_if_t, is_arithm...
+#include <vector> // for vector
+
+namespace rawspeed {
+
+// This is a Natural Cubic Spline. The second derivative at curve ends are zero.
+// See https://en.wikipedia.org/wiki/Spline_(mathematics)
+// section "Algorithm for computing natural cubic splines"
+
+template <typename T = ushort16,
+ typename = std::enable_if_t<std::is_arithmetic<T>::value>>
+class Spline final {
+public:
+ using value_type = T;
+
+ // These are the constant factors for each segment of the curve.
+ // Each segment i will have the formula:
+ // f(x) = a[i] + b[i]*(x - x[i]) + c[i]*(x - x[i])^2 + d[i]*(x - x[i])^3
+ struct Segment {
+ double a;
+ double b;
+ double c;
+ double d;
+ };
+
+private:
+ int num_coords;
+ int num_segments;
+
+ std::vector<int> xCp;
+ std::vector<Segment> segments;
+
+ void prepare() {
+ // Extra values used during computation
+ std::vector<double> h(num_segments);
+ std::vector<double> alpha(num_segments);
+ std::vector<double> mu(num_coords);
+ std::vector<double> z(num_coords);
+
+ for (int i = 0; i < num_segments; i++)
+ h[i] = xCp[i + 1] - xCp[i];
+
+ for (int i = 1; i < num_segments; i++) {
+ Segment& sp = segments[i - 1];
+ Segment& s = segments[i];
+ Segment& sn = segments[i + 1];
+
+ alpha[i] = (3. / h[i]) * (sn.a - s.a) - (3. / h[i - 1]) * (s.a - sp.a);
+ }
+
+ mu[0] = z[0] = 0;
+
+ for (int i = 1; i < num_segments; i++) {
+ const double l = 2 * (xCp[i + 1] - xCp[i - 1]) - (h[i - 1] * mu[i - 1]);
+ mu[i] = h[i] / l;
+ z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l;
+ }
+
+ z.back() = segments.back().c = 0;
+
+ for (int i = num_segments - 1; i >= 0; i--) {
+ Segment& s = segments[i];
+ Segment& sn = segments[i + 1];
+
+ s.c = z[i] - mu[i] * sn.c;
+ s.b = (sn.a - s.a) / h[i] - h[i] * (sn.c + 2 * s.c) / 3.;
+ s.d = (sn.c - s.c) / (3. * h[i]);
+ }
+
+ // The last segment is nonsensical, and was only used to temporairly store
+ // the a and c to simplify calculations, so drop that 'segment' now
+ segments.pop_back();
+
+ assert(static_cast<typename decltype(segments)::size_type>(num_segments) ==
+ segments.size());
+ }
+
+public:
+ explicit Spline(const std::vector<iPoint2D>& control_points) {
+ assert(control_points.size() >= 2 &&
+ "Need at least two points to interpolate between");
+
+ // Expect the X coords of the curve to start/end at the extreme values
+ assert(control_points.front().x == 0);
+ assert(control_points.back().x == 65535);
+
+ assert(std::adjacent_find(
+ control_points.cbegin(), control_points.cend(),
+ [](const iPoint2D& lhs, const iPoint2D& rhs) -> bool {
+ return std::greater_equal<>()(lhs.x, rhs.x);
+ }) == control_points.cend() &&
+ "The X coordinates must all be strictly increasing");
+
+#ifndef NDEBUG
+ if (!std::is_floating_point<value_type>::value) {
+ // The Y coords must be limited to the range of value_type
+ std::for_each(control_points.cbegin(), control_points.cend(),
+ [](const iPoint2D& p) -> void {
+ assert(p.y >= std::numeric_limits<value_type>::min());
+ assert(p.y <= std::numeric_limits<value_type>::max());
+ });
+ }
+#endif
+
+ num_coords = control_points.size();
+ num_segments = num_coords - 1;
+
+ xCp.resize(num_coords);
+ segments.resize(num_coords);
+ for (int i = 0; i < num_coords; i++) {
+ xCp[i] = control_points[i].x;
+ segments[i].a = control_points[i].y;
+ }
+
+ prepare();
+ }
+
+ std::vector<Segment> getSegments() const { return segments; }
+
+ std::vector<value_type> calculateCurve() const {
+ std::vector<value_type> curve(65536);
+
+ for (int i = 0; i < num_segments; i++) {
+ const Segment& s = segments[i];
+
+ for (int x = xCp[i]; x <= xCp[i + 1]; x++) {
+ double diff = x - xCp[i];
+ double diff_2 = diff * diff;
+ double diff_3 = diff * diff * diff;
+
+ double interpolated = s.a + s.b * diff + s.c * diff_2 + s.d * diff_3;
+
+ if (!std::is_floating_point<value_type>::value) {
+ interpolated = std::max(
+ interpolated, double(std::numeric_limits<value_type>::min()));
+ interpolated = std::min(
+ interpolated, double(std::numeric_limits<value_type>::max()));
+ }
+
+ curve[x] = interpolated;
+ }
+ }
+
+ return curve;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/TableLookUp.cpp
b/subprojects/rawspeed/src/librawspeed/common/TableLookUp.cpp
new file mode 100644
index 00000000..803a323e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/TableLookUp.cpp
@@ -0,0 +1,81 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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
+*/
+
+#include "common/TableLookUp.h"
+#include "decoders/RawDecoderException.h" // for RawDecoderException (ptr o...
+#include <cassert> // for assert
+
+namespace rawspeed {
+
+const int TABLE_SIZE = 65536 * 2;
+
+// Creates n numre of tables.
+TableLookUp::TableLookUp(int _ntables, bool _dither)
+ : ntables(_ntables), dither(_dither) {
+ if (ntables < 1) {
+ ThrowRDE("Cannot construct 0 tables");
+ }
+ tables.resize(ntables * TABLE_SIZE, ushort16(0));
+}
+
+void TableLookUp::setTable(int ntable, const std::vector<ushort16>& table) {
+ assert(!table.empty());
+
+ const int nfilled = table.size();
+ if (nfilled >= 65536)
+ ThrowRDE("Table lookup with %i entries is unsupported", nfilled);
+
+ if (ntable > ntables) {
+ ThrowRDE("Table lookup with number greater than number of tables.");
+ }
+ ushort16* t = &tables[ntable * TABLE_SIZE];
+ if (!dither) {
+ for (int i = 0; i < 65536; i++) {
+ t[i] = (i < nfilled) ? table[i] : table[nfilled - 1];
+ }
+ return;
+ }
+ for (int i = 0; i < nfilled; i++) {
+ int center = table[i];
+ int lower = i > 0 ? table[i - 1] : center;
+ int upper = i < (nfilled - 1) ? table[i + 1] : center;
+ int delta = upper - lower;
+ t[i * 2] = clampBits(center - ((upper - lower + 2) / 4), 16);
+ t[i * 2 + 1] = ushort16(delta);
+ // FIXME: this is completely broken when the curve is non-monotonic.
+ }
+
+ for (int i = nfilled; i < 65536; i++) {
+ t[i * 2] = table[nfilled - 1];
+ t[i * 2 + 1] = 0;
+ }
+ t[0] = t[1];
+ t[TABLE_SIZE - 1] = t[TABLE_SIZE - 2];
+}
+
+ushort16* TableLookUp::getTable(int n) {
+ if (n > ntables) {
+ ThrowRDE("Table lookup with number greater than number of tables.");
+ }
+ return &tables[n * TABLE_SIZE];
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/TableLookUp.h
b/subprojects/rawspeed/src/librawspeed/common/TableLookUp.h
new file mode 100644
index 00000000..6bad3ef8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/TableLookUp.h
@@ -0,0 +1,40 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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/Common.h" // for ushort16
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class TableLookUp {
+public:
+ TableLookUp(int ntables, bool dither);
+
+ void setTable(int ntable, const std::vector<ushort16>& table);
+ ushort16* getTable(int n);
+ const int ntables;
+ std::vector<ushort16> tables;
+ const bool dither;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/common/meson.build
b/subprojects/rawspeed/src/librawspeed/common/meson.build
new file mode 100644
index 00000000..9ae854a6
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/meson.build
@@ -0,0 +1,38 @@
+dependencies = [openmp_dep]
+
+sources = files(
+ 'Array2DRef.h',
+ 'ChecksumFile.cpp',
+ 'ChecksumFile.h',
+ 'Common.cpp',
+ 'Common.h',
+ 'Cpuid.cpp',
+ 'DefaultInitAllocatorAdaptor.h',
+ 'DngOpcodes.cpp',
+ 'DngOpcodes.h',
+ 'ErrorLog.cpp',
+ 'ErrorLog.h',
+ 'Memory.cpp',
+ 'Memory.h',
+ 'Mutex.h',
+ 'NORangesSet.h',
+ 'Optional.h',
+ 'Point.h',
+ 'Range.h',
+ 'RawImage.cpp',
+ 'RawImage.h',
+ 'RawImageDataFloat.cpp',
+ 'RawImageDataU16.cpp',
+ 'RawspeedException.h',
+ 'SimpleLUT.h',
+ 'Spline.h',
+ 'TableLookUp.cpp',
+ 'TableLookUp.h',
+)
+
+librawspeed_common = static_library(
+ 'rawspeed-common',
+ sources,
+ dependencies: dependencies,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.cpp
new file mode 100644
index 00000000..8f840861
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.cpp
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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/AbstractTiffDecoder.h"
+#include "common/Common.h" // for uint32
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffIFD, TiffRootIFD, Tiff...
+#include <vector> // for vector
+
+namespace rawspeed {
+
+const TiffIFD* AbstractTiffDecoder::getIFDWithLargestImage(TiffTag filter) const
+{
+ std::vector<const TiffIFD*> ifds = mRootIFD->getIFDsWithTag(filter);
+
+ if (ifds.empty())
+ ThrowRDE("No suitable IFD with tag 0x%04x found.", filter);
+
+ auto res = ifds[0];
+ uint32 width = res->getEntry(IMAGEWIDTH)->getU32();
+ for (auto ifd : ifds) {
+ TiffEntry* widthE = ifd->getEntry(IMAGEWIDTH);
+ // guard against random maker note entries with the same tag
+ if (widthE->count == 1 && widthE->getU32() > width) {
+ res = ifd;
+ width = widthE->getU32();
+ }
+ }
+
+ return res;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.h
new file mode 100644
index 00000000..830f2a9b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/AbstractTiffDecoder.h
@@ -0,0 +1,70 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "decoders/RawDecoder.h" // for RawDecoder
+#include "tiff/TiffIFD.h" // for TiffID, TiffRootIFD, TiffRootIFDOwner
+#include "tiff/TiffTag.h" // for IMAGEWIDTH, TiffTag
+#include <memory> // for unique_ptr
+#include <string> // for string
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+class Buffer;
+
+class AbstractTiffDecoder : public RawDecoder
+{
+protected:
+ TiffRootIFDOwner mRootIFD;
+public:
+ AbstractTiffDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : RawDecoder(file), mRootIFD(std::move(root)) {}
+
+ TiffIFD* getRootIFD() final { return mRootIFD.get(); }
+
+ inline bool checkCameraSupported(const CameraMetaData* meta, const TiffID& id,
+ const std::string& mode) {
+ return RawDecoder::checkCameraSupported(meta, id.make, id.model, mode);
+ }
+
+ using RawDecoder::setMetaData;
+
+ inline void setMetaData(const CameraMetaData* meta, const TiffID& id,
+ const std::string& mode, int iso_speed) {
+ setMetaData(meta, id.make, id.model, mode, iso_speed);
+ }
+
+ inline void setMetaData(const CameraMetaData* meta, const std::string& mode,
+ int iso_speed) {
+ setMetaData(meta, mRootIFD->getID(), mode, iso_speed);
+ }
+
+ inline void checkSupportInternal(const CameraMetaData* meta) override {
+ checkCameraSupported(meta, mRootIFD->getID(), "");
+ }
+
+ const TiffIFD* getIFDWithLargestImage(TiffTag filter = IMAGEWIDTH) const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.cpp
new file mode 100644
index 00000000..bb53164d
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.cpp
@@ -0,0 +1,465 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/ArwDecoder.h"
+#include "common/Common.h" // for uint32, uchar8
+#include "common/Point.h" // for iPoint2D
+#include "common/RawspeedException.h" // for RawspeedException
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre...
+#include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endi...
+#include "metadata/Camera.h" // for Hints
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for DNGPRIVATEDATA
+#include <cassert> // for assert
+#include <cstring> // for memcpy, size_t
+#include <memory> // for unique_ptr
+#include <set> // for set
+#include <string> // for operator==, string
+#include <vector> // for vector
+
+using std::vector;
+using std::string;
+
+namespace rawspeed {
+
+bool ArwDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "SONY";
+}
+
+RawImage ArwDecoder::decodeSRF(const TiffIFD* raw) {
+ raw = mRootIFD->getIFDWithTag(IMAGEWIDTH);
+
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ if (width == 0 || height == 0 || width > 3360 || height > 2460)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ uint32 len = width * height * 2;
+
+ // Constants taken from dcraw
+ uint32 off = 862144;
+ uint32 key_off = 200896;
+ uint32 head_off = 164600;
+
+ // Replicate the dcraw contortions to get the "decryption" key
+ const uchar8* keyData = mFile->getData(key_off, 1);
+ uint32 offset = (*keyData) * 4;
+ keyData = mFile->getData(key_off + offset, 4);
+ uint32 key = getU32BE(keyData);
+ static const size_t head_size = 40;
+ const uchar8* head_orig = mFile->getData(head_off, head_size);
+ vector<uchar8> head(head_size);
+ SonyDecrypt(reinterpret_cast<const uint32*>(head_orig),
+ reinterpret_cast<uint32*>(&head[0]), 10, key);
+ for (int i = 26; i > 22; i--)
+ key = key << 8 | head[i - 1];
+
+ // "Decrypt" the whole image buffer
+ auto image_data = mFile->getData(off, len);
+ auto image_decoded = Buffer::Create(len);
+ SonyDecrypt(reinterpret_cast<const uint32*>(image_data),
+ reinterpret_cast<uint32*>(image_decoded.get()), len / 4, key);
+
+ Buffer di(move(image_decoded), len);
+
+ // And now decode as a normal 16bit raw
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->createData();
+
+ UncompressedDecompressor u(di, 0, len, mRaw);
+ u.decodeRawUnpacked<16, Endianness::big>(width, height);
+
+ return mRaw;
+}
+
+RawImage ArwDecoder::decodeRawInternal() {
+ const TiffIFD* raw = nullptr;
+ vector<const TiffIFD*> data = mRootIFD->getIFDsWithTag(STRIPOFFSETS);
+
+ if (data.empty()) {
+ TiffEntry *model = mRootIFD->getEntryRecursive(MODEL);
+
+ if (model && model->getString() == "DSLR-A100") {
+ // We've caught the elusive A100 in the wild, a transitional format
+ // between the simple sanity of the MRW custom format and the wordly
+ // wonderfullness of the Tiff-based ARW format, let's shoot from the hip
+ raw = mRootIFD->getIFDWithTag(SUBIFDS);
+ uint32 off = raw->getEntry(SUBIFDS)->getU32();
+ uint32 width = 3881;
+ uint32 height = 2608;
+
+ mRaw->dim = iPoint2D(width, height);
+
+ ByteStream input(mFile, off);
+ SonyArw1Decompressor a(mRaw);
+ mRaw->createData();
+ a.decompress(input);
+
+ return mRaw;
+ }
+
+ if (hints.has("srf_format"))
+ return decodeSRF(raw);
+
+ ThrowRDE("No image data found");
+ }
+
+ raw = data[0];
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ if (1 == compression) {
+ DecodeUncompressed(raw);
+ return mRaw;
+ }
+
+ if (32767 != compression)
+ ThrowRDE("Unsupported compression");
+
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);
+
+ if (offsets->count != 1) {
+ ThrowRDE("Multiple Strips found: %u", offsets->count);
+ }
+ if (counts->count != offsets->count) {
+ ThrowRDE(
+ "Byte count number does not match strip size: count:%u, strips:%u ",
+ counts->count, offsets->count);
+ }
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ switch (bitPerPixel) {
+ case 8:
+ case 12:
+ case 14:
+ break;
+ default:
+ ThrowRDE("Unexpected bits per pixel: %u", bitPerPixel);
+ }
+
+ // Sony E-550 marks compressed 8bpp ARW with 12 bit per pixel
+ // this makes the compression detect it as a ARW v1.
+ // This camera has however another MAKER entry, so we MAY be able
+ // to detect it this way in the future.
+ data = mRootIFD->getIFDsWithTag(MAKE);
+ if (data.size() > 1) {
+ for (auto &i : data) {
+ string make = i->getEntry(MAKE)->getString();
+ /* Check for maker "SONY" without spaces */
+ if (make == "SONY")
+ bitPerPixel = 8;
+ }
+ }
+
+ if (width == 0 || height == 0 || height % 2 != 0 || width > 8000 ||
+ height > 5320)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ bool arw1 = uint64(counts->getU32()) * 8 != width * height * bitPerPixel;
+ if (arw1)
+ height += 8;
+
+ mRaw->dim = iPoint2D(width, height);
+
+ std::vector<ushort16> curve(0x4001);
+ TiffEntry *c = raw->getEntry(SONY_CURVE);
+ std::array<uint32, 6> sony_curve = {{0, 0, 0, 0, 0, 4095}};
+
+ for (uint32 i = 0; i < 4; i++)
+ sony_curve[i+1] = (c->getU16(i) >> 2) & 0xfff;
+
+ for (uint32 i = 0; i < 0x4001; i++)
+ curve[i] = i;
+
+ for (uint32 i = 0; i < 5; i++)
+ for (uint32 j = sony_curve[i] + 1; j <= sony_curve[i+1]; j++)
+ curve[j] = curve[j-1] + (1 << i);
+
+ RawImageCurveGuard curveHandler(&mRaw, curve, uncorrectedRawValues);
+
+ uint32 c2 = counts->getU32();
+ uint32 off = offsets->getU32();
+
+ if (!mFile->isValid(off))
+ ThrowRDE("Data offset after EOF, file probably truncated");
+
+ if (!mFile->isValid(off, c2))
+ c2 = mFile->getSize() - off;
+
+ ByteStream input(mFile, off, c2);
+
+ if (arw1) {
+ SonyArw1Decompressor a(mRaw);
+ mRaw->createData();
+ a.decompress(input);
+ } else
+ DecodeARW2(input, width, height, bitPerPixel);
+
+ return mRaw;
+}
+
+void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) {
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 off = raw->getEntry(STRIPOFFSETS)->getU32();
+ uint32 c2 = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+
+ mRaw->dim = iPoint2D(width, height);
+
+ if (width == 0 || height == 0 || width > 8000 || height > 5320)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (c2 == 0)
+ ThrowRDE("Strip is empty, nothing to decode!");
+
+ const Buffer buf(mFile->getSubView(off, c2));
+
+ mRaw->createData();
+
+ UncompressedDecompressor u(buf, mRaw);
+
+ if (hints.has("sr2_format"))
+ u.decodeRawUnpacked<14, Endianness::big>(width, height);
+ else
+ u.decodeRawUnpacked<16, Endianness::little>(width, height);
+}
+
+void ArwDecoder::DecodeARW2(const ByteStream& input, uint32 w, uint32 h,
+ uint32 bpp) {
+
+ if (bpp == 8) {
+ SonyArw2Decompressor a2(mRaw, input);
+ mRaw->createData();
+ a2.decompress();
+ return;
+ } // End bpp = 8
+
+ if (bpp == 12) {
+ mRaw->createData();
+ UncompressedDecompressor u(input, mRaw);
+ u.decode12BitRaw<Endianness::little>(w, h);
+
+ // Shift scales, since black and white are the same as compressed precision
+ mShiftDownScale = 2;
+ return;
+ }
+ ThrowRDE("Unsupported bit depth");
+}
+
+void ArwDecoder::ParseA100WB() {
+ if (!mRootIFD->hasEntryRecursive(DNGPRIVATEDATA))
+ return;
+
+ // only contains the offset, not the length!
+ TiffEntry* priv = mRootIFD->getEntryRecursive(DNGPRIVATEDATA);
+ ByteStream bs = priv->getData();
+ bs.setByteOrder(Endianness::little);
+ const uint32 off = bs.getU32();
+
+ bs = ByteStream(*mFile, off);
+
+ // MRW style, see MrwDecoder
+
+ bs.setByteOrder(Endianness::big);
+ uint32 tag = bs.getU32();
+ if (0x4D5249 != tag) // MRI
+ ThrowRDE("Can not parse DNGPRIVATEDATA, invalid tag (0x%x).", tag);
+
+ bs.setByteOrder(Endianness::little);
+ uint32 len = bs.getU32();
+
+ bs = bs.getSubStream(bs.getPosition(), len);
+
+ while (bs.getRemainSize() > 0) {
+ bs.setByteOrder(Endianness::big);
+ tag = bs.getU32();
+ bs.setByteOrder(Endianness::little);
+ len = bs.getU32();
+ bs.check(len);
+ if (!len)
+ ThrowRDE("Found entry of zero length, corrupt.");
+
+ if (0x574247 != tag) { // WBG
+ // not the tag we are interested in, skip
+ bs.skipBytes(len);
+ continue;
+ }
+
+ bs.skipBytes(4);
+
+ bs.setByteOrder(Endianness::little);
+ std::array<ushort16, 4> tmp;
+ for (auto& coeff : tmp)
+ coeff = bs.getU16();
+
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(tmp[0]);
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(tmp[1]);
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(tmp[3]);
+
+ // only need this one block, no need to process any further
+ break;
+ }
+}
+
+void ArwDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ //Default
+ int iso = 0;
+
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ auto id = mRootIFD->getID();
+
+ setMetaData(meta, id, "", iso);
+ mRaw->whitePoint >>= mShiftDownScale;
+ mRaw->blackLevel >>= mShiftDownScale;
+
+ // Set the whitebalance
+ try {
+ if (id.model == "DSLR-A100") { // Handle the MRW style WB of the A100
+ ParseA100WB();
+ } else { // Everything else but the A100
+ GetWB();
+ }
+ } catch (RawspeedException& e) {
+ mRaw->setError(e.what());
+ // We caught an exception reading WB, just ignore it
+ }
+}
+
+void ArwDecoder::SonyDecrypt(const uint32* ibuf, uint32* obuf, uint32 len,
+ uint32 key) {
+ if (0 == len)
+ return;
+
+ std::array<uint32, 128> pad;
+
+ // Initialize the decryption pad from the key
+ for (int p=0; p < 4; p++)
+ pad[p] = key = uint32(key * 48828125UL + 1UL);
+ pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31;
+ for (int p=4; p < 127; p++)
+ pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31;
+ for (int p=0; p < 127; p++)
+ pad[p] = getU32BE(&pad[p]);
+
+ int p = 127;
+ // Decrypt the buffer in place using the pad
+ for (; len > 0; len--) {
+ pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+1+64) & 127];
+
+ uint32 pv;
+ memcpy(&pv, &(pad[p & 127]), sizeof(uint32));
+
+ uint32 bv;
+ memcpy(&bv, ibuf, sizeof(uint32));
+
+ bv ^= pv;
+
+ memcpy(obuf, &bv, sizeof(uint32));
+
+ ibuf++;
+ obuf++;
+ p++;
+ }
+}
+
+void ArwDecoder::GetWB() {
+ // Set the whitebalance for all the modern ARW formats (everything after A100)
+ if (mRootIFD->hasEntryRecursive(DNGPRIVATEDATA)) {
+ NORangesSet<Buffer> ifds_undecoded;
+
+ TiffEntry *priv = mRootIFD->getEntryRecursive(DNGPRIVATEDATA);
+ TiffRootIFD makerNoteIFD(nullptr, &ifds_undecoded, priv->getRootIfdData(),
+ priv->getU32());
+
+ TiffEntry *sony_offset = makerNoteIFD.getEntryRecursive(SONY_OFFSET);
+ TiffEntry *sony_length = makerNoteIFD.getEntryRecursive(SONY_LENGTH);
+ TiffEntry *sony_key = makerNoteIFD.getEntryRecursive(SONY_KEY);
+ if(!sony_offset || !sony_length || !sony_key || sony_key->count != 4)
+ ThrowRDE("couldn't find the correct metadata for WB decoding");
+
+ assert(sony_offset != nullptr);
+ uint32 off = sony_offset->getU32();
+
+ assert(sony_length != nullptr);
+ // The Decryption is done in blocks of 4 bytes.
+ uint32 len = roundDown(sony_length->getU32(), 4);
+
+ assert(sony_key != nullptr);
+ uint32 key = getU32LE(sony_key->getData(4));
+
+ // "Decrypt" IFD
+ const auto& ifd_crypt = priv->getRootIfdData();
+ const auto EncryptedBuffer = ifd_crypt.getSubView(off, len);
+ // We do have to prepend 'off' padding, because TIFF uses absolute offsets.
+ const auto DecryptedBufferSize = off + EncryptedBuffer.getSize();
+ auto DecryptedBuffer = Buffer::Create(DecryptedBufferSize);
+
+ SonyDecrypt(reinterpret_cast<const uint32*>(EncryptedBuffer.begin()),
+ reinterpret_cast<uint32*>(DecryptedBuffer.get() + off), len / 4,
+ key);
+
+ NORangesSet<Buffer> ifds_decoded;
+ Buffer decIFD(std::move(DecryptedBuffer), DecryptedBufferSize);
+ const Buffer Padding(decIFD.getSubView(0, off));
+ // The Decrypted Root Ifd can not point to preceding padding buffer.
+ ifds_decoded.emplace(Padding);
+
+ DataBuffer dbIDD(decIFD, priv->getRootIfdData().getByteOrder());
+ TiffRootIFD encryptedIFD(nullptr, &ifds_decoded, dbIDD, off);
+
+ if (encryptedIFD.hasEntry(SONYGRBGLEVELS)){
+ TiffEntry *wb = encryptedIFD.getEntry(SONYGRBGLEVELS);
+ if (wb->count != 4)
+ ThrowRDE("WB has %d entries instead of 4", wb->count);
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
+ } else if (encryptedIFD.hasEntry(SONYRGGBLEVELS)){
+ TiffEntry *wb = encryptedIFD.getEntry(SONYRGGBLEVELS);
+ if (wb->count != 4)
+ ThrowRDE("WB has %d entries instead of 4", wb->count);
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(3);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.h
new file mode 100644
index 00000000..c89a21ba
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ArwDecoder.h
@@ -0,0 +1,61 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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 "common/RawImage.h" // for RawImage
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "io/ByteStream.h" // for ByteStream
+#include "tiff/TiffIFD.h" // for TiffIFD (ptr only), TiffRo...
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+class Buffer;
+
+class ArwDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ ArwDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ void ParseA100WB();
+
+ int getDecoderVersion() const override { return 1; }
+ RawImage decodeSRF(const TiffIFD* raw);
+ void DecodeARW2(const ByteStream& input, uint32 w, uint32 h, uint32 bpp);
+ void DecodeUncompressed(const TiffIFD* raw);
+ void SonyDecrypt(const uint32* ibuf, uint32* obuf, uint32 len, uint32 key);
+ void GetWB();
+ ByteStream in;
+ int mShiftDownScale = 0;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.cpp
new file mode 100644
index 00000000..004e1a56
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.cpp
@@ -0,0 +1,323 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015-2017 Roman Lebedev
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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/Cr2Decoder.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawspeedException.h" // for RawspeedException
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/Cr2Decompressor.h" // for Cr2Decompressor, Cr2S...
+#include "interpolators/Cr2sRawInterpolator.h" // for Cr2sRawInterpolator
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endiannes...
+#include "metadata/Camera.h" // for Hints
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE
+#include "parsers/TiffParserException.h" // for ThrowTPE
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_SHORT
+#include "tiff/TiffTag.h" // for TiffTag, CANONCOLORDATA
+#include <array> // for array
+#include <cassert> // for assert
+#include <memory> // for unique_ptr, allocator...
+#include <string> // for operator==, string
+#include <vector> // for vector
+// IWYU pragma: no_include <ext/alloc_traits.h>
+
+using std::string;
+
+namespace rawspeed {
+
+bool Cr2Decoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+ const std::string& model = id.model;
+
+ // FIXME: magic
+
+ return make == "Canon" ||
+ (make == "Kodak" && (model == "DCS520C" || model == "DCS560C"));
+}
+
+RawImage Cr2Decoder::decodeOldFormat() {
+ uint32 offset = 0;
+ if (mRootIFD->getEntryRecursive(CANON_RAW_DATA_OFFSET))
+ offset = mRootIFD->getEntryRecursive(CANON_RAW_DATA_OFFSET)->getU32();
+ else {
+ // D2000 is oh so special...
+ auto ifd = mRootIFD->getIFDWithTag(CFAPATTERN);
+ if (! ifd->hasEntry(STRIPOFFSETS))
+ ThrowRDE("Couldn't find offset");
+
+ offset = ifd->getEntry(STRIPOFFSETS)->getU32();
+ }
+
+ ByteStream b(mFile, offset, Endianness::big);
+ b.skipBytes(41);
+ int height = b.getU16();
+ int width = b.getU16();
+
+ // some old models (1D/1DS/D2000C) encode two lines as one
+ // see: FIX_CANON_HALF_HEIGHT_DOUBLE_WIDTH
+ if (width > 2*height) {
+ height *= 2;
+ width /= 2;
+ }
+ width *= 2; // components
+
+ mRaw->dim = {width, height};
+
+ const ByteStream bs(mFile->getSubView(offset), 0);
+
+ Cr2Decompressor l(bs, mRaw);
+ mRaw->createData();
+
+ Cr2Slicing slicing(/*numSlices=*/1, /*sliceWidth=don't care*/ 0,
+ /*lastSliceWidth=*/width);
+ l.decode(slicing);
+
+ // deal with D2000 GrayResponseCurve
+ TiffEntry* curve = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x123));
+ if (curve && curve->type == TIFF_SHORT && curve->count == 4096) {
+ auto table = curve->getU16Array(curve->count);
+ RawImageCurveGuard curveHandler(&mRaw, table, uncorrectedRawValues);
+
+ // Apply table
+ if (!uncorrectedRawValues)
+ mRaw->sixteenBitLookup();
+ }
+
+ return mRaw;
+}
+
+// for technical details about Cr2 mRAW/sRAW, see http://lclevy.free.fr/cr2/
+
+RawImage Cr2Decoder::decodeNewFormat() {
+ TiffEntry* sensorInfoE = mRootIFD->getEntryRecursive(CANON_SENSOR_INFO);
+ if (!sensorInfoE)
+ ThrowTPE("failed to get SensorInfo from MakerNote");
+
+ assert(sensorInfoE != nullptr);
+
+ const ushort16 width = sensorInfoE->getU16(1);
+ const ushort16 height = sensorInfoE->getU16(2);
+ mRaw->dim = {width, height};
+
+ int componentsPerPixel = 1;
+ TiffIFD* raw = mRootIFD->getSubIFDs()[3].get();
+ if (raw->hasEntry(CANON_SRAWTYPE) &&
+ raw->getEntry(CANON_SRAWTYPE)->getU32() == 4)
+ componentsPerPixel = 3;
+
+ mRaw->setCpp(componentsPerPixel);
+ mRaw->isCFA = (mRaw->getCpp() == 1);
+
+ Cr2Slicing slicing;
+ // there are four cases:
+ // * there is a tag with three components,
+ // $ last two components are non-zero: all fine then.
+ // $ first two components are zero, last component is non-zero
+ // we let Cr2Decompressor guess it (it'll throw if fails)
+ // $ else the image is considered corrupt.
+ // * there is a tag with not three components, the image is considered
+ // corrupt. $ there is no tag, we let Cr2Decompressor guess it (it'll throw if
+ // fails)
+ TiffEntry* cr2SliceEntry = raw->getEntryRecursive(CANONCR2SLICE);
+ if (cr2SliceEntry) {
+ if (cr2SliceEntry->count != 3) {
+ ThrowRDE("Found RawImageSegmentation tag with %d elements, should be 3.",
+ cr2SliceEntry->count);
+ }
+
+ if (cr2SliceEntry->getU16(1) != 0 && cr2SliceEntry->getU16(2) != 0) {
+ // first component can be either zero or non-zero, don't care
+ slicing = Cr2Slicing(/*numSlices=*/1 + cr2SliceEntry->getU16(0),
+ /*sliceWidth=*/cr2SliceEntry->getU16(1),
+ /*lastSliceWidth=*/cr2SliceEntry->getU16(2));
+ } else if (cr2SliceEntry->getU16(0) == 0 && cr2SliceEntry->getU16(1) == 0 &&
+ cr2SliceEntry->getU16(2) != 0) {
+ // PowerShot G16, PowerShot S120, let Cr2Decompressor guess.
+ } else {
+ ThrowRDE("Strange RawImageSegmentation tag: (%d, %d, %d), image corrupt.",
+ cr2SliceEntry->getU16(0), cr2SliceEntry->getU16(1),
+ cr2SliceEntry->getU16(2));
+ }
+ } // EOS 20D, EOS-1D Mark II, let Cr2Decompressor guess.
+
+ const uint32 offset = raw->getEntry(STRIPOFFSETS)->getU32();
+ const uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+
+ const ByteStream bs(mFile->getSubView(offset, count), 0);
+
+ Cr2Decompressor d(bs, mRaw);
+ mRaw->createData();
+ d.decode(slicing);
+
+ if (mRaw->metadata.subsampling.x > 1 || mRaw->metadata.subsampling.y > 1)
+ sRawInterpolate();
+
+ return mRaw;
+}
+
+RawImage Cr2Decoder::decodeRawInternal() {
+ if (mRootIFD->getSubIFDs().size() < 4)
+ return decodeOldFormat();
+ else // NOLINT ok, here it make sense
+ return decodeNewFormat();
+}
+
+void Cr2Decoder::checkSupportInternal(const CameraMetaData* meta) {
+ auto id = mRootIFD->getID();
+ // Check for sRaw mode
+ if (mRootIFD->getSubIFDs().size() == 4) {
+ TiffEntry* typeE = mRootIFD->getSubIFDs()[3]->getEntryRecursive(CANON_SRAWTYPE);
+ if (typeE && typeE->getU32() == 4) {
+ checkCameraSupported(meta, id, "sRaw1");
+ return;
+ }
+ }
+
+ checkCameraSupported(meta, id, "");
+}
+
+void Cr2Decoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+
+ string mode;
+
+ if (mRaw->metadata.subsampling.y == 2 && mRaw->metadata.subsampling.x == 2)
+ mode = "sRaw1";
+
+ if (mRaw->metadata.subsampling.y == 1 && mRaw->metadata.subsampling.x == 2)
+ mode = "sRaw2";
+
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ // Fetch the white balance
+ try{
+ if (mRootIFD->hasEntryRecursive(CANONCOLORDATA)) {
+ TiffEntry *wb = mRootIFD->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
+ int offset = hints.get("wb_offset", 126);
+
+ offset /= 2;
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(wb->getU16(offset + 0));
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(wb->getU16(offset + 1));
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(wb->getU16(offset + 3));
+ } else {
+ if (mRootIFD->hasEntryRecursive(CANONSHOTINFO) &&
+ mRootIFD->hasEntryRecursive(CANONPOWERSHOTG9WB)) {
+ TiffEntry *shot_info = mRootIFD->getEntryRecursive(CANONSHOTINFO);
+ TiffEntry *g9_wb = mRootIFD->getEntryRecursive(CANONPOWERSHOTG9WB);
+
+ ushort16 wb_index = shot_info->getU16(7);
+ int wb_offset = (wb_index < 18) ? "012347800000005896"[wb_index]-'0' : 0;
+ wb_offset = wb_offset*8 + 2;
+
+ mRaw->metadata.wbCoeffs[0] =
+ static_cast<float>(g9_wb->getU32(wb_offset + 1));
+ mRaw->metadata.wbCoeffs[1] =
+ (static_cast<float>(g9_wb->getU32(wb_offset + 0)) +
+ static_cast<float>(g9_wb->getU32(wb_offset + 3))) /
+ 2.0F;
+ mRaw->metadata.wbCoeffs[2] =
+ static_cast<float>(g9_wb->getU32(wb_offset + 2));
+ } else if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0xa4))) {
+ // WB for the old 1D and 1DS
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0xa4));
+ if (wb->count >= 3) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
+ }
+ }
+ }
+ } catch (RawspeedException& e) {
+ mRaw->setError(e.what());
+ // We caught an exception reading WB, just ignore it
+ }
+ setMetaData(meta, mode, iso);
+}
+
+int Cr2Decoder::getHue() {
+ if (hints.has("old_sraw_hue"))
+ return (mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x);
+
+ if (!mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x10))) {
+ return 0;
+ }
+ uint32 model_id =
+ mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x10))->getU32();
+ if (model_id >= 0x80000281 || model_id == 0x80000218 || (hints.has("force_new_sraw_hue")))
+ return ((mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x) - 1) >> 1;
+
+ return (mRaw->metadata.subsampling.y * mRaw->metadata.subsampling.x);
+}
+
+// Interpolate and convert sRaw data.
+void Cr2Decoder::sRawInterpolate() {
+ TiffEntry* wb = mRootIFD->getEntryRecursive(CANONCOLORDATA);
+ if (!wb)
+ ThrowRDE("Unable to locate WB info.");
+
+ // Offset to sRaw coefficients used to reconstruct uncorrected RGB data.
+ uint32 offset = 78;
+
+ std::array<int, 3> sraw_coeffs;
+
+ assert(wb != nullptr);
+ sraw_coeffs[0] = wb->getU16(offset + 0);
+ sraw_coeffs[1] =
+ (wb->getU16(offset + 1) + wb->getU16(offset + 2) + 1) >> 1;
+ sraw_coeffs[2] = wb->getU16(offset + 3);
+
+ if (hints.has("invert_sraw_wb")) {
+ sraw_coeffs[0] = static_cast<int>(
+ 1024.0F / (static_cast<float>(sraw_coeffs[0]) / 1024.0F));
+ sraw_coeffs[2] = static_cast<int>(
+ 1024.0F / (static_cast<float>(sraw_coeffs[2]) / 1024.0F));
+ }
+
+ /* Determine sRaw coefficients */
+ bool isOldSraw = hints.has("sraw_40d");
+ bool isNewSraw = hints.has("sraw_new");
+
+ Cr2sRawInterpolator i(mRaw, sraw_coeffs, getHue());
+
+ int version;
+ if (isOldSraw)
+ version = 0;
+ else {
+ if (isNewSraw) {
+ version = 2;
+ } else {
+ version = 1;
+ }
+ }
+
+ i.interpolate(version);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.h
new file mode 100644
index 00000000..d5a80fed
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/Cr2Decoder.h
@@ -0,0 +1,54 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+class Buffer;
+
+class Cr2Decoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ Cr2Decoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 9; }
+ RawImage decodeOldFormat();
+ RawImage decodeNewFormat();
+ void sRawInterpolate();
+ int getHue();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.cpp
new file mode 100644
index 00000000..03327377
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.cpp
@@ -0,0 +1,222 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2015-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
+*/
+
+#include "decoders/CrwDecoder.h"
+#include "common/Common.h" // for ushort16, uchar8, uint32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawspeedException.h" // for RawspeedException
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/CrwDecompressor.h" // for CrwDecompressor
+#include "io/Buffer.h" // for Buffer
+#include "metadata/Camera.h" // for Hints
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE, CFA_RED
+#include "tiff/CiffEntry.h" // for CiffEntry, CIFF_SHORT
+#include "tiff/CiffIFD.h" // for CiffIFD
+#include "tiff/CiffTag.h" // for CIFF_MAKEMODEL, CIFF_SHOT...
+#include <array> // for array
+#include <cassert> // for assert
+#include <cmath> // for abs, copysignf, expf, logf
+#include <cstring> // for memcmp, size_t
+#include <memory> // for unique_ptr
+#include <string> // for string
+#include <utility> // for move
+#include <vector> // for vector
+
+using std::vector;
+using std::string;
+using std::abs;
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool CrwDecoder::isCRW(const Buffer* input) {
+ static const std::array<char, 8> magic = {
+ {'H', 'E', 'A', 'P', 'C', 'C', 'D', 'R'}};
+ static const size_t magic_offset = 6;
+ const unsigned char* data = input->getData(magic_offset, magic.size());
+ return 0 == memcmp(data, magic.data(), magic.size());
+}
+
+CrwDecoder::CrwDecoder(std::unique_ptr<const CiffIFD> rootIFD,
+ const Buffer* file)
+ : RawDecoder(file), mRootIFD(move(rootIFD)) {}
+
+RawImage CrwDecoder::decodeRawInternal() {
+ const CiffEntry* rawData = mRootIFD->getEntry(CIFF_RAWDATA);
+ if (!rawData)
+ ThrowRDE("Couldn't find the raw data chunk");
+
+ const CiffEntry* sensorInfo = mRootIFD->getEntryRecursive(CIFF_SENSORINFO);
+ if (!sensorInfo || sensorInfo->count < 6 || sensorInfo->type != CIFF_SHORT)
+ ThrowRDE("Couldn't find image sensor info");
+
+ assert(sensorInfo != nullptr);
+ uint32 width = sensorInfo->getU16(1);
+ uint32 height = sensorInfo->getU16(2);
+ mRaw->dim = iPoint2D(width, height);
+
+ const CiffEntry* decTable = mRootIFD->getEntryRecursive(CIFF_DECODERTABLE);
+ if (!decTable || decTable->type != CIFF_LONG)
+ ThrowRDE("Couldn't find decoder table");
+
+ assert(decTable != nullptr);
+ uint32 dec_table = decTable->getU32();
+
+ bool lowbits = ! hints.has("no_decompressed_lowbits");
+
+ CrwDecompressor c(mRaw, dec_table, lowbits, rawData->getData());
+ mRaw->createData();
+ c.decompress();
+
+ return mRaw;
+}
+
+void CrwDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ vector<const CiffIFD*> data = mRootIFD->getIFDsWithTag(CIFF_MAKEMODEL);
+ if (data.empty())
+ ThrowRDE("Model name not found");
+ vector<string> makemodel = data[0]->getEntry(CIFF_MAKEMODEL)->getStrings();
+ if (makemodel.size() < 2)
+ ThrowRDE("wrong number of strings for make/model");
+ string make = makemodel[0];
+ string model = makemodel[1];
+
+ this->checkCameraSupported(meta, make, model, "");
+}
+
+// based on exiftool's Image::ExifTool::Canon::CanonEv
+float __attribute__((const)) CrwDecoder::canonEv(const long in) {
+ // remove sign
+ long val = abs(in);
+ // remove fraction
+ long frac = val & 0x1f;
+ val -= frac;
+ // convert 1/3 (0x0c) and 2/3 (0x14) codes
+ if (frac == 0x0c) {
+ frac = 32.0F / 3;
+ }
+ else if (frac == 0x14) {
+ frac = 64.0F / 3;
+ }
+ return copysignf((val + frac) / 32.0F, in);
+}
+
+void CrwDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+ vector<const CiffIFD*> data = mRootIFD->getIFDsWithTag(CIFF_MAKEMODEL);
+ if (data.empty())
+ ThrowRDE("Model name not found");
+ vector<string> makemodel = data[0]->getEntry(CIFF_MAKEMODEL)->getStrings();
+ if (makemodel.size() < 2)
+ ThrowRDE("wrong number of strings for make/model");
+ string make = makemodel[0];
+ string model = makemodel[1];
+ string mode;
+
+ if (mRootIFD->hasEntryRecursive(CIFF_SHOTINFO)) {
+ const CiffEntry* shot_info = mRootIFD->getEntryRecursive(CIFF_SHOTINFO);
+ if (shot_info->type == CIFF_SHORT && shot_info->count >= 2) {
+ // os << exp(canonEv(value.toLong()) * log(2.0)) * 100.0 / 32.0;
+ ushort16 iso_index = shot_info->getU16(2);
+ iso = expf(canonEv(static_cast<long>(iso_index)) * logf(2.0)) * 100.0F /
+ 32.0F;
+ }
+ }
+
+ // Fetch the white balance
+ try{
+ if (mRootIFD->hasEntryRecursive(static_cast<CiffTag>(0x0032))) {
+ const CiffEntry* wb =
+ mRootIFD->getEntryRecursive(static_cast<CiffTag>(0x0032));
+ if (wb->type == CIFF_BYTE && wb->count == 768) {
+ // We're in a D30 file, values are RGGB
+ // This will probably not get used anyway as a 0x102c tag should exist
+ std::array<uchar8, 4> wbMuls{{wb->getByte(72), wb->getByte(73),
+ wb->getByte(74), wb->getByte(75)}};
+ for (const auto& mul : wbMuls) {
+ if (0 == mul)
+ ThrowRDE("WB coeffient is zero!");
+ }
+
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(1024.0 / wbMuls[0]);
+ mRaw->metadata.wbCoeffs[1] =
+ static_cast<float>((1024.0 / wbMuls[1]) + (1024.0 / wbMuls[2])) /
+ 2.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(1024.0 / wbMuls[3]);
+ } else if (wb->type == CIFF_BYTE && wb->count > 768) { // Other G series and S series cameras
+ // correct offset for most cameras
+ int offset = hints.get("wb_offset", 120);
+
+ std::array<ushort16, 2> key = {{0x410, 0x45f3}};
+ if (! hints.has("wb_mangle"))
+ key[0] = key[1] = 0;
+
+ offset /= 2;
+ mRaw->metadata.wbCoeffs[0] =
+ static_cast<float>(wb->getU16(offset + 1) ^ key[1]);
+ mRaw->metadata.wbCoeffs[1] =
+ static_cast<float>(wb->getU16(offset + 0) ^ key[0]);
+ mRaw->metadata.wbCoeffs[2] =
+ static_cast<float>(wb->getU16(offset + 2) ^ key[0]);
+ }
+ }
+ if (mRootIFD->hasEntryRecursive(static_cast<CiffTag>(0x102c))) {
+ const CiffEntry* entry =
+ mRootIFD->getEntryRecursive(static_cast<CiffTag>(0x102c));
+ if (entry->type == CIFF_SHORT && entry->getU16() > 512) {
+ // G1/Pro90 CYGM pattern
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(entry->getU16(62));
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(entry->getU16(63));
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(entry->getU16(60));
+ mRaw->metadata.wbCoeffs[3] = static_cast<float>(entry->getU16(61));
+ } else if (entry->type == CIFF_SHORT) {
+ /* G2, S30, S40 */
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(entry->getU16(51));
+ mRaw->metadata.wbCoeffs[1] = (static_cast<float>(entry->getU16(50)) +
+ static_cast<float>(entry->getU16(53))) /
+ 2.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(entry->getU16(52));
+ }
+ }
+ if (mRootIFD->hasEntryRecursive(CIFF_SHOTINFO) && mRootIFD->hasEntryRecursive(CIFF_WHITEBALANCE)) {
+ const CiffEntry* shot_info = mRootIFD->getEntryRecursive(CIFF_SHOTINFO);
+ ushort16 wb_index = shot_info->getU16(7);
+ const CiffEntry* wb_data = mRootIFD->getEntryRecursive(CIFF_WHITEBALANCE);
+ /* CANON EOS D60, CANON EOS 10D, CANON EOS 300D */
+ if (wb_index > 9)
+ ThrowRDE("Invalid white balance index");
+ int wb_offset = 1 + ("0134567028"[wb_index]-'0') * 4;
+ mRaw->metadata.wbCoeffs[0] = wb_data->getU16(wb_offset + 0);
+ mRaw->metadata.wbCoeffs[1] = wb_data->getU16(wb_offset + 1);
+ mRaw->metadata.wbCoeffs[2] = wb_data->getU16(wb_offset + 3);
+ }
+ } catch (RawspeedException& e) {
+ mRaw->setError(e.what());
+ // We caught an exception reading WB, just ignore it
+ }
+
+ setMetaData(meta, make, model, mode, iso);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.h
new file mode 100644
index 00000000..b469799d
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/CrwDecoder.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/CiffIFD.h" // for CiffIFD
+#include <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class CrwDecoder final : public RawDecoder {
+ std::unique_ptr<const CiffIFD> mRootIFD;
+
+public:
+ CrwDecoder(std::unique_ptr<const CiffIFD> rootIFD, const Buffer* file);
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ static bool isCRW(const Buffer* input);
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+ static float canonEv(long in);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.cpp
new file mode 100644
index 00000000..69cfe4e7
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.cpp
@@ -0,0 +1,117 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/DcrDecoder.h"
+#include "common/NORangesSet.h" // for set
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/KodakDecompressor.h" // for KodakDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_SHORT
+#include "tiff/TiffIFD.h" // for TiffRootIFD, TiffID
+#include "tiff/TiffTag.h" // for COMPRESSION, KODAK_IFD
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool DcrDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "Kodak";
+}
+
+void DcrDecoder::checkImageDimensions() {
+ if (width > 4516 || height > 3012)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+}
+
+RawImage DcrDecoder::decodeRawInternal() {
+ SimpleTiffDecoder::prepareForRawDecoding();
+
+ ByteStream input(mFile, off);
+
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ if (65000 != compression)
+ ThrowRDE("Unsupported compression %d", compression);
+
+ TiffEntry* ifdoffset = mRootIFD->getEntryRecursive(KODAK_IFD);
+ if (!ifdoffset)
+ ThrowRDE("Couldn't find the Kodak IFD offset");
+
+ NORangesSet<Buffer> ifds;
+
+ assert(ifdoffset != nullptr);
+ TiffRootIFD kodakifd(nullptr, &ifds, ifdoffset->getRootIfdData(),
+ ifdoffset->getU32());
+
+ TiffEntry* linearization = kodakifd.getEntryRecursive(KODAK_LINEARIZATION);
+ if (!linearization ||
+ !(linearization->count == 1024 || linearization->count == 4096) ||
+ linearization->type != TIFF_SHORT)
+ ThrowRDE("Couldn't find the linearization table");
+
+ assert(linearization != nullptr);
+ auto linTable = linearization->getU16Array(linearization->count);
+
+ RawImageCurveGuard curveHandler(&mRaw, linTable, uncorrectedRawValues);
+
+ // FIXME: dcraw does all sorts of crazy things besides this to fetch
+ // WB from what appear to be presets and calculate it in weird ways
+ // The only file I have only uses this method, if anybody careas look
+ // in dcraw.c parse_kodak_ifd() for all that weirdness
+ TiffEntry* blob = kodakifd.getEntryRecursive(static_cast<TiffTag>(0x03fd));
+ if (blob && blob->count == 72) {
+ for (auto i = 0U; i < 3; i++) {
+ const auto mul = blob->getU16(20 + i);
+ if (0 == mul)
+ ThrowRDE("WB coeffient is zero!");
+ mRaw->metadata.wbCoeffs[i] = 2048.0F / mul;
+ }
+ }
+
+ const int bps = [CurveSize = linearization->count]() -> int {
+ switch (CurveSize) {
+ case 1024:
+ return 10;
+ case 4096:
+ return 12;
+ }
+ __builtin_unreachable();
+ }();
+
+ KodakDecompressor k(mRaw, input, bps, uncorrectedRawValues);
+ k.decompress();
+
+ return mRaw;
+}
+
+void DcrDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.h
new file mode 100644
index 00000000..82cf9426
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DcrDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/SimpleTiffDecoder.h" // for SimpleTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class DcrDecoder final : public SimpleTiffDecoder {
+ void checkImageDimensions() override;
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ DcrDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : SimpleTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.cpp
new file mode 100644
index 00000000..c91a6936
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.cpp
@@ -0,0 +1,77 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+
+ 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/DcsDecoder.h"
+#include "decoders/RawDecoderException.h" // for RawDecoderExcept...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "tiff/TiffEntry.h" // for TiffEntry, TiffD...
+#include "tiff/TiffIFD.h" // for TiffRootIFD
+#include "tiff/TiffTag.h" // for TiffTag::GRAYRES...
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool DcsDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "KODAK";
+}
+
+void DcsDecoder::checkImageDimensions() {
+ if (width > 3072 || height > 2048)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+}
+
+RawImage DcsDecoder::decodeRawInternal() {
+ SimpleTiffDecoder::prepareForRawDecoding();
+
+ TiffEntry *linearization = mRootIFD->getEntryRecursive(GRAYRESPONSECURVE);
+ if (!linearization || linearization->count != 256 || linearization->type != TIFF_SHORT)
+ ThrowRDE("Couldn't find the linearization table");
+
+ assert(linearization != nullptr);
+ auto table = linearization->getU16Array(256);
+
+ RawImageCurveGuard curveHandler(&mRaw, table, uncorrectedRawValues);
+
+ UncompressedDecompressor u(*mFile, off, c2, mRaw);
+
+ if (uncorrectedRawValues)
+ u.decode8BitRaw<true>(width, height);
+ else
+ u.decode8BitRaw<false>(width, height);
+
+ return mRaw;
+}
+
+void DcsDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.h
new file mode 100644
index 00000000..c6ab0fa6
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DcsDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/SimpleTiffDecoder.h" // for SimpleTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class DcsDecoder final : public SimpleTiffDecoder {
+ void checkImageDimensions() override;
+
+public:
+ static bool __attribute__((pure))
+ isAppropriateDecoder(const TiffRootIFD* rootIFD, const Buffer* file);
+ DcsDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : SimpleTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.cpp
new file mode 100644
index 00000000..70a459f4
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.cpp
@@ -0,0 +1,784 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h" // for HAVE_JPEG, HAVE_ZLIB
+#include "decoders/DngDecoder.h"
+#include "common/Common.h" // for uint32, roundUpDi...
+#include "common/DngOpcodes.h" // for DngOpcodes
+#include "common/NORangesSet.h" // for set
+#include "common/Point.h" // for iPoint2D, iRectan...
+#include "common/RawspeedException.h" // for RawspeedException
+#include "decoders/RawDecoderException.h" // for ThrowRDE, RawDeco...
+#include "decompressors/AbstractDngDecompressor.h" // for DngSliceElement
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "metadata/BlackArea.h" // for BlackArea
+#include "metadata/Camera.h" // for Camera
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "metadata/ColorFilterArray.h" // for CFAColor, ColorFi...
+#include "parsers/TiffParserException.h" // for ThrowTPE
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_LONG
+#include "tiff/TiffIFD.h" // for TiffIFD, TiffRootIFD
+#include "tiff/TiffTag.h" // for ACTIVEAREA, TILEO...
+#include <algorithm> // for any_of
+#include <array> // for array, array<>::v...
+#include <cassert> // for assert
+#include <limits> // for numeric_limits
+#include <map> // for map
+#include <memory> // for unique_ptr
+#include <stdexcept> // for out_of_range
+#include <string> // for string, operator+
+#include <utility> // for move, pair
+#include <vector> // for vector, allocator
+
+using std::vector;
+using std::map;
+using std::string;
+
+namespace rawspeed {
+
+bool __attribute__((pure))
+DngDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ return rootIFD->hasEntryRecursive(DNGVERSION);
+}
+
+DngDecoder::DngDecoder(TiffRootIFDOwner&& rootIFD, const Buffer* file)
+ : AbstractTiffDecoder(move(rootIFD), file) {
+ if (!mRootIFD->hasEntryRecursive(DNGVERSION))
+ ThrowRDE("DNG, but version tag is missing. Will not guess.");
+
+ const uchar8* v = mRootIFD->getEntryRecursive(DNGVERSION)->getData(4);
+
+ if (v[0] != 1)
+ ThrowRDE("Not a supported DNG image format: v%u.%u.%u.%u", (int)v[0], (int)v[1], (int)v[2], (int)v[3]);
+// if (v[1] > 4)
+// ThrowRDE("Not a supported DNG image format: v%u.%u.%u.%u", (int)v[0], (int)v[1], (int)v[2], (int)v[3]);
+
+ if ((v[0] <= 1) && (v[1] < 1)) // Prior to v1.1.xxx fix LJPEG encoding bug
+ mFixLjpeg = true;
+ else
+ mFixLjpeg = false;
+}
+
+void DngDecoder::dropUnsuportedChunks(std::vector<const TiffIFD*>* data) {
+ for (auto i = data->begin(); i != data->end();) {
+ const auto& ifd = *i;
+
+ int comp = ifd->getEntry(COMPRESSION)->getU16();
+ bool isSubsampled = false;
+ bool isAlpha = false;
+
+ if (ifd->hasEntry(NEWSUBFILETYPE) &&
+ ifd->getEntry(NEWSUBFILETYPE)->isInt()) {
+ const uint32 NewSubFileType = (*i)->getEntry(NEWSUBFILETYPE)->getU32();
+
+ // bit 0 is on if image is subsampled.
+ // the value itself can be either 1, or 0x10001.
+ // or 5 for "Transparency information for subsampled raw images"
+ isSubsampled = NewSubFileType & (1 << 0);
+
+ // bit 2 is on if image contains transparency information.
+ // the value itself can be either 4 or 5
+ isAlpha = NewSubFileType & (1 << 2);
+ }
+
+ // normal raw?
+ bool supported = !isSubsampled && !isAlpha;
+
+ switch (comp) {
+ case 1: // uncompressed
+ case 7: // lossless JPEG
+#ifdef HAVE_ZLIB
+ case 8: // deflate
+#endif
+ case 9: // VC-5 as used by GoPro
+#ifdef HAVE_JPEG
+ case 0x884c: // lossy JPEG
+#endif
+ // no change, if supported, then is still supported.
+ break;
+
+#ifndef HAVE_ZLIB
+ case 8: // deflate
+#pragma message \
+ "ZLIB is not present! Deflate compression will not be supported!"
+ writeLog(DEBUG_PRIO_WARNING, "DNG Decoder: found Deflate-encoded chunk, "
+ "but the deflate support was disabled at "
+ "build!");
+ [[clang::fallthrough]];
+#endif
+#ifndef HAVE_JPEG
+ case 0x884c: // lossy JPEG
+#pragma message \
+ "JPEG is not present! Lossy JPEG compression will not be supported!"
+ writeLog(DEBUG_PRIO_WARNING, "DNG Decoder: found lossy JPEG-encoded "
+ "chunk, but the jpeg support was "
+ "disabled at build!");
+ [[clang::fallthrough]];
+#endif
+ default:
+ supported = false;
+ break;
+ }
+
+ if (supported)
+ ++i;
+ else
+ i = data->erase(i);
+ }
+}
+
+void DngDecoder::parseCFA(const TiffIFD* raw) {
+
+ // Check if layout is OK, if present
+ if (raw->hasEntry(CFALAYOUT) && raw->getEntry(CFALAYOUT)->getU16() != 1)
+ ThrowRDE("Unsupported CFA Layout.");
+
+ TiffEntry* cfadim = raw->getEntry(CFAREPEATPATTERNDIM);
+ if (cfadim->count != 2)
+ ThrowRDE("Couldn't read CFA pattern dimension");
+
+ // Does NOT contain dimensions as some documents state
+ TiffEntry* cPat = raw->getEntry(CFAPATTERN);
+
+ iPoint2D cfaSize(cfadim->getU32(1), cfadim->getU32(0));
+ if (cfaSize.area() != cPat->count) {
+ ThrowRDE("CFA pattern dimension and pattern count does not "
+ "match: %d.",
+ cPat->count);
+ }
+
+ mRaw->cfa.setSize(cfaSize);
+
+ static const map<uint32, CFAColor> int2enum = {
+ {0, CFA_RED}, {1, CFA_GREEN}, {2, CFA_BLUE}, {3, CFA_CYAN},
+ {4, CFA_MAGENTA}, {5, CFA_YELLOW}, {6, CFA_WHITE},
+ };
+
+ for (int y = 0; y < cfaSize.y; y++) {
+ for (int x = 0; x < cfaSize.x; x++) {
+ uint32 c1 = cPat->getByte(x + y * cfaSize.x);
+ CFAColor c2 = CFA_UNKNOWN;
+
+ try {
+ c2 = int2enum.at(c1);
+ } catch (std::out_of_range&) {
+ ThrowRDE("Unsupported CFA Color: %u", c1);
+ }
+
+ mRaw->cfa.setColorAt(iPoint2D(x, y), c2);
+ }
+ }
+
+ // the cfa is specified relative to the ActiveArea. we want it relative (0,0)
+ // Since in handleMetadata(), in subFrame() we unconditionally shift CFA by
+ // activearea+DefaultCropOrigin; here we need to undo the 'ACTIVEAREA' part.
+ if (!raw->hasEntry(ACTIVEAREA))
+ return;
+
+ TiffEntry* active_area = raw->getEntry(ACTIVEAREA);
+ if (active_area->count != 4)
+ ThrowRDE("active area has %d values instead of 4", active_area->count);
+
+ const auto aa = active_area->getFloatArray(2);
+ if (std::any_of(aa.cbegin(), aa.cend(), [](const auto v) {
+ return v < std::numeric_limits<iPoint2D::value_type>::min() ||
+ v > std::numeric_limits<iPoint2D::value_type>::max();
+ }))
+ ThrowRDE("Error decoding active area");
+
+ mRaw->cfa.shiftLeft(aa[1]);
+ mRaw->cfa.shiftDown(aa[0]);
+}
+
+DngTilingDescription DngDecoder::getTilingDescription(const TiffIFD* raw) {
+ if (raw->hasEntry(TILEOFFSETS)) {
+ const uint32 tilew = raw->getEntry(TILEWIDTH)->getU32();
+ const uint32 tileh = raw->getEntry(TILELENGTH)->getU32();
+
+ if (!(tilew > 0 && tileh > 0))
+ ThrowRDE("Invalid tile size: (%u, %u)", tilew, tileh);
+
+ assert(tilew > 0);
+ const uint32 tilesX = roundUpDivision(mRaw->dim.x, tilew);
+ if (!tilesX)
+ ThrowRDE("Zero tiles horizontally");
+
+ assert(tileh > 0);
+ const uint32 tilesY = roundUpDivision(mRaw->dim.y, tileh);
+ if (!tilesY)
+ ThrowRDE("Zero tiles vertically");
+
+ TiffEntry* offsets = raw->getEntry(TILEOFFSETS);
+ TiffEntry* counts = raw->getEntry(TILEBYTECOUNTS);
+ if (offsets->count != counts->count) {
+ ThrowRDE("Tile count mismatch: offsets:%u count:%u", offsets->count,
+ counts->count);
+ }
+
+ // tilesX * tilesY may overflow, but division is fine, so let's do that.
+ if (offsets->count / tilesX != tilesY ||
+ offsets->count / tilesY != tilesX) {
+ ThrowRDE("Tile X/Y count mismatch: total:%u X:%u, Y:%u", offsets->count,
+ tilesX, tilesY);
+ }
+
+ return {mRaw->dim, tilew, tileh};
+ }
+
+ // Strips
+ TiffEntry* offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry* counts = raw->getEntry(STRIPBYTECOUNTS);
+
+ if (counts->count != offsets->count) {
+ ThrowRDE("Byte count number does not match strip size: "
+ "count:%u, stips:%u ",
+ counts->count, offsets->count);
+ }
+
+ uint32 yPerSlice = raw->hasEntry(ROWSPERSTRIP)
+ ? raw->getEntry(ROWSPERSTRIP)->getU32()
+ : mRaw->dim.y;
+
+ if (yPerSlice == 0 || yPerSlice > static_cast<uint32>(mRaw->dim.y) ||
+ roundUpDivision(mRaw->dim.y, yPerSlice) != counts->count) {
+ ThrowRDE("Invalid y per slice %u or strip count %u (height = %u)",
+ yPerSlice, counts->count, mRaw->dim.y);
+ }
+
+ return {mRaw->dim, static_cast<uint32>(mRaw->dim.x), yPerSlice};
+}
+
+void DngDecoder::decodeData(const TiffIFD* raw, uint32 sample_format) {
+ if (compression == 8 && sample_format != 3) {
+ ThrowRDE("Only float format is supported for "
+ "deflate-compressed data.");
+ } else if ((compression == 7 || compression == 0x884c) &&
+ sample_format != 1) {
+ ThrowRDE("Only 16 bit unsigned data supported for "
+ "JPEG-compressed data.");
+ }
+
+ uint32 predictor = ~0U;
+ if (raw->hasEntry(PREDICTOR))
+ predictor = raw->getEntry(PREDICTOR)->getU32();
+
+ // Some decompressors (such as VC5) may depend on the white point
+ if (raw->hasEntry(WHITELEVEL)) {
+ TiffEntry* whitelevel = raw->getEntry(WHITELEVEL);
+ if (whitelevel->isInt())
+ mRaw->whitePoint = whitelevel->getU32();
+ }
+
+ AbstractDngDecompressor slices(mRaw, getTilingDescription(raw), compression,
+ mFixLjpeg, bps, predictor);
+
+ slices.slices.reserve(slices.dsc.numTiles);
+
+ TiffEntry* offsets = nullptr;
+ TiffEntry* counts = nullptr;
+ if (raw->hasEntry(TILEOFFSETS)) {
+ offsets = raw->getEntry(TILEOFFSETS);
+ counts = raw->getEntry(TILEBYTECOUNTS);
+ } else { // Strips
+ offsets = raw->getEntry(STRIPOFFSETS);
+ counts = raw->getEntry(STRIPBYTECOUNTS);
+ }
+ assert(slices.dsc.numTiles == offsets->count);
+ assert(slices.dsc.numTiles == counts->count);
+
+ NORangesSet<Buffer> tilesLegality;
+ for (auto n = 0U; n < slices.dsc.numTiles; n++) {
+ const auto offset = offsets->getU32(n);
+ const auto count = counts->getU32(n);
+
+ if (count < 1)
+ ThrowRDE("Tile %u is empty", n);
+
+ ByteStream bs(mFile->getSubView(offset, count), 0,
+ mRootIFD->rootBuffer.getByteOrder());
+
+ if (!tilesLegality.emplace(bs).second)
+ ThrowTPE("Two tiles overlap. Raw corrupt!");
+
+ slices.slices.emplace_back(slices.dsc, n, bs);
+ }
+
+ assert(slices.slices.size() == slices.dsc.numTiles);
+ if (slices.slices.empty())
+ ThrowRDE("No valid slices found.");
+
+ // FIXME: should we sort the tiles, to linearize the input reading?
+
+ mRaw->createData();
+
+ slices.decompress();
+}
+
+RawImage DngDecoder::decodeRawInternal() {
+ vector<const TiffIFD*> data = mRootIFD->getIFDsWithTag(COMPRESSION);
+
+ if (data.empty())
+ ThrowRDE("No image data found");
+
+ dropUnsuportedChunks(&data);
+
+ if (data.empty())
+ ThrowRDE("No RAW chunks found");
+
+ if (data.size() > 1) {
+ writeLog(DEBUG_PRIO_EXTRA, "Multiple RAW chunks found - using first only!");
+ }
+
+ const TiffIFD* raw = data[0];
+
+ bps = raw->getEntry(BITSPERSAMPLE)->getU32();
+ if (bps < 1 || bps > 32)
+ ThrowRDE("Unsupported bit per sample count: %u.", bps);
+
+ uint32 sample_format = 1;
+ if (raw->hasEntry(SAMPLEFORMAT))
+ sample_format = raw->getEntry(SAMPLEFORMAT)->getU32();
+
+ compression = raw->getEntry(COMPRESSION)->getU16();
+
+ switch (sample_format) {
+ case 1:
+ mRaw = RawImage::create(TYPE_USHORT16);
+ break;
+ case 3:
+ mRaw = RawImage::create(TYPE_FLOAT32);
+ break;
+ default:
+ ThrowRDE("Only 16 bit unsigned or float point data supported. Sample "
+ "format %u is not supported.",
+ sample_format);
+ }
+
+ mRaw->isCFA = (raw->getEntry(PHOTOMETRICINTERPRETATION)->getU16() == 32803);
+
+ if (mRaw->isCFA)
+ writeLog(DEBUG_PRIO_EXTRA, "This is a CFA image");
+ else {
+ writeLog(DEBUG_PRIO_EXTRA, "This is NOT a CFA image");
+ }
+
+ if (sample_format == 1 && bps > 16)
+ ThrowRDE("Integer precision larger than 16 bits currently not supported.");
+
+ if (sample_format == 3 && bps != 32 && compression != 8)
+ ThrowRDE("Uncompressed float point must be 32 bits per sample.");
+
+ mRaw->dim.x = raw->getEntry(IMAGEWIDTH)->getU32();
+ mRaw->dim.y = raw->getEntry(IMAGELENGTH)->getU32();
+
+ if (!mRaw->dim.hasPositiveArea())
+ ThrowRDE("Image has zero size");
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // Yeah, sure, here it would be just dumb to leave this for production :)
+ if (mRaw->dim.x > 7424 || mRaw->dim.y > 5552) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+#endif
+
+ if (mRaw->isCFA)
+ parseCFA(raw);
+
+ uint32 cpp = raw->getEntry(SAMPLESPERPIXEL)->getU32();
+
+ if (cpp < 1 || cpp > 4)
+ ThrowRDE("Unsupported samples per pixel count: %u.", cpp);
+
+ mRaw->setCpp(cpp);
+
+ // Now load the image
+ decodeData(raw, sample_format);
+
+ handleMetadata(raw);
+
+ return mRaw;
+}
+
+void DngDecoder::handleMetadata(const TiffIFD* raw) {
+ // Crop
+ if (raw->hasEntry(ACTIVEAREA)) {
+ TiffEntry *active_area = raw->getEntry(ACTIVEAREA);
+ if (active_area->count != 4)
+ ThrowRDE("active area has %d values instead of 4", active_area->count);
+
+ const iRectangle2D fullImage(0, 0, mRaw->dim.x, mRaw->dim.y);
+
+ const auto corners = active_area->getU32Array(4);
+ const iPoint2D topLeft(corners[1], corners[0]);
+ const iPoint2D bottomRight(corners[3], corners[2]);
+
+ if (!(fullImage.isPointInsideInclusive(topLeft) &&
+ fullImage.isPointInsideInclusive(bottomRight) &&
+ bottomRight >= topLeft)) {
+ ThrowRDE("Rectangle (%u, %u, %u, %u) not inside image (%u, %u, %u, %u).",
+ topLeft.x, topLeft.y, bottomRight.x, bottomRight.y,
+ fullImage.getTopLeft().x, fullImage.getTopLeft().y,
+ fullImage.getBottomRight().x, fullImage.getBottomRight().y);
+ }
+
+ iRectangle2D crop;
+ crop.setTopLeft(topLeft);
+ crop.setBottomRightAbsolute(bottomRight);
+ assert(fullImage.isThisInside(fullImage));
+
+ mRaw->subFrame(crop);
+ }
+
+ if (raw->hasEntry(DEFAULTCROPORIGIN) && raw->hasEntry(DEFAULTCROPSIZE)) {
+ iRectangle2D cropped(0, 0, mRaw->dim.x, mRaw->dim.y);
+ TiffEntry *origin_entry = raw->getEntry(DEFAULTCROPORIGIN);
+ TiffEntry *size_entry = raw->getEntry(DEFAULTCROPSIZE);
+
+ /* Read crop position (sometimes is rational so use float) */
+ const auto tl = origin_entry->getFloatArray(2);
+ if (std::any_of(tl.cbegin(), tl.cend(), [](const auto v) {
+ return v < std::numeric_limits<iPoint2D::value_type>::min() ||
+ v > std::numeric_limits<iPoint2D::value_type>::max();
+ }))
+ ThrowRDE("Error decoding default crop origin");
+
+ iPoint2D cropOrigin(tl[0], tl[1]);
+ if (cropped.isPointInsideInclusive(cropOrigin))
+ cropped = iRectangle2D(cropOrigin, {0, 0});
+
+ cropped.dim = mRaw->dim - cropped.pos;
+
+ /* Read size (sometimes is rational so use float) */
+ const auto sz = size_entry->getFloatArray(2);
+ if (std::any_of(sz.cbegin(), sz.cend(), [](const auto v) {
+ return v < std::numeric_limits<iPoint2D::value_type>::min() ||
+ v > std::numeric_limits<iPoint2D::value_type>::max();
+ }))
+ ThrowRDE("Error decoding default crop size");
+
+ iPoint2D size(sz[0], sz[1]);
+ if ((size + cropped.pos).isThisInside(mRaw->dim))
+ cropped.dim = size;
+
+ if (!cropped.hasPositiveArea())
+ ThrowRDE("No positive crop area");
+
+ mRaw->subFrame(cropped);
+ }
+ if (mRaw->dim.area() <= 0)
+ ThrowRDE("No image left after crop");
+
+ // Apply stage 1 opcodes
+ if (applyStage1DngOpcodes && raw->hasEntry(OPCODELIST1)) {
+ try {
+ TiffEntry* opcodes = raw->getEntry(OPCODELIST1);
+ // The entry might exist, but it might be empty, which means no opcodes
+ if (opcodes->count > 0) {
+ DngOpcodes codes(mRaw, opcodes);
+ codes.applyOpCodes(mRaw);
+ }
+ } catch (RawDecoderException& e) {
+ // We push back errors from the opcode parser, since the image may still
+ // be usable
+ mRaw->setError(e.what());
+ }
+ }
+
+ // Linearization
+ if (raw->hasEntry(LINEARIZATIONTABLE) &&
+ raw->getEntry(LINEARIZATIONTABLE)->count > 0) {
+ TiffEntry *lintable = raw->getEntry(LINEARIZATIONTABLE);
+ auto table = lintable->getU16Array(lintable->count);
+ RawImageCurveGuard curveHandler(&mRaw, table, uncorrectedRawValues);
+ if (!uncorrectedRawValues)
+ mRaw->sixteenBitLookup();
+ }
+
+ if (mRaw->getDataType() == TYPE_USHORT16) {
+ // Default white level is (2 ** BitsPerSample) - 1
+ mRaw->whitePoint = (1UL << bps) - 1UL;
+ } else if (mRaw->getDataType() == TYPE_FLOAT32) {
+ // Default white level is 1.0f. But we can't represent that here.
+ mRaw->whitePoint = 65535;
+ }
+
+ if (raw->hasEntry(WHITELEVEL)) {
+ TiffEntry *whitelevel = raw->getEntry(WHITELEVEL);
+ if (whitelevel->isInt())
+ mRaw->whitePoint = whitelevel->getU32();
+ }
+ // Set black
+ setBlack(raw);
+
+ // Apply opcodes to lossy DNG
+ if (compression == 0x884c && !uncorrectedRawValues &&
+ raw->hasEntry(OPCODELIST2)) {
+ // We must apply black/white scaling
+ mRaw->scaleBlackWhite();
+
+ // Apply stage 2 codes
+ try {
+ DngOpcodes codes(mRaw, raw->getEntry(OPCODELIST2));
+ codes.applyOpCodes(mRaw);
+ } catch (RawDecoderException& e) {
+ // We push back errors from the opcode parser, since the image may still
+ // be usable
+ mRaw->setError(e.what());
+ }
+ mRaw->blackAreas.clear();
+ mRaw->blackLevel = 0;
+ mRaw->blackLevelSeparate[0] = mRaw->blackLevelSeparate[1] =
+ mRaw->blackLevelSeparate[2] = mRaw->blackLevelSeparate[3] = 0;
+ mRaw->whitePoint = 65535;
+ }
+}
+
+void DngDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ mRaw->metadata.isoSpeed = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ TiffID id;
+
+ try {
+ id = mRootIFD->getID();
+ } catch (RawspeedException& e) {
+ mRaw->setError(e.what());
+ // not all dngs have MAKE/MODEL entries,
+ // will be dealt with by using UNIQUECAMERAMODEL below
+ }
+
+ // Set the make and model
+ mRaw->metadata.make = id.make;
+ mRaw->metadata.model = id.model;
+
+ const Camera* cam = meta->getCamera(id.make, id.model, "dng");
+ if (!cam) //Also look for non-DNG cameras in case it's a converted file
+ cam = meta->getCamera(id.make, id.model, "");
+ if (!cam) // Worst case scenario, look for any such camera.
+ cam = meta->getCamera(id.make, id.model);
+ if (cam) {
+ mRaw->metadata.canonical_make = cam->canonical_make;
+ mRaw->metadata.canonical_model = cam->canonical_model;
+ mRaw->metadata.canonical_alias = cam->canonical_alias;
+ mRaw->metadata.canonical_id = cam->canonical_id;
+ } else {
+ mRaw->metadata.canonical_make = id.make;
+ mRaw->metadata.canonical_model = mRaw->metadata.canonical_alias = id.model;
+ if (mRootIFD->hasEntryRecursive(UNIQUECAMERAMODEL)) {
+ mRaw->metadata.canonical_id = mRootIFD->getEntryRecursive(UNIQUECAMERAMODEL)->getString();
+ } else {
+ mRaw->metadata.canonical_id = id.make + " " + id.model;
+ }
+ }
+
+ // Fetch the white balance
+ if (mRootIFD->hasEntryRecursive(ASSHOTNEUTRAL)) {
+ TiffEntry* as_shot_neutral = mRootIFD->getEntryRecursive(ASSHOTNEUTRAL);
+ if (as_shot_neutral->count == 3) {
+ for (uint32 i = 0; i < 3; i++) {
+ float c = as_shot_neutral->getFloat(i);
+ mRaw->metadata.wbCoeffs[i] = (c > 0.0F) ? (1.0F / c) : 0.0F;
+ }
+ }
+ } else if (mRootIFD->hasEntryRecursive(ASSHOTWHITEXY)) {
+ TiffEntry* as_shot_white_xy = mRootIFD->getEntryRecursive(ASSHOTWHITEXY);
+ if (as_shot_white_xy->count == 2) {
+ mRaw->metadata.wbCoeffs[0] = as_shot_white_xy->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = as_shot_white_xy->getFloat(1);
+ mRaw->metadata.wbCoeffs[2] =
+ 1 - mRaw->metadata.wbCoeffs[0] - mRaw->metadata.wbCoeffs[1];
+
+ const std::array<float, 3> d65_white = {{0.950456, 1, 1.088754}};
+ for (uint32 i = 0; i < 3; i++)
+ mRaw->metadata.wbCoeffs[i] /= d65_white[i];
+ }
+ }
+}
+
+/* DNG Images are assumed to be decodable unless explicitly set so */
+void DngDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ // We set this, since DNG's are not explicitly added.
+ failOnUnknown = false;
+
+ if (!(mRootIFD->hasEntryRecursive(MAKE) && mRootIFD->hasEntryRecursive(MODEL))) {
+ // Check "Unique Camera Model" instead, uses this for both make + model.
+ if (mRootIFD->hasEntryRecursive(UNIQUECAMERAMODEL)) {
+ string unique = mRootIFD->getEntryRecursive(UNIQUECAMERAMODEL)->getString();
+ checkCameraSupported(meta, {unique, unique}, "dng");
+ return;
+ }
+ // If we don't have make/model we cannot tell, but still assume yes.
+ return;
+ }
+
+ checkCameraSupported(meta, mRootIFD->getID(), "dng");
+}
+
+/* Decodes DNG masked areas into blackareas in the image */
+bool DngDecoder::decodeMaskedAreas(const TiffIFD* raw) {
+ TiffEntry *masked = raw->getEntry(MASKEDAREAS);
+
+ if (masked->type != TIFF_SHORT && masked->type != TIFF_LONG)
+ return false;
+
+ uint32 nrects = masked->count/4;
+ if (0 == nrects)
+ return false;
+
+ /* Since we may both have short or int, copy it to int array. */
+ auto rects = masked->getU32Array(nrects*4);
+
+ const iRectangle2D fullImage(0, 0, mRaw->getUncroppedDim().x,
+ mRaw->getUncroppedDim().y);
+ const iPoint2D top = mRaw->getCropOffset();
+
+ for (uint32 i = 0; i < nrects; i++) {
+ iPoint2D topleft = iPoint2D(rects[i * 4UL + 1UL], rects[i * 4UL]);
+ iPoint2D bottomright = iPoint2D(rects[i * 4UL + 3UL], rects[i * 4UL + 2UL]);
+
+ if (!(fullImage.isPointInsideInclusive(topleft) &&
+ fullImage.isPointInsideInclusive(bottomright) &&
+ (topleft < bottomright)))
+ ThrowRDE("Bad masked area.");
+
+ // Is this a horizontal box, only add it if it covers the active width of the image
+ if (topleft.x <= top.x && bottomright.x >= (mRaw->dim.x + top.x)) {
+ mRaw->blackAreas.emplace_back(topleft.y, bottomright.y - topleft.y,
+ false);
+ }
+ // Is it a vertical box, only add it if it covers the active height of the
+ // image
+ else if (topleft.y <= top.y && bottomright.y >= (mRaw->dim.y + top.y)) {
+ mRaw->blackAreas.emplace_back(topleft.x, bottomright.x - topleft.x, true);
+ }
+ }
+ return !mRaw->blackAreas.empty();
+}
+
+bool DngDecoder::decodeBlackLevels(const TiffIFD* raw) {
+ iPoint2D blackdim(1,1);
+ if (raw->hasEntry(BLACKLEVELREPEATDIM)) {
+ TiffEntry *bleveldim = raw->getEntry(BLACKLEVELREPEATDIM);
+ if (bleveldim->count != 2)
+ return false;
+ blackdim = iPoint2D(bleveldim->getU32(0), bleveldim->getU32(1));
+ }
+
+ if (blackdim.x == 0 || blackdim.y == 0)
+ return false;
+
+ if (!raw->hasEntry(BLACKLEVEL))
+ return true;
+
+ if (mRaw->getCpp() != 1)
+ return false;
+
+ TiffEntry* black_entry = raw->getEntry(BLACKLEVEL);
+ if (black_entry->count < blackdim.area())
+ ThrowRDE("BLACKLEVEL entry is too small");
+
+ using BlackType = decltype(mRaw->blackLevelSeparate)::value_type;
+
+ if (blackdim.x < 2 || blackdim.y < 2) {
+ // We so not have enough to fill all individually, read a single and copy it
+ float value = black_entry->getFloat();
+
+ if (value < std::numeric_limits<BlackType>::min() ||
+ value > std::numeric_limits<BlackType>::max())
+ ThrowRDE("Error decoding black level");
+
+ for (int y = 0; y < 2; y++) {
+ for (int x = 0; x < 2; x++)
+ mRaw->blackLevelSeparate[y*2+x] = value;
+ }
+ } else {
+ for (int y = 0; y < 2; y++) {
+ for (int x = 0; x < 2; x++) {
+ float value = black_entry->getFloat(y * blackdim.x + x);
+
+ if (value < std::numeric_limits<BlackType>::min() ||
+ value > std::numeric_limits<BlackType>::max())
+ ThrowRDE("Error decoding black level");
+
+ mRaw->blackLevelSeparate[y * 2 + x] = value;
+ }
+ }
+ }
+
+ // DNG Spec says we must add black in deltav and deltah
+ if (raw->hasEntry(BLACKLEVELDELTAV)) {
+ TiffEntry *blackleveldeltav = raw->getEntry(BLACKLEVELDELTAV);
+ if (static_cast<int>(blackleveldeltav->count) < mRaw->dim.y)
+ ThrowRDE("BLACKLEVELDELTAV array is too small");
+ std::array<float, 2> black_sum = {{}};
+ for (int i = 0; i < mRaw->dim.y; i++)
+ black_sum[i&1] += blackleveldeltav->getFloat(i);
+
+ for (int i = 0; i < 4; i++) {
+ const float value =
+ black_sum[i >> 1] / static_cast<float>(mRaw->dim.y) * 2.0F;
+ if (value < std::numeric_limits<BlackType>::min() ||
+ value > std::numeric_limits<BlackType>::max())
+ ThrowRDE("Error decoding black level");
+
+ if (__builtin_sadd_overflow(mRaw->blackLevelSeparate[i], value,
+ &mRaw->blackLevelSeparate[i]))
+ ThrowRDE("Integer overflow when calculating black level");
+ }
+ }
+
+ if (raw->hasEntry(BLACKLEVELDELTAH)){
+ TiffEntry *blackleveldeltah = raw->getEntry(BLACKLEVELDELTAH);
+ if (static_cast<int>(blackleveldeltah->count) < mRaw->dim.x)
+ ThrowRDE("BLACKLEVELDELTAH array is too small");
+ std::array<float, 2> black_sum = {{}};
+ for (int i = 0; i < mRaw->dim.x; i++)
+ black_sum[i&1] += blackleveldeltah->getFloat(i);
+
+ for (int i = 0; i < 4; i++) {
+ const float value =
+ black_sum[i & 1] / static_cast<float>(mRaw->dim.x) * 2.0F;
+ if (value < std::numeric_limits<BlackType>::min() ||
+ value > std::numeric_limits<BlackType>::max())
+ ThrowRDE("Error decoding black level");
+
+ if (__builtin_sadd_overflow(mRaw->blackLevelSeparate[i], value,
+ &mRaw->blackLevelSeparate[i]))
+ ThrowRDE("Integer overflow when calculating black level");
+ }
+ }
+ return true;
+}
+
+void DngDecoder::setBlack(const TiffIFD* raw) {
+
+ if (raw->hasEntry(MASKEDAREAS) && decodeMaskedAreas(raw))
+ return;
+
+ // Black defaults to 0
+ mRaw->blackLevelSeparate.fill(0);
+
+ if (raw->hasEntry(BLACKLEVEL))
+ decodeBlackLevels(raw);
+}
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.h
new file mode 100644
index 00000000..35ed4735
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/DngDecoder.h
@@ -0,0 +1,65 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "common/RawImage.h" // for RawImage
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffIFD (ptr only), TiffRo...
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+class Buffer;
+
+struct DngTilingDescription;
+
+class DngDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ DngDecoder(TiffRootIFDOwner&& rootIFD, const Buffer* file);
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+ bool mFixLjpeg;
+ void dropUnsuportedChunks(std::vector<const TiffIFD*>* data);
+ void parseCFA(const TiffIFD* raw);
+ DngTilingDescription getTilingDescription(const TiffIFD* raw);
+ void decodeData(const TiffIFD* raw, uint32 sample_format);
+ void handleMetadata(const TiffIFD* raw);
+ bool decodeMaskedAreas(const TiffIFD* raw);
+ bool decodeBlackLevels(const TiffIFD* raw);
+ void setBlack(const TiffIFD* raw);
+
+private:
+ int bps = -1;
+ int compression = -1;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.cpp
new file mode 100644
index 00000000..d6f47578
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.cpp
@@ -0,0 +1,77 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/ErfDecoder.h"
+#include "decoders/RawDecoderException.h" // for RawDecoderExcept...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Endianness.h" // for Endianness::big
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD
+#include "tiff/TiffTag.h" // for TiffTag::EPSONWB
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool ErfDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "SEIKO EPSON CORP.";
+}
+
+void ErfDecoder::checkImageDimensions() {
+ if (width > 3040 || height > 2024)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+}
+
+RawImage ErfDecoder::decodeRawInternal() {
+ SimpleTiffDecoder::prepareForRawDecoding();
+
+ UncompressedDecompressor u(*mFile, off, c2, mRaw);
+
+ u.decode12BitRaw<Endianness::big, false, true>(width, height);
+
+ return mRaw;
+}
+
+void ErfDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+
+ if (mRootIFD->hasEntryRecursive(EPSONWB)) {
+ TiffEntry *wb = mRootIFD->getEntryRecursive(EPSONWB);
+ if (wb->count == 256) {
+ // Magic values taken directly from dcraw
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(wb->getU16(24)) * 508.0F *
+ 1.078F / static_cast<float>(0x10000);
+ mRaw->metadata.wbCoeffs[1] = 1.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(wb->getU16(25)) * 382.0F *
+ 1.173F / static_cast<float>(0x10000);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.h
new file mode 100644
index 00000000..c286cdbb
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ErfDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/SimpleTiffDecoder.h" // for SimpleTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class ErfDecoder final : public SimpleTiffDecoder {
+ void checkImageDimensions() override;
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ ErfDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : SimpleTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.cpp
new file mode 100644
index 00000000..aaa2ff97
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.cpp
@@ -0,0 +1,443 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017-2019 Roman Lebedev
+ Copyright (C) 2019 Robert Bridge
+
+ 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/IiqDecoder.h"
+#include "common/Array2DRef.h" // for Array2DRef
+#include "common/Common.h" // for uint32, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/Spline.h" // for Spline, Spline<>::va...
+#include "decoders/RawDecoder.h" // for RawDecoder::(anonymous)
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/PhaseOneDecompressor.h" // for PhaseOneStrip, Phase...
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianne...
+#include "metadata/CameraMetaData.h" // for CameraMetaData for CFA
+#include "tiff/TiffIFD.h" // for TiffRootIFD, TiffID
+#include <algorithm> // for adjacent_find, gener...
+#include <array> // for array, array<>::cons...
+#include <cassert> // for assert
+#include <cmath> // for lround()
+#include <cstdlib> // for int abs(int)
+#include <functional> // for greater_equal
+#include <iterator> // for advance, next, begin
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool IiqDecoder::isAppropriateDecoder(const Buffer* file) {
+ assert(file);
+
+ const DataBuffer db(*file, Endianness::little);
+
+ // The IIQ magic. Is present for all IIQ raws.
+ return db.get<uint32>(8) == 0x49494949;
+}
+
+bool IiqDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ return IiqDecoder::isAppropriateDecoder(file) &&
+ (make == "Phase One A/S" || make == "Leaf");
+}
+
+// FIXME: this is very close to SamsungV0Decompressor::computeStripes()
+std::vector<PhaseOneStrip>
+IiqDecoder::computeSripes(const Buffer& raw_data,
+ std::vector<IiqOffset>&& offsets,
+ uint32 height) const {
+ assert(height > 0);
+ assert(offsets.size() == (1 + height));
+
+ ByteStream bs(DataBuffer(raw_data, Endianness::little));
+
+ // so... here's the thing. offsets are not guaranteed to be in
+ // monotonically increasing order. so for each element of 'offsets',
+ // we need to find element which specifies next larger offset.
+ // and only then by subtracting those two offsets we get the slice size.
+
+ std::sort(offsets.begin(), offsets.end(),
+ [](const IiqOffset& a, const IiqOffset& b) {
+ if (a.offset == b.offset && &a != &b)
+ ThrowRDE("Two identical offsets found. Corrupt raw.");
+ return a.offset < b.offset;
+ });
+
+ std::vector<PhaseOneStrip> slices;
+ slices.reserve(height);
+
+ auto offset_iterator = std::begin(offsets);
+ bs.skipBytes(offset_iterator->offset);
+
+ auto next_offset_iterator = std::next(offset_iterator);
+ while (next_offset_iterator < std::end(offsets)) {
+ assert(next_offset_iterator->offset > offset_iterator->offset);
+ const auto size = next_offset_iterator->offset - offset_iterator->offset;
+ assert(size > 0);
+
+ slices.emplace_back(offset_iterator->n, bs.getStream(size));
+
+ std::advance(offset_iterator, 1);
+ std::advance(next_offset_iterator, 1);
+ }
+
+ assert(slices.size() == height);
+
+ return slices;
+}
+
+RawImage IiqDecoder::decodeRawInternal() {
+ const Buffer buf(mFile->getSubView(8));
+ const DataBuffer db(buf, Endianness::little);
+ ByteStream bs(db);
+
+ bs.skipBytes(4); // Phase One magic
+ bs.skipBytes(4); // padding?
+
+ const auto origPos = bs.getPosition();
+
+ const uint32 entries_offset = bs.getU32();
+
+ bs.setPosition(entries_offset);
+
+ const uint32 entries_count = bs.getU32();
+ bs.skipBytes(4); // ???
+
+ // this is how much is to be read for all the entries
+ ByteStream es(bs.getStream(entries_count, 16));
+
+ bs.setPosition(origPos);
+
+ uint32 width = 0;
+ uint32 height = 0;
+ uint32 split_row = 0;
+ uint32 split_col = 0;
+
+ Buffer raw_data;
+ ByteStream block_offsets;
+ ByteStream wb;
+ ByteStream correction_meta_data;
+
+ for (uint32 entry = 0; entry < entries_count; entry++) {
+ const uint32 tag = es.getU32();
+ es.skipBytes(4); // type
+ const uint32 len = es.getU32();
+ const uint32 data = es.getU32();
+
+ switch (tag) {
+ case 0x107:
+ wb = bs.getSubStream(data, len);
+ break;
+ case 0x108:
+ width = data;
+ break;
+ case 0x109:
+ height = data;
+ break;
+ case 0x10f:
+ raw_data = bs.getSubView(data, len);
+ break;
+ case 0x110:
+ correction_meta_data = bs.getSubStream(data);
+ break;
+ case 0x21c:
+ // they are not guaranteed to be sequential!
+ block_offsets = bs.getSubStream(data, len);
+ break;
+ case 0x21d:
+ black_level = data >> 2;
+ break;
+ case 0x222:
+ split_col = data;
+ break;
+ case 0x224:
+ split_row = data;
+ break;
+ default:
+ // FIXME: is there a "block_sizes" entry?
+ break;
+ }
+ }
+
+ // FIXME: could be wrong. max "active pixels" in "Sensor+" mode - "101 MP"
+ if (width == 0 || height == 0 || width > 11976 || height > 8852)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (split_col > width || split_row > height)
+ ThrowRDE("Invalid sensor quadrant split values (%u, %u)", split_row,
+ split_col);
+
+ block_offsets = block_offsets.getStream(height, sizeof(uint32));
+
+ std::vector<IiqOffset> offsets;
+ offsets.reserve(1 + height);
+
+ for (uint32 row = 0; row < height; row++)
+ offsets.emplace_back(row, block_offsets.getU32());
+
+ // to simplify slice size calculation, we insert a dummy offset,
+ // which will be used much like end()
+ offsets.emplace_back(height, raw_data.getSize());
+
+ std::vector<PhaseOneStrip> strips(
+ computeSripes(raw_data, std::move(offsets), height));
+
+ mRaw->dim = iPoint2D(width, height);
+
+ PhaseOneDecompressor p(mRaw, std::move(strips));
+ mRaw->createData();
+ p.decompress();
+
+ if (correction_meta_data.getSize() != 0 && iiq)
+ CorrectPhaseOneC(correction_meta_data, split_row, split_col);
+
+ for (int i = 0; i < 3; i++)
+ mRaw->metadata.wbCoeffs[i] = wb.getFloat();
+
+ return mRaw;
+}
+
+void IiqDecoder::CorrectPhaseOneC(ByteStream meta_data, uint32 split_row,
+ uint32 split_col) {
+ meta_data.skipBytes(8);
+ const uint32 bytes_to_entries = meta_data.getU32();
+ meta_data.setPosition(bytes_to_entries);
+ const uint32 entries_count = meta_data.getU32();
+ meta_data.skipBytes(4);
+
+ // this is how much is to be read for all the entries
+ ByteStream entries(meta_data.getStream(entries_count, 12));
+ meta_data.setPosition(0);
+
+ bool QuadrantMultipliersSeen = false;
+ bool SensorDefectsSeen = false;
+
+ for (uint32 entry = 0; entry < entries_count; entry++) {
+ const uint32 tag = entries.getU32();
+ const uint32 len = entries.getU32();
+ const uint32 offset = entries.getU32();
+
+ switch (tag) {
+ case 0x400: // Sensor Defects
+ if (SensorDefectsSeen)
+ ThrowRDE("Second sensor defects entry seen. Unexpected.");
+ correctSensorDefects(meta_data.getSubStream(offset, len));
+ SensorDefectsSeen = true;
+ break;
+ case 0x431:
+ if (QuadrantMultipliersSeen)
+ ThrowRDE("Second quadrant multipliers entry seen. Unexpected.");
+ if (iiq.quadrantMultipliers)
+ CorrectQuadrantMultipliersCombined(meta_data.getSubStream(offset, len),
+ split_row, split_col);
+ QuadrantMultipliersSeen = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// This method defines a correction that compensates for the fact that
+// IIQ files may come from a camera with multiple (four, in this case)
+// sensors combined into a single "sensor." Because the different
+// sensors may have slightly different responses, we need to multiply
+// the pixels in each by a correction factor to ensure that they blend
+// together smoothly. The correction factor is not a single
+// multiplier, but a curve defined by seven control points. Each
+// curve's control points share the same seven X-coordinates.
+void IiqDecoder::CorrectQuadrantMultipliersCombined(ByteStream data,
+ uint32 split_row,
+ uint32 split_col) {
+ std::array<uint32, 9> shared_x_coords;
+
+ // Read the middle seven points from the file
+ std::generate_n(std::next(shared_x_coords.begin()), 7,
+ [&data] { return data.getU32(); });
+
+ // All the curves include (0, 0) and (65535, 65535),
+ // so the first and last points are predefined
+ shared_x_coords.front() = 0;
+ shared_x_coords.back() = 65535;
+
+ // Check that the middle coordinates make sense.
+ if (std::adjacent_find(shared_x_coords.cbegin(), shared_x_coords.cend(),
+ std::greater_equal<>()) != shared_x_coords.cend())
+ ThrowRDE("The X coordinates must all be strictly increasing");
+
+ std::array<std::array<std::vector<iPoint2D>, 2>, 2> control_points;
+ for (auto& quadRow : control_points) {
+ for (auto& quadrant : quadRow) {
+ quadrant.reserve(9);
+ quadrant.emplace_back(0, 0);
+
+ for (int i = 1; i < 8; i++) {
+ // These multipliers are expressed in ten-thousandths in the
+ // file
+ const uint64 y_coord =
+ (uint64(data.getU32()) * shared_x_coords[i]) / 10000ULL;
+ if (y_coord > 65535)
+ ThrowRDE("The Y coordinate %llu is too large", y_coord);
+ quadrant.emplace_back(shared_x_coords[i], y_coord);
+ }
+
+ quadrant.emplace_back(65535, 65535);
+ assert(quadrant.size() == 9);
+ }
+ }
+
+ for (int quadRow = 0; quadRow < 2; quadRow++) {
+ for (int quadCol = 0; quadCol < 2; quadCol++) {
+ const Spline<> s(control_points[quadRow][quadCol]);
+ const std::vector<ushort16> curve = s.calculateCurve();
+
+ int row_start = quadRow == 0 ? 0 : split_row;
+ int row_end = quadRow == 0 ? split_row : mRaw->dim.y;
+ int col_start = quadCol == 0 ? 0 : split_col;
+ int col_end = quadCol == 0 ? split_col : mRaw->dim.x;
+
+ for (int row = row_start; row < row_end; row++) {
+ auto* pixel =
+ reinterpret_cast<ushort16*>(mRaw->getData(col_start, row));
+ for (int col = col_start; col < col_end; col++, pixel++) {
+ // This adjustment is expected to be made with the
+ // black-level already subtracted from the pixel values.
+ // Because this is kept as metadata and not subtracted at
+ // this point, to make the correction work we subtract the
+ // appropriate amount before indexing into the curve and
+ // then add it back so that subtracting the black level
+ // later will work as expected
+ const ushort16 diff = *pixel < black_level ? *pixel : black_level;
+ *pixel = curve[*pixel - diff] + diff;
+ }
+ }
+ }
+ }
+}
+
+void IiqDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ checkCameraSupported(meta, mRootIFD->getID(), "");
+
+ auto id = mRootIFD->getID();
+ const Camera* cam = meta->getCamera(id.make, id.model, mRaw->metadata.mode);
+ if (!cam)
+ ThrowRDE("Couldn't find camera %s %s", id.make.c_str(), id.model.c_str());
+
+ mRaw->cfa = cam->cfa;
+}
+
+void IiqDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+
+ if (black_level)
+ mRaw->blackLevel = black_level;
+}
+
+void IiqDecoder::correctSensorDefects(ByteStream data) {
+ while (data.getRemainSize() != 0) {
+ const ushort16 col = data.getU16();
+ const ushort16 row = data.getU16();
+ const ushort16 type = data.getU16();
+ data.skipBytes(2); // Ignore uknown/unused bits.
+
+ if (col >= mRaw->dim.x) // Value for col is outside the raw image.
+ continue;
+ switch (type) {
+ case 131: // bad column
+ case 137: // bad column
+ correctBadColumn(col);
+ break;
+ case 129: // bad pixel
+ handleBadPixel(col, row);
+ break;
+ default: // Oooh, a sensor defect not in dcraw!
+ break;
+ }
+ }
+}
+
+void IiqDecoder::handleBadPixel(const ushort16 col, const ushort16 row) {
+ MutexLocker guard(&mRaw->mBadPixelMutex);
+ mRaw->mBadPixelPositions.insert(mRaw->mBadPixelPositions.end(),
+ (static_cast<uint32>(row) << 16) + col);
+}
+
+void IiqDecoder::correctBadColumn(const ushort16 col) {
+ const Array2DRef<uint16_t> img(reinterpret_cast<uint16_t*>(mRaw->getData()),
+ mRaw->dim.x, mRaw->dim.y,
+ mRaw->pitch / sizeof(uint16_t));
+
+ for (int row = 2; row < mRaw->dim.y - 2; row++) {
+ if (mRaw->cfa.getColorAt(col, row) == CFA_GREEN) {
+ /* Do green pixels. Let's pretend we are in "G" pixel, in the middle:
+ * G=G
+ * BGB
+ * G0G
+ * We accumulate the values 4 "G" pixels form diagonals, then check which
+ * of 4 values is most distant from the mean of those 4 values, subtract
+ * it from the sum, average (divide by 3) and round to nearest int.
+ */
+ int max = 0;
+ std::array<ushort16, 4> val;
+ std::array<int32, 4> dev;
+ int32 sum = 0;
+ sum += val[0] = img(col - 1, row - 1);
+ sum += val[1] = img(col - 1, row + 1);
+ sum += val[2] = img(col + 1, row - 1);
+ sum += val[3] = img(col + 1, row + 1);
+ for (int i = 0; i < 4; i++) {
+ dev[i] = std::abs((val[i] * 4) - sum);
+ if (dev[max] < dev[i])
+ max = i;
+ }
+ const int three_pixels = sum - val[max];
+ // This is `std::lround(three_pixels / 3.0)`, but without FP.
+ img(col, row) = (three_pixels + 1) / 3;
+ } else {
+ /*
+ * Do non-green pixels. Let's pretend we are in "R" pixel, in the middle:
+ * RG=GR
+ * GB=BG
+ * RGRGR
+ * GB0BG
+ * RG0GR
+ * We have 6 other "R" pixels - 2 by horizontal, 4 by diagonals.
+ * We need to combine them, to get the value of the pixel we are in.
+ */
+ uint32 diags = img(col - 2, row + 2) + img(col - 2, row - 2) +
+ img(col + 2, row + 2) + img(col + 2, row - 2);
+ uint32 horiz = img(col - 2, row) + img(col + 2, row);
+ // But this is not just averaging, we bias towards the horizontal pixels.
+ img(col, row) = std::lround(diags * 0.0732233 + horiz * 0.3535534);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.h
new file mode 100644
index 00000000..7b8dc2d3
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/IiqDecoder.h
@@ -0,0 +1,75 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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 "common/RawImage.h" // for RawImage
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFD (ptr only)
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class Buffer;
+class ByteStream;
+class CameraMetaData;
+struct PhaseOneStrip;
+
+class IiqDecoder final : public AbstractTiffDecoder {
+ struct IiqOffset {
+ uint32 n;
+ uint32 offset;
+
+ IiqOffset() = default;
+ IiqOffset(uint32 block, uint32 offset_) : n(block), offset(offset_) {}
+ };
+
+ std::vector<PhaseOneStrip> computeSripes(const Buffer& raw_data,
+ std::vector<IiqOffset>&& offsets,
+ uint32 height) const;
+
+public:
+ static bool isAppropriateDecoder(const Buffer* file);
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+
+ IiqDecoder(TiffRootIFDOwner&& rootIFD, const Buffer* file)
+ : AbstractTiffDecoder(move(rootIFD), file) {}
+
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+ uint32 black_level = 0;
+ void CorrectPhaseOneC(ByteStream meta_data, uint32 split_row,
+ uint32 split_col);
+ void CorrectQuadrantMultipliersCombined(ByteStream data, uint32 split_row,
+ uint32 split_col);
+ void correctSensorDefects(ByteStream data);
+ void correctBadColumn(ushort16 col);
+ void handleBadPixel(ushort16 col, ushort16 row);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.cpp
new file mode 100644
index 00000000..8bcdcb0e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.cpp
@@ -0,0 +1,163 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+
+ 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/KdcDecoder.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "common/NORangesSet.h" // for NORangesSet
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for RawDecoderExcept...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/Endianness.h" // for Endianness
+#include "metadata/Camera.h" // for Hints
+#include "parsers/TiffParserException.h" // for TiffParserException
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD
+#include "tiff/TiffTag.h" // for TiffTag::COMPRES...
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+bool KdcDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "EASTMAN KODAK COMPANY";
+}
+
+Buffer KdcDecoder::getInputBuffer() {
+ TiffEntry* offset = mRootIFD->getEntryRecursive(KODAK_KDC_OFFSET);
+ if (!offset || offset->count < 13)
+ ThrowRDE("Couldn't find the KDC offset");
+
+ assert(offset != nullptr);
+ uint64 off = uint64(offset->getU32(4)) + uint64(offset->getU32(12));
+ if (off > std::numeric_limits<uint32>::max())
+ ThrowRDE("Offset is too large.");
+
+ // Offset hardcoding gotten from dcraw
+ if (hints.has("easyshare_offset_hack"))
+ off = off < 0x15000 ? 0x15000 : 0x17000;
+
+ if (off > mFile->getSize())
+ ThrowRDE("offset is out of bounds");
+
+ const auto area = mRaw->dim.area();
+ if (area > std::numeric_limits<decltype(area)>::max() / 12) // round down
+ ThrowRDE("Image dimensions are way too large, potential for overflow");
+
+ const auto bits = 12 * area;
+ if (bits % 8 != 0)
+ ThrowRDE("Bad combination of image dims and bpp, bit count %% 8 != 0");
+ const auto bytes = bits / 8;
+
+ return mFile->getSubView(off, bytes);
+}
+
+RawImage KdcDecoder::decodeRawInternal() {
+ if (!mRootIFD->hasEntryRecursive(COMPRESSION))
+ ThrowRDE("Couldn't find compression setting");
+
+ auto compression = mRootIFD->getEntryRecursive(COMPRESSION)->getU32();
+ if (7 != compression)
+ ThrowRDE("Unsupported compression %d", compression);
+
+ TiffEntry* ifdoffset = mRootIFD->getEntryRecursive(KODAK_IFD2);
+ if (!ifdoffset)
+ ThrowRDE("Couldn't find the Kodak IFD offset");
+
+ NORangesSet<Buffer> ifds;
+
+ assert(ifdoffset != nullptr);
+ TiffRootIFD kodakifd(nullptr, &ifds, ifdoffset->getRootIfdData(),
+ ifdoffset->getU32());
+
+ uint32 width = 0;
+ uint32 height = 0;
+ TiffEntry* ew = kodakifd.getEntryRecursive(KODAK_KDC_SENSOR_WIDTH);
+ TiffEntry* eh = kodakifd.getEntryRecursive(KODAK_KDC_SENSOR_HEIGHT);
+ if (ew && eh) {
+ width = ew->getU32();
+ height = eh->getU32();
+ } else
+ ThrowRDE("Unable to retrieve image size");
+
+ mRaw->dim = iPoint2D(width, height);
+
+ const Buffer inputBuffer = KdcDecoder::getInputBuffer();
+
+ mRaw->createData();
+
+ UncompressedDecompressor u(inputBuffer, mRaw);
+
+ u.decode12BitRaw<Endianness::big>(width, height);
+
+ return mRaw;
+}
+
+void KdcDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+
+ // Try the kodak hidden IFD for WB
+ if (mRootIFD->hasEntryRecursive(KODAK_IFD2)) {
+ TiffEntry *ifdoffset = mRootIFD->getEntryRecursive(KODAK_IFD2);
+ try {
+ NORangesSet<Buffer> ifds;
+
+ TiffRootIFD kodakifd(nullptr, &ifds, ifdoffset->getRootIfdData(),
+ ifdoffset->getU32());
+
+ if (kodakifd.hasEntryRecursive(KODAK_KDC_WB)) {
+ TiffEntry *wb = kodakifd.getEntryRecursive(KODAK_KDC_WB);
+ if (wb->count == 3) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
+ }
+ }
+ } catch (TiffParserException &e) {
+ mRaw->setError(e.what());
+ }
+ }
+
+ // Use the normal WB if available
+ if (mRootIFD->hasEntryRecursive(KODAKWB)) {
+ TiffEntry *wb = mRootIFD->getEntryRecursive(KODAKWB);
+ if (wb->count == 734 || wb->count == 1502) {
+ mRaw->metadata.wbCoeffs[0] =
+ static_cast<float>(((static_cast<ushort16>(wb->getByte(148))) << 8) |
+ wb->getByte(149)) /
+ 256.0F;
+ mRaw->metadata.wbCoeffs[1] = 1.0F;
+ mRaw->metadata.wbCoeffs[2] =
+ static_cast<float>(((static_cast<ushort16>(wb->getByte(150))) << 8) |
+ wb->getByte(151)) /
+ 256.0F;
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.h
new file mode 100644
index 00000000..0a57cfda
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/KdcDecoder.h
@@ -0,0 +1,53 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "io/Buffer.h" // for Buffer
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class KdcDecoder final : public AbstractTiffDecoder
+{
+ Buffer getInputBuffer();
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ KdcDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.cpp
new file mode 100644
index 00000000..72209e6a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.cpp
@@ -0,0 +1,59 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/MefDecoder.h"
+#include "decoders/RawDecoderException.h" // for RawDecoderException (ptr o...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Endianness.h" // for Endianness::big
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+bool MefDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "Mamiya-OP Co.,Ltd.";
+}
+
+void MefDecoder::checkImageDimensions() {
+ if (width > 4016 || height > 5344)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+}
+
+RawImage MefDecoder::decodeRawInternal() {
+ SimpleTiffDecoder::prepareForRawDecoding();
+
+ UncompressedDecompressor u(*mFile, off, mRaw);
+
+ u.decode12BitRaw<Endianness::big>(width, height);
+
+ return mRaw;
+}
+
+void MefDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, "", 0);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.h
new file mode 100644
index 00000000..66cb2808
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MefDecoder.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/SimpleTiffDecoder.h" // for SimpleTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class MefDecoder final : public SimpleTiffDecoder {
+ void checkImageDimensions() override;
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ MefDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : SimpleTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.cpp
new file mode 100644
index 00000000..2e0c6212
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.cpp
@@ -0,0 +1,181 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+
+ 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/MosDecoder.h"
+#include "common/Common.h" // for uint32, uchar8
+#include "common/Point.h" // for iPoint2D
+#include "decoders/IiqDecoder.h" // for IiqDecoder::isAppr...
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "decoders/RawDecoderException.h" // for RawDecoderExcept...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for getU32LE, getLE
+#include "parsers/TiffParserException.h" // for TiffParserException
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for TiffTag::TILEOFF...
+#include <cassert> // for assert
+#include <cstring> // for memchr
+#include <istream> // for istringstream
+#include <memory> // for unique_ptr
+#include <string> // for string, allocator
+#include <utility> // for move
+
+using std::string;
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool MosDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ try {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // This is messy. see https://github.com/darktable-org/rawspeed/issues/116
+ // Old Leafs are MOS, new ones are IIQ. Use IIQ's magic to differentiate.
+ return make == "Leaf" && !IiqDecoder::isAppropriateDecoder(file);
+ } catch (const TiffParserException&) {
+ // Last ditch effort to identify Leaf cameras that don't have a Tiff Make
+ // set
+ TiffEntry* softwareIFD = rootIFD->getEntryRecursive(SOFTWARE);
+ if (!softwareIFD)
+ return false;
+
+ const string software = trimSpaces(softwareIFD->getString());
+ return software == "Camera Library";
+ }
+}
+
+MosDecoder::MosDecoder(TiffRootIFDOwner&& rootIFD, const Buffer* file)
+ : AbstractTiffDecoder(move(rootIFD), file) {
+ if (mRootIFD->getEntryRecursive(MAKE)) {
+ auto id = mRootIFD->getID();
+ make = id.make;
+ model = id.model;
+ } else {
+ TiffEntry *xmp = mRootIFD->getEntryRecursive(XMP);
+ if (!xmp)
+ ThrowRDE("Couldn't find the XMP");
+
+ assert(xmp != nullptr);
+ string xmpText = xmp->getString();
+ make = getXMPTag(xmpText, "Make");
+ model = getXMPTag(xmpText, "Model");
+ }
+}
+
+string MosDecoder::getXMPTag(const string &xmp, const string &tag) {
+ string::size_type start = xmp.find("<tiff:"+tag+">");
+ string::size_type end = xmp.find("</tiff:"+tag+">");
+ if (start == string::npos || end == string::npos || end <= start)
+ ThrowRDE("Couldn't find tag '%s' in the XMP", tag.c_str());
+ int startlen = tag.size()+7;
+ return xmp.substr(start+startlen, end-start-startlen);
+}
+
+RawImage MosDecoder::decodeRawInternal() {
+ uint32 off = 0;
+
+ const TiffIFD *raw = nullptr;
+
+ if (mRootIFD->hasEntryRecursive(TILEOFFSETS)) {
+ raw = mRootIFD->getIFDWithTag(TILEOFFSETS);
+ off = raw->getEntry(TILEOFFSETS)->getU32();
+ } else {
+ raw = mRootIFD->getIFDWithTag(CFAPATTERN);
+ off = raw->getEntry(STRIPOFFSETS)->getU32();
+ }
+
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ // FIXME: could be wrong. max "active pixels" - "80 MP"
+ if (width == 0 || height == 0 || width > 10328 || height > 7760)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->createData();
+
+ UncompressedDecompressor u(*mFile, off, mRaw);
+
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ if (1 == compression) {
+ const DataBuffer db(*mFile);
+ const ByteStream bs(db);
+ const Endianness endianness = getTiffByteOrder(bs, 0);
+
+ if (Endianness::big == endianness)
+ u.decodeRawUnpacked<16, Endianness::big>(width, height);
+ else
+ u.decodeRawUnpacked<16, Endianness::little>(width, height);
+ }
+ else if (99 == compression || 7 == compression) {
+ ThrowRDE("Leaf LJpeg not yet supported");
+ //LJpegPlain l(mFile, mRaw);
+ //l.startDecoder(off, mFile->getSize()-off, 0, 0);
+ } else
+ ThrowRDE("Unsupported compression: %d", compression);
+
+ return mRaw;
+}
+
+void MosDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ RawDecoder::checkCameraSupported(meta, make, model, "");
+}
+
+void MosDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ RawDecoder::setMetaData(meta, make, model, "", 0);
+
+ // Fetch the white balance (see dcraw.c parse_mos for more metadata that can be gotten)
+ if (mRootIFD->hasEntryRecursive(LEAFMETADATA)) {
+ ByteStream bs = mRootIFD->getEntryRecursive(LEAFMETADATA)->getData();
+
+ // We need at least a couple of bytes:
+ // "NeutObj_neutrals" + 28 bytes binay + 4x uint as strings + 3x space + \0
+ const uint32 minSize = 16+28+4+3+1;
+
+ // dcraw does actual parsing, since we just want one field we bruteforce it
+ while (bs.getRemainSize() > minSize) {
+ if (bs.skipPrefix("NeutObj_neutrals", 16)) {
+ bs.skipBytes(28);
+ // check for nulltermination of string inside bounds
+ if (!memchr(bs.peekData(bs.getRemainSize()), 0, bs.getRemainSize()))
+ break;
+ std::array<uint32, 4> tmp = {{}};
+ std::istringstream iss(bs.peekString());
+ iss >> tmp[0] >> tmp[1] >> tmp[2] >> tmp[3];
+ if (!iss.fail() && tmp[0] > 0 && tmp[1] > 0 && tmp[2] > 0 &&
+ tmp[3] > 0) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(tmp[0]) / tmp[1];
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(tmp[0]) / tmp[2];
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(tmp[0]) / tmp[3];
+ }
+ break;
+ }
+ bs.skipBytes(1);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.h
new file mode 100644
index 00000000..b64d95db
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MosDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFD (ptr only)
+#include <string> // for string
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class MosDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ MosDecoder(TiffRootIFDOwner&& rootIFD, const Buffer* file);
+
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+ std::string make, model;
+ std::string getXMPTag(const std::string &xmp, const std::string &tag);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.cpp
new file mode 100644
index 00000000..0c8dadec
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.cpp
@@ -0,0 +1,200 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decoders/MrwDecoder.h"
+#include "common/Common.h" // for uint32
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for DataBuffer, Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endi...
+#include "metadata/Camera.h" // for Hints
+#include "parsers/TiffParser.h" // for TiffParser
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <cassert> // for assert
+#include <cstring> // for memcmp, size_t
+#include <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+MrwDecoder::MrwDecoder(const Buffer* file) : RawDecoder(file) { parseHeader(); }
+
+int MrwDecoder::isMRW(const Buffer* input) {
+ static const std::array<char, 4> magic = {{0x00, 'M', 'R', 'M'}};
+ const unsigned char* data = input->getData(0, magic.size());
+ return 0 == memcmp(data, magic.data(), magic.size());
+}
+
+void MrwDecoder::parseHeader() {
+ if (!isMRW(mFile))
+ ThrowRDE("This isn't actually a MRW file, why are you calling me?");
+
+ const DataBuffer db(*mFile, Endianness::big);
+ ByteStream bs(db);
+
+ // magic
+ bs.skipBytes(4);
+
+ // the size of the rest of the header, up to the image data
+ const auto headerSize = bs.getU32();
+ bs.check(headerSize);
+
+ // ... and offset to the image data at the same time
+ const auto dataOffset = bs.getPosition() + headerSize;
+ assert(bs.getPosition() == 8);
+
+ // now, let's parse rest of the header.
+ bs = bs.getSubStream(0, dataOffset);
+ bs.skipBytes(8);
+
+ bool foundPRD = false;
+ while (bs.getRemainSize() > 0) {
+ uint32 tag = bs.getU32();
+ uint32 len = bs.getU32();
+ bs.check(len);
+ if (!len)
+ ThrowRDE("Found entry of zero length, MRW is corrupt.");
+
+ const auto origPos = bs.getPosition();
+
+ switch (tag) {
+ case 0x505244: { // PRD
+ foundPRD = true;
+ bs.skipBytes(8); // Version Number
+ raw_height = bs.getU16(); // CCD Size Y
+ raw_width = bs.getU16(); // CCD Size X
+
+ if (!raw_width || !raw_height || raw_width > 3280 || raw_height > 2456) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", raw_width,
+ raw_height);
+ }
+
+ bs.skipBytes(2); // Image Size Y
+ bs.skipBytes(2); // Image Size X
+
+ bpp = bs.getByte(); // DataSize
+ if (12 != bpp && 16 != bpp)
+ ThrowRDE("Unknown data size");
+
+ if ((raw_height * raw_width * bpp) % 8 != 0)
+ ThrowRDE("Bad combination of image size and raw dimensions.");
+
+ if (12 != bs.getByte()) // PixelSize
+ ThrowRDE("Unexpected pixel size");
+
+ const auto SM = bs.getByte(); // StorageMethod
+ if (0x52 != SM && 0x59 != SM)
+ ThrowRDE("Unknown storage method");
+ packed = (0x59 == SM);
+
+ if ((12 == bpp) != packed)
+ ThrowRDE("Packed/BPP sanity check failed!");
+
+ bs.skipBytes(1); // Unknown1
+ bs.skipBytes(2); // Unknown2
+ bs.skipBytes(2); // BayerPattern
+ break;
+ }
+ case 0x545457: // TTW
+ // Base value for offsets needs to be at the beginning of the TIFF block,
+ // not the file
+ rootIFD = TiffParser::parse(nullptr, bs.getBuffer(len));
+ break;
+ case 0x574247: // WBG
+ bs.skipBytes(4); // 4 factors
+ static_assert(4 == (sizeof(wb_coeffs) / sizeof(wb_coeffs[0])),
+ "wrong coeff count");
+ for (auto& wb_coeff : wb_coeffs)
+ wb_coeff = static_cast<float>(bs.getU16()); // gain
+
+ // FIXME?
+ // Gf = Gr / 2^(6+F)
+ break;
+ default:
+ // unknown block, let's just ignore
+ break;
+ }
+
+ bs.setPosition(origPos + len);
+ }
+
+ if (!foundPRD)
+ ThrowRDE("Did not find PRD tag. Image corrupt.");
+
+ // processed all of the header. the image data is directly next
+
+ const auto imageBits = raw_height * raw_width * bpp;
+ assert(imageBits > 0);
+ assert(imageBits % 8 == 0);
+
+ imageData = db.getSubView(bs.getPosition(), imageBits / 8);
+}
+
+RawImage MrwDecoder::decodeRawInternal() {
+ mRaw->dim = iPoint2D(raw_width, raw_height);
+ mRaw->createData();
+
+ DataBuffer db(imageData, Endianness::big);
+ ByteStream bs(db);
+ UncompressedDecompressor u(bs, mRaw);
+
+ if (packed)
+ u.decode12BitRaw<Endianness::big>(raw_width, raw_height);
+ else
+ u.decodeRawUnpacked<12, Endianness::big>(raw_width, raw_height);
+
+ return mRaw;
+}
+
+void MrwDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ if (!rootIFD)
+ ThrowRDE("Couldn't find make and model");
+
+ auto id = rootIFD->getID();
+ this->checkCameraSupported(meta, id.make, id.model, "");
+}
+
+void MrwDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ //Default
+ int iso = 0;
+
+ if (!rootIFD)
+ ThrowRDE("Couldn't find make and model");
+
+ auto id = rootIFD->getID();
+ setMetaData(meta, id.make, id.model, "", iso);
+
+ 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];
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.h
new file mode 100644
index 00000000..2c50fddb
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/MrwDecoder.h
@@ -0,0 +1,57 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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 "common/RawImage.h" // for RawImage
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "io/Buffer.h" // for Buffer
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <cmath> // for NAN
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+class MrwDecoder final : public RawDecoder {
+ TiffRootIFDOwner rootIFD;
+
+ uint32 raw_width = 0;
+ uint32 raw_height = 0;
+ Buffer imageData;
+ uint32 bpp = 0;
+ uint32 packed = 0;
+ std::array<float, 4> wb_coeffs = {{NAN, NAN, NAN, NAN}};
+
+public:
+ explicit MrwDecoder(const Buffer* file);
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ static int isMRW(const Buffer* input);
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+ void parseHeader();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.cpp
new file mode 100644
index 00000000..aaa1aa0b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.cpp
@@ -0,0 +1,109 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/NakedDecoder.h"
+#include "common/Common.h" // for BitOrder, BitOrd...
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for RawDecoderExcept...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "metadata/Camera.h" // for Camera, Hints
+#include <map> // for map
+#include <stdexcept> // for out_of_range
+#include <string> // for string, basic_st...
+
+using std::map;
+using std::string;
+
+namespace rawspeed {
+
+class Buffer;
+class CameraMetaData;
+
+NakedDecoder::NakedDecoder(const Buffer* file, const Camera* c)
+ : RawDecoder(file), cam(c) {}
+
+const map<string, BitOrder> NakedDecoder::order2enum = {
+ {"plain", BitOrder_LSB},
+ {"jpeg", BitOrder_MSB},
+ {"jpeg16", BitOrder_MSB16},
+ {"jpeg32", BitOrder_MSB32},
+};
+
+void NakedDecoder::parseHints() {
+ const auto& cHints = cam->hints;
+ const auto& make = cam->make.c_str();
+ const auto& model = cam->model.c_str();
+
+ auto parseHint = [&cHints, &make, &model](const string& name) {
+ if (!cHints.has(name))
+ ThrowRDE("%s %s: couldn't find %s", make, model, name.c_str());
+
+ return cHints.get(name, 0U);
+ };
+
+ width = parseHint("full_width");
+ height = parseHint("full_height");
+
+ if (width == 0 || height == 0)
+ ThrowRDE("%s %s: image is of zero size?", make, model);
+
+ filesize = parseHint("filesize");
+ offset = cHints.get("offset", 0);
+ if (filesize == 0 || offset >= filesize)
+ ThrowRDE("%s %s: no image data found", make, model);
+
+ bits = cHints.get("bits", (filesize-offset)*8/width/height);
+ if (bits == 0)
+ ThrowRDE("%s %s: image bpp is invalid: %u", make, model, bits);
+
+ auto order = cHints.get("order", string());
+ if (!order.empty()) {
+ try {
+ bo = order2enum.at(order);
+ } catch (std::out_of_range&) {
+ ThrowRDE("%s %s: unknown order: %s", make, model, order.c_str());
+ }
+ }
+}
+
+RawImage NakedDecoder::decodeRawInternal() {
+ parseHints();
+
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->createData();
+
+ UncompressedDecompressor u(*mFile, offset, mRaw);
+
+ iPoint2D pos(0, 0);
+ u.readUncompressedRaw(mRaw->dim, pos, width * bits / 8, bits, bo);
+
+ return mRaw;
+}
+
+void NakedDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ this->checkCameraSupported(meta, cam->make, cam->model, cam->mode);
+}
+
+void NakedDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ setMetaData(meta, cam->make, cam->model, cam->mode, 0);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.h
new file mode 100644
index 00000000..38b799d1
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/NakedDecoder.h
@@ -0,0 +1,59 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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, BitOrder::BitOrder_MSB16
+#include "common/RawImage.h" // for RawImage
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include <map> // for map
+#include <string> // for string, basic_st...
+
+namespace rawspeed {
+
+class Camera;
+class CameraMetaData;
+class Buffer;
+
+class NakedDecoder final : public RawDecoder {
+ const Camera* cam;
+
+ uint32 width{0};
+ uint32 height{0};
+ uint32 filesize{0};
+ uint32 bits{0};
+ uint32 offset{0};
+ BitOrder bo{BitOrder_MSB16};
+
+ static const std::map<std::string, BitOrder> order2enum;
+ void parseHints();
+
+public:
+ NakedDecoder(const Buffer* file, const Camera* c);
+ RawImage decodeRawInternal() override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.cpp
new file mode 100644
index 00000000..1e1ed2ed
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.cpp
@@ -0,0 +1,783 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+
+ 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/NefDecoder.h"
+#include "common/Common.h" // for uint32, uchar8
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/NikonDecompressor.h" // for NikonDecompressor
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for getU16BE, Endian...
+#include "io/IOException.h" // for ThrowIOE
+#include "metadata/Camera.h" // for Hints
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_...
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for TiffTag, IMAGELE...
+#include <algorithm> // for min
+#include <cassert> // for assert
+#include <cmath> // for pow, exp, log
+#include <memory> // for unique_ptr, allo...
+#include <sstream> // for operator<<, ostr...
+#include <string> // for string, operator==
+#include <vector> // for vector
+// IWYU pragma: no_include <ext/alloc_traits.h>
+
+using std::vector;
+using std::string;
+using std::min;
+using std::ostringstream;
+
+namespace rawspeed {
+
+bool NefDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "NIKON CORPORATION" || make == "NIKON";
+}
+
+RawImage NefDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(CFAPATTERN);
+ auto compression = raw->getEntry(COMPRESSION)->getU32();
+
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);
+
+ if (mRootIFD->getEntryRecursive(MODEL)->getString() == "NIKON D100 ") { /**Sigh**/
+ if (!mFile->isValid(offsets->getU32()))
+ ThrowRDE("Image data outside of file.");
+ if (!D100IsCompressed(offsets->getU32())) {
+ DecodeD100Uncompressed();
+ return mRaw;
+ }
+ }
+
+ if (compression == 1 || (hints.has("force_uncompressed")) ||
+ NEFIsUncompressed(raw)) {
+ DecodeUncompressed();
+ return mRaw;
+ }
+
+ if (NEFIsUncompressedRGB(raw)) {
+ DecodeSNefUncompressed();
+ return mRaw;
+ }
+
+ if (offsets->count != 1) {
+ ThrowRDE("Multiple Strips found: %u", offsets->count);
+ }
+ if (counts->count != offsets->count) {
+ ThrowRDE(
+ "Byte count number does not match strip size: count:%u, strips:%u ",
+ counts->count, offsets->count);
+ }
+ if (!mFile->isValid(offsets->getU32(), counts->getU32()))
+ ThrowRDE("Invalid strip byte count. File probably truncated.");
+
+ if (34713 != compression)
+ ThrowRDE("Unsupported compression");
+
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ mRaw->dim = iPoint2D(width, height);
+
+ raw = mRootIFD->getIFDWithTag(static_cast<TiffTag>(0x8c));
+
+ TiffEntry *meta;
+ if (raw->hasEntry(static_cast<TiffTag>(0x96))) {
+ meta = raw->getEntry(static_cast<TiffTag>(0x96));
+ } else {
+ meta = raw->getEntry(static_cast<TiffTag>(0x8c)); // Fall back
+ }
+
+ ByteStream rawData(mFile, offsets->getU32(), counts->getU32());
+
+ NikonDecompressor n(mRaw, meta->getData(), bitPerPixel);
+ mRaw->createData();
+ n.decompress(rawData, uncorrectedRawValues);
+
+ return mRaw;
+}
+
+/*
+Figure out if a NEF file is compressed. These fancy heuristics
+are only needed for the D100, thanks to a bug in some cameras
+that tags all images as "compressed".
+*/
+bool NefDecoder::D100IsCompressed(uint32 offset) {
+ const uchar8 *test = mFile->getData(offset, 256);
+ int i;
+
+ for (i = 15; i < 256; i += 16)
+ if (test[i])
+ return true;
+
+ return false;
+}
+
+/* At least the D810 has a broken firmware that tags uncompressed images
+ as if they were compressed. For those cases we set uncompressed mode
+ by figuring out that the image is the size of uncompressed packing */
+bool NefDecoder::NEFIsUncompressed(const TiffIFD* raw) {
+ TiffEntry* counts = raw->getEntry(STRIPBYTECOUNTS);
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ if (!width || !height || !bitPerPixel)
+ return false;
+
+ const auto avaliableInputBytes = counts->getU32(0);
+ const auto requiredPixels = iPoint2D(width, height).area();
+
+ // Now, there can be three situations.
+
+ // We might have not enough input to produce the requested image size.
+ const uint64 avaliableInputBits = uint64(8) * avaliableInputBytes;
+ const auto avaliablePixels = avaliableInputBits / bitPerPixel; // round down!
+ if (avaliablePixels < requiredPixels)
+ return false;
+
+ // We might have exactly enough input with no padding whatsoever.
+ if (avaliablePixels == requiredPixels)
+ return true;
+
+ // Or, we might have too much input. And sadly this is the worst case.
+ // We can't just accept this. Some *compressed* NEF's also pass this check :(
+ // Thus, let's accept *some* *small* padding.
+ const auto requiredInputBits = bitPerPixel * requiredPixels;
+ const auto requiredInputBytes = roundUpDivision(requiredInputBits, 8);
+ // While we might have more *pixels* than needed, it does not nessesairly mean
+ // that we have more input *bytes*. We might be off by a few pixels, and with
+ // small image dimensions and bpp, we might still be in the same byte.
+ assert(avaliableInputBytes >= requiredInputBytes);
+ const auto totalPadding = avaliableInputBytes - requiredInputBytes;
+ if (totalPadding % height != 0)
+ return false; // Inconsistent padding makes no sense here.
+ const auto perRowPadding = totalPadding / height;
+ return perRowPadding < 16;
+}
+
+/* At least the D810 has a broken firmware that tags uncompressed images
+ as if they were compressed. For those cases we set uncompressed mode
+ by figuring out that the image is the size of uncompressed packing */
+bool NefDecoder::NEFIsUncompressedRGB(const TiffIFD* raw) {
+ uint32 byteCount = raw->getEntry(STRIPBYTECOUNTS)->getU32(0);
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ if (byteCount % 3 != 0)
+ return false;
+
+ return byteCount / 3 == iPoint2D(width, height).area();
+}
+
+void NefDecoder::DecodeUncompressed() {
+ auto raw = getIFDWithLargestImage(CFAPATTERN);
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);
+ uint32 yPerSlice = raw->getEntry(ROWSPERSTRIP)->getU32();
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ mRaw->dim = iPoint2D(width, height);
+
+ if (width == 0 || height == 0 || width > 8288 || height > 5520)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (counts->count != offsets->count) {
+ ThrowRDE("Byte count number does not match strip size: "
+ "count:%u, stips:%u ",
+ counts->count, offsets->count);
+ }
+
+ if (yPerSlice == 0 || yPerSlice > static_cast<uint32>(mRaw->dim.y) ||
+ roundUpDivision(mRaw->dim.y, yPerSlice) != counts->count) {
+ ThrowRDE("Invalid y per slice %u or strip count %u (height = %u)",
+ yPerSlice, counts->count, mRaw->dim.y);
+ }
+
+ vector<NefSlice> slices;
+ slices.reserve(counts->count);
+ uint32 offY = 0;
+
+ for (uint32 s = 0; s < counts->count; s++) {
+ NefSlice slice;
+ slice.offset = offsets->getU32(s);
+ slice.count = counts->getU32(s);
+
+ if (slice.count < 1)
+ ThrowRDE("Slice %u is empty", s);
+
+ if (offY + yPerSlice > height)
+ slice.h = height - offY;
+ else
+ slice.h = yPerSlice;
+
+ offY = min(height, offY + yPerSlice);
+
+ if (!mFile->isValid(slice.offset, slice.count))
+ ThrowRDE("Slice offset/count invalid");
+
+ slices.push_back(slice);
+ }
+
+ if (slices.empty())
+ ThrowRDE("No valid slices found. File probably truncated.");
+
+ assert(height == offY);
+ assert(slices.size() == counts->count);
+
+ mRaw->createData();
+ if (bitPerPixel == 14 && width*slices[0].h*2 == slices[0].count)
+ bitPerPixel = 16; // D3 & D810
+
+ bitPerPixel = hints.get("real_bpp", bitPerPixel);
+
+ switch (bitPerPixel) {
+ case 12:
+ case 14:
+ case 16:
+ break;
+ default:
+ ThrowRDE("Invalid bpp found: %u", bitPerPixel);
+ }
+
+ bool bitorder = ! hints.has("msb_override");
+
+ offY = 0;
+ for (const NefSlice& slice : slices) {
+ ByteStream in(mFile, slice.offset, slice.count);
+ iPoint2D size(width, slice.h);
+ iPoint2D pos(0, offY);
+
+ if (hints.has("coolpixmangled")) {
+ UncompressedDecompressor u(in, mRaw);
+ u.readUncompressedRaw(size, pos, width * bitPerPixel / 8, 12,
+ BitOrder_MSB32);
+ } else {
+ if (hints.has("coolpixsplit"))
+ readCoolpixSplitRaw(in, size, pos, width * bitPerPixel / 8);
+ else {
+ UncompressedDecompressor u(in, mRaw);
+ if (in.getSize() % size.y != 0)
+ ThrowRDE("Inconsistent row size");
+ const auto inputPitchBytes = in.getSize() / size.y;
+ u.readUncompressedRaw(size, pos, inputPitchBytes, bitPerPixel,
+ bitorder ? BitOrder_MSB : BitOrder_LSB);
+ }
+ }
+
+ offY += slice.h;
+ }
+}
+
+void NefDecoder::readCoolpixSplitRaw(const ByteStream& input,
+ const iPoint2D& size,
+ const iPoint2D& offset, int inputPitch) {
+ uchar8* data = mRaw->getData();
+ uint32 outPitch = mRaw->pitch;
+ uint32 w = size.x;
+ uint32 h = size.y;
+ uint32 cpp = mRaw->getCpp();
+ if (input.getRemainSize() < (inputPitch*h)) {
+ if (static_cast<int>(input.getRemainSize()) > inputPitch)
+ h = input.getRemainSize() / inputPitch - 1;
+ else
+ ThrowIOE(
+ "Not enough data to decode a single line. Image file truncated.");
+ }
+
+ if (offset.y > mRaw->dim.y)
+ ThrowRDE("Invalid y offset");
+ if (offset.x + size.x > mRaw->dim.x)
+ ThrowRDE("Invalid x offset");
+
+ uint32 y = offset.y;
+ h = min(h + static_cast<uint32>(offset.y), static_cast<uint32>(mRaw->dim.y));
+ w *= cpp;
+ h /= 2;
+ BitPumpMSB in(input);
+ for (; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) * cpp + y * 2 * outPitch]);
+ for (uint32 x = 0 ; x < w; x++) {
+ dest[x] = in.getBits(12);
+ }
+ }
+ for (y = offset.y; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) * cpp + (y * 2 + 1) * outPitch]);
+ for (uint32 x = 0 ; x < w; x++) {
+ dest[x] = in.getBits(12);
+ }
+ }
+}
+
+void NefDecoder::DecodeD100Uncompressed() {
+ auto ifd = mRootIFD->getIFDWithTag(STRIPOFFSETS, 1);
+
+ uint32 offset = ifd->getEntry(STRIPOFFSETS)->getU32();
+ // Hardcode the sizes as at least the width is not correctly reported
+ uint32 width = 3040;
+ uint32 height = 2024;
+
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->createData();
+
+ UncompressedDecompressor u(*mFile, offset, mRaw);
+
+ u.decode12BitRaw<Endianness::big, false, true>(width, height);
+}
+
+void NefDecoder::DecodeSNefUncompressed() {
+ auto raw = getIFDWithLargestImage(CFAPATTERN);
+ uint32 offset = raw->getEntry(STRIPOFFSETS)->getU32();
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ if (width == 0 || height == 0 || width % 2 != 0 || width > 3680 ||
+ height > 2456)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->setCpp(3);
+ mRaw->isCFA = false;
+ mRaw->createData();
+
+ ByteStream in(mFile, offset);
+
+ DecodeNikonSNef(&in, width, height);
+}
+
+void NefDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ auto id = mRootIFD->getID();
+ string mode = getMode();
+ string extended_mode = getExtendedMode(mode);
+
+ if (meta->hasCamera(id.make, id.model, extended_mode))
+ checkCameraSupported(meta, id, extended_mode);
+ else
+ checkCameraSupported(meta, id, mode);
+}
+
+string NefDecoder::getMode() {
+ ostringstream mode;
+ auto raw = getIFDWithLargestImage(CFAPATTERN);
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ uint32 bitPerPixel = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ if (NEFIsUncompressedRGB(raw))
+ mode << "sNEF-uncompressed";
+ else {
+ if (1 == compression || NEFIsUncompressed(raw))
+ mode << bitPerPixel << "bit-uncompressed";
+ else
+ mode << bitPerPixel << "bit-compressed";
+ }
+ return mode.str();
+}
+
+string NefDecoder::getExtendedMode(const string &mode) {
+ ostringstream extended_mode;
+
+ auto ifd = mRootIFD->getIFDWithTag(CFAPATTERN);
+ uint32 width = ifd->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = ifd->getEntry(IMAGELENGTH)->getU32();
+
+ extended_mode << width << "x" << height << "-" << mode;
+ return extended_mode.str();
+}
+
+// We use this for the D50 and D2X whacky WB "encryption"
+const std::array<uchar8, 256> NefDecoder::serialmap = {
+ {0xc1, 0xbf, 0x6d, 0x0d, 0x59, 0xc5, 0x13, 0x9d, 0x83, 0x61, 0x6b, 0x4f,
+ 0xc7, 0x7f, 0x3d, 0x3d, 0x53, 0x59, 0xe3, 0xc7, 0xe9, 0x2f, 0x95, 0xa7,
+ 0x95, 0x1f, 0xdf, 0x7f, 0x2b, 0x29, 0xc7, 0x0d, 0xdf, 0x07, 0xef, 0x71,
+ 0x89, 0x3d, 0x13, 0x3d, 0x3b, 0x13, 0xfb, 0x0d, 0x89, 0xc1, 0x65, 0x1f,
+ 0xb3, 0x0d, 0x6b, 0x29, 0xe3, 0xfb, 0xef, 0xa3, 0x6b, 0x47, 0x7f, 0x95,
+ 0x35, 0xa7, 0x47, 0x4f, 0xc7, 0xf1, 0x59, 0x95, 0x35, 0x11, 0x29, 0x61,
+ 0xf1, 0x3d, 0xb3, 0x2b, 0x0d, 0x43, 0x89, 0xc1, 0x9d, 0x9d, 0x89, 0x65,
+ 0xf1, 0xe9, 0xdf, 0xbf, 0x3d, 0x7f, 0x53, 0x97, 0xe5, 0xe9, 0x95, 0x17,
+ 0x1d, 0x3d, 0x8b, 0xfb, 0xc7, 0xe3, 0x67, 0xa7, 0x07, 0xf1, 0x71, 0xa7,
+ 0x53, 0xb5, 0x29, 0x89, 0xe5, 0x2b, 0xa7, 0x17, 0x29, 0xe9, 0x4f, 0xc5,
+ 0x65, 0x6d, 0x6b, 0xef, 0x0d, 0x89, 0x49, 0x2f, 0xb3, 0x43, 0x53, 0x65,
+ 0x1d, 0x49, 0xa3, 0x13, 0x89, 0x59, 0xef, 0x6b, 0xef, 0x65, 0x1d, 0x0b,
+ 0x59, 0x13, 0xe3, 0x4f, 0x9d, 0xb3, 0x29, 0x43, 0x2b, 0x07, 0x1d, 0x95,
+ 0x59, 0x59, 0x47, 0xfb, 0xe5, 0xe9, 0x61, 0x47, 0x2f, 0x35, 0x7f, 0x17,
+ 0x7f, 0xef, 0x7f, 0x95, 0x95, 0x71, 0xd3, 0xa3, 0x0b, 0x71, 0xa3, 0xad,
+ 0x0b, 0x3b, 0xb5, 0xfb, 0xa3, 0xbf, 0x4f, 0x83, 0x1d, 0xad, 0xe9, 0x2f,
+ 0x71, 0x65, 0xa3, 0xe5, 0x07, 0x35, 0x3d, 0x0d, 0xb5, 0xe9, 0xe5, 0x47,
+ 0x3b, 0x9d, 0xef, 0x35, 0xa3, 0xbf, 0xb3, 0xdf, 0x53, 0xd3, 0x97, 0x53,
+ 0x49, 0x71, 0x07, 0x35, 0x61, 0x71, 0x2f, 0x43, 0x2f, 0x11, 0xdf, 0x17,
+ 0x97, 0xfb, 0x95, 0x3b, 0x7f, 0x6b, 0xd3, 0x25, 0xbf, 0xad, 0xc7, 0xc5,
+ 0xc5, 0xb5, 0x8b, 0xef, 0x2f, 0xd3, 0x07, 0x6b, 0x25, 0x49, 0x95, 0x25,
+ 0x49, 0x6d, 0x71, 0xc7}};
+const std::array<uchar8, 256> NefDecoder::keymap = {
+ {0xa7, 0xbc, 0xc9, 0xad, 0x91, 0xdf, 0x85, 0xe5, 0xd4, 0x78, 0xd5, 0x17,
+ 0x46, 0x7c, 0x29, 0x4c, 0x4d, 0x03, 0xe9, 0x25, 0x68, 0x11, 0x86, 0xb3,
+ 0xbd, 0xf7, 0x6f, 0x61, 0x22, 0xa2, 0x26, 0x34, 0x2a, 0xbe, 0x1e, 0x46,
+ 0x14, 0x68, 0x9d, 0x44, 0x18, 0xc2, 0x40, 0xf4, 0x7e, 0x5f, 0x1b, 0xad,
+ 0x0b, 0x94, 0xb6, 0x67, 0xb4, 0x0b, 0xe1, 0xea, 0x95, 0x9c, 0x66, 0xdc,
+ 0xe7, 0x5d, 0x6c, 0x05, 0xda, 0xd5, 0xdf, 0x7a, 0xef, 0xf6, 0xdb, 0x1f,
+ 0x82, 0x4c, 0xc0, 0x68, 0x47, 0xa1, 0xbd, 0xee, 0x39, 0x50, 0x56, 0x4a,
+ 0xdd, 0xdf, 0xa5, 0xf8, 0xc6, 0xda, 0xca, 0x90, 0xca, 0x01, 0x42, 0x9d,
+ 0x8b, 0x0c, 0x73, 0x43, 0x75, 0x05, 0x94, 0xde, 0x24, 0xb3, 0x80, 0x34,
+ 0xe5, 0x2c, 0xdc, 0x9b, 0x3f, 0xca, 0x33, 0x45, 0xd0, 0xdb, 0x5f, 0xf5,
+ 0x52, 0xc3, 0x21, 0xda, 0xe2, 0x22, 0x72, 0x6b, 0x3e, 0xd0, 0x5b, 0xa8,
+ 0x87, 0x8c, 0x06, 0x5d, 0x0f, 0xdd, 0x09, 0x19, 0x93, 0xd0, 0xb9, 0xfc,
+ 0x8b, 0x0f, 0x84, 0x60, 0x33, 0x1c, 0x9b, 0x45, 0xf1, 0xf0, 0xa3, 0x94,
+ 0x3a, 0x12, 0x77, 0x33, 0x4d, 0x44, 0x78, 0x28, 0x3c, 0x9e, 0xfd, 0x65,
+ 0x57, 0x16, 0x94, 0x6b, 0xfb, 0x59, 0xd0, 0xc8, 0x22, 0x36, 0xdb, 0xd2,
+ 0x63, 0x98, 0x43, 0xa1, 0x04, 0x87, 0x86, 0xf7, 0xa6, 0x26, 0xbb, 0xd6,
+ 0x59, 0x4d, 0xbf, 0x6a, 0x2e, 0xaa, 0x2b, 0xef, 0xe6, 0x78, 0xb6, 0x4e,
+ 0xe0, 0x2f, 0xdc, 0x7c, 0xbe, 0x57, 0x19, 0x32, 0x7e, 0x2a, 0xd0, 0xb8,
+ 0xba, 0x29, 0x00, 0x3c, 0x52, 0x7d, 0xa8, 0x49, 0x3b, 0x2d, 0xeb, 0x25,
+ 0x49, 0xfa, 0xa3, 0xaa, 0x39, 0xa7, 0xc5, 0xa7, 0x50, 0x11, 0x36, 0xfb,
+ 0xc6, 0x67, 0x4a, 0xf5, 0xa5, 0x12, 0x65, 0x7e, 0xb0, 0xdf, 0xaf, 0x4e,
+ 0xb3, 0x61, 0x7f, 0x2f}};
+
+void NefDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+
+ int white = mRaw->whitePoint;
+ int black = mRaw->blackLevel;
+
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ // Read the whitebalance
+
+ if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(12))) {
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(12));
+ if (wb->count == 4) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(2);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(1);
+ if (mRaw->metadata.wbCoeffs[1] <= 0.0F)
+ mRaw->metadata.wbCoeffs[1] = 1.0F;
+ }
+ } else if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x0097))) {
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x0097));
+ if (wb->count > 4) {
+ uint32 version = 0;
+ for (uint32 i = 0; i < 4; i++) {
+ const auto v = wb->getByte(i);
+ if (v < '0' || v > '9')
+ ThrowRDE("Bad version component: %c - not a digit", v);
+ version = (version << 4) + v - '0';
+ }
+
+ if (version == 0x100 && wb->count >= 80 && wb->type == TIFF_UNDEFINED) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(wb->getU16(36));
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(wb->getU16(37));
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(wb->getU16(38));
+ } else if (version == 0x103 && wb->count >= 26 && wb->type == TIFF_UNDEFINED) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(wb->getU16(10));
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(wb->getU16(11));
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(wb->getU16(12));
+ } else if (((version == 0x204 && wb->count >= 564) ||
+ (version == 0x205 && wb->count >= 284)) &&
+ mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x001d)) &&
+ mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x00a7))) {
+ // Get the serial number
+ string serial =
+ mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x001d))
+ ->getString();
+ if (serial.length() > 9)
+ ThrowRDE("Serial number is too long (%zu)", serial.length());
+ uint32 serialno = 0;
+ for (unsigned char c : serial) {
+ if (c >= '0' && c <= '9')
+ serialno = serialno*10 + c-'0';
+ else
+ serialno = serialno*10 + c%10;
+ }
+
+ // Get the decryption key
+ TiffEntry* key =
+ mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x00a7));
+ const uchar8 *keydata = key->getData(4);
+ uint32 keyno = keydata[0]^keydata[1]^keydata[2]^keydata[3];
+
+ // "Decrypt" the block using the serial and key
+ uchar8 ci = serialmap[serialno & 0xff];
+ uchar8 cj = keymap[keyno & 0xff];
+ uchar8 ck = 0x60;
+
+ ByteStream bs = wb->getData();
+ bs.skipBytes(version == 0x204 ? 284 : 4);
+
+ std::array<uchar8, 14 + 8> buf;
+ for (unsigned char& i : buf) {
+ cj = uchar8(cj + ci * ck); // modulo arithmetics.
+ i = bs.getByte() ^ cj;
+ ck++;
+ }
+
+ // Finally set the WB coeffs
+ uint32 off = (version == 0x204) ? 6 : 14;
+ mRaw->metadata.wbCoeffs[0] =
+ static_cast<float>(getU16BE(buf.data() + off + 0));
+ mRaw->metadata.wbCoeffs[1] =
+ static_cast<float>(getU16BE(buf.data() + off + 2));
+ mRaw->metadata.wbCoeffs[2] =
+ static_cast<float>(getU16BE(buf.data() + off + 6));
+ }
+ }
+ } else if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x0014))) {
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x0014));
+ ByteStream bs = wb->getData();
+ if (wb->count == 2560 && wb->type == TIFF_UNDEFINED) {
+ bs.skipBytes(1248);
+ bs.setByteOrder(Endianness::big);
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(bs.getU16()) / 256.0;
+ mRaw->metadata.wbCoeffs[1] = 1.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(bs.getU16()) / 256.0;
+ } else if (bs.hasPatternAt("NRW ", 4, 0)) {
+ uint32 offset = 0;
+ if (!bs.hasPatternAt("0100", 4, 4) && wb->count > 72)
+ offset = 56;
+ else if (wb->count > 1572)
+ offset = 1556;
+
+ if (offset) {
+ bs.skipBytes(offset);
+ bs.setByteOrder(Endianness::little);
+ mRaw->metadata.wbCoeffs[0] = 4.0 * bs.getU32();
+ mRaw->metadata.wbCoeffs[1] = bs.getU32();
+ mRaw->metadata.wbCoeffs[1] += bs.getU32();
+ mRaw->metadata.wbCoeffs[2] = 4.0 * bs.getU32();
+ }
+ }
+ }
+
+ if (hints.has("nikon_wb_adjustment")) {
+ mRaw->metadata.wbCoeffs[0] *= 256/527.0;
+ mRaw->metadata.wbCoeffs[2] *= 256/317.0;
+ }
+
+ auto id = mRootIFD->getID();
+ string mode = getMode();
+ string extended_mode = getExtendedMode(mode);
+ if (meta->hasCamera(id.make, id.model, extended_mode)) {
+ setMetaData(meta, id, extended_mode, iso);
+ } else if (meta->hasCamera(id.make, id.model, mode)) {
+ setMetaData(meta, id, mode, iso);
+ } else {
+ setMetaData(meta, id, "", iso);
+ }
+
+ if (white != 65536)
+ mRaw->whitePoint = white;
+ if (black != -1)
+ mRaw->blackLevel = black;
+}
+
+
+// DecodeNikonYUY2 decodes 12 bit data in an YUY2-like pattern (2 Luma, 1 Chroma per 2 pixels).
+// We un-apply the whitebalance, so output matches lossless.
+// Note that values are scaled. See comment below on details.
+// OPTME: It would be trivial to run this multithreaded.
+void NefDecoder::DecodeNikonSNef(ByteStream* input, uint32 w, uint32 h) {
+ if (w < 6)
+ ThrowIOE("got a %u wide sNEF, aborting", w);
+
+ if (input->getRemainSize() < (w * h * 3))
+ ThrowIOE("Not enough data to decode. Image file truncated.");
+
+ // We need to read the applied whitebalance, since we should return
+ // data before whitebalance, so we "unapply" it.
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(12));
+ if (!wb)
+ ThrowRDE("Unable to locate whitebalance needed for decompression");
+
+ assert(wb != nullptr);
+ if (wb->count != 4 || wb->type != TIFF_RATIONAL)
+ ThrowRDE("Whitebalance has unknown count or type");
+
+ float wb_r = wb->getFloat(0);
+ float wb_b = wb->getFloat(1);
+
+ // ((1024/x)*((1<<16)-1)+(1<<9))<=((1<<31)-1), x>0 gives: (0.0312495)
+ const float lower_limit = 13'421'568.0 / 429'496'627.0;
+ if (wb_r < lower_limit || wb_b < lower_limit || wb_r > 10.0F || wb_b > 10.0F)
+ ThrowRDE("Whitebalance has bad values (%f, %f)", wb_r, wb_b);
+
+ mRaw->metadata.wbCoeffs[0] = wb_r;
+ mRaw->metadata.wbCoeffs[1] = 1.0F;
+ mRaw->metadata.wbCoeffs[2] = wb_b;
+
+ auto inv_wb_r = static_cast<int>(1024.0 / wb_r);
+ auto inv_wb_b = static_cast<int>(1024.0 / wb_b);
+
+ auto curve = gammaCurve(1 / 2.4, 12.92, 1, 4095);
+
+ // Scale output values to 16 bits.
+ for (int i = 0 ; i < 4096; i++) {
+ curve[i] = clampBits(static_cast<int>(curve[i]) << 2, 16);
+ }
+
+ curve.resize(4095);
+
+ RawImageCurveGuard curveHandler(&mRaw, curve, false);
+
+ ushort16 tmp;
+ auto* tmpch = reinterpret_cast<uchar8*>(&tmp);
+
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+ const uchar8* in = input->getData(w * h * 3);
+
+ for (uint32 y = 0; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+ uint32 random = in[0] + (in[1] << 8) + (in[2] << 16);
+ for (uint32 x = 0 ; x < w*3; x += 6) {
+ uint32 g1 = in[0];
+ uint32 g2 = in[1];
+ uint32 g3 = in[2];
+ uint32 g4 = in[3];
+ uint32 g5 = in[4];
+ uint32 g6 = in[5];
+
+ in+=6;
+ auto y1 = static_cast<float>(g1 | ((g2 & 0x0f) << 8));
+ auto y2 = static_cast<float>((g2 >> 4) | (g3 << 4));
+ auto cb = static_cast<float>(g4 | ((g5 & 0x0f) << 8));
+ auto cr = static_cast<float>((g5 >> 4) | (g6 << 4));
+
+ float cb2 = cb;
+ float cr2 = cr;
+ // Interpolate right pixel. We assume the sample is aligned with left pixel.
+ if ((x+6) < w*3) {
+ g4 = in[3];
+ g5 = in[4];
+ g6 = in[5];
+ cb2 = (static_cast<float>((g4 | ((g5 & 0x0f) << 8))) + cb) * 0.5F;
+ cr2 = (static_cast<float>(((g5 >> 4) | (g6 << 4))) + cr) * 0.5F;
+ }
+
+ cb -= 2048;
+ cr -= 2048;
+ cb2 -= 2048;
+ cr2 -= 2048;
+
+ mRaw->setWithLookUp(clampBits(static_cast<int>(y1 + 1.370705 * cr), 12),
+ tmpch, &random);
+ dest[x] = clampBits((inv_wb_r * tmp + (1<<9)) >> 10, 15);
+
+ mRaw->setWithLookUp(
+ clampBits(static_cast<int>(y1 - 0.337633 * cb - 0.698001 * cr), 12),
+ reinterpret_cast<uchar8*>(&dest[x + 1]), &random);
+
+ mRaw->setWithLookUp(clampBits(static_cast<int>(y1 + 1.732446 * cb), 12),
+ tmpch, &random);
+ dest[x+2] = clampBits((inv_wb_b * tmp + (1<<9)) >> 10, 15);
+
+ mRaw->setWithLookUp(clampBits(static_cast<int>(y2 + 1.370705 * cr2), 12),
+ tmpch, &random);
+ dest[x+3] = clampBits((inv_wb_r * tmp + (1<<9)) >> 10, 15);
+
+ mRaw->setWithLookUp(
+ clampBits(static_cast<int>(y2 - 0.337633 * cb2 - 0.698001 * cr2), 12),
+ reinterpret_cast<uchar8*>(&dest[x + 4]), &random);
+
+ mRaw->setWithLookUp(clampBits(static_cast<int>(y2 + 1.732446 * cb2), 12),
+ tmpch, &random);
+ dest[x+5] = clampBits((inv_wb_b * tmp + (1<<9)) >> 10, 15);
+ }
+ }
+}
+
+// From: dcraw.c -- Dave Coffin's raw photo decoder
+#define SQR(x) ((x)*(x))
+std::vector<ushort16> NefDecoder::gammaCurve(double pwr, double ts, int mode,
+ int imax) {
+ std::vector<ushort16> curve(65536);
+
+ int i;
+ std::array<double, 6> g;
+ std::array<double, 2> bnd = {{}};
+ double r;
+ g[0] = pwr;
+ g[1] = ts;
+ g[2] = g[3] = g[4] = 0;
+ bnd[g[1] >= 1] = 1;
+ if (g[1] && (g[1]-1)*(g[0]-1) <= 0) {
+ for (i=0; i < 48; i++) {
+ g[2] = (bnd[0] + bnd[1])/2;
+ if (g[0])
+ bnd[(pow(g[2] / g[1], -g[0]) - 1) / g[0] - 1 / g[2] > -1] = g[2];
+ else
+ bnd[g[2] / exp(1 - 1 / g[2]) < g[1]] = g[2];
+ }
+ g[3] = g[2] / g[1];
+ if (g[0])
+ g[4] = g[2] * (1 / g[0] - 1);
+ }
+ if (g[0]) {
+ g[5] = 1 / (g[1] * SQR(g[3]) / 2 - g[4] * (1 - g[3]) +
+ (1 - pow(g[3], 1 + g[0])) * (1 + g[4]) / (1 + g[0])) -
+ 1;
+ } else {
+ g[5] = 1 / (g[1] * SQR(g[3]) / 2 + 1 - g[2] - g[3] -
+ g[2] * g[3] * (log(g[3]) - 1)) -
+ 1;
+ }
+
+ if (mode == 0)
+ ThrowRDE("Unimplemented mode");
+
+ mode--;
+
+ for (i=0; i < 0x10000; i++) {
+ curve[i] = 0xffff;
+ if ((r = static_cast<double>(i) / imax) < 1) {
+ curve[i] = static_cast<ushort16>(
+ 0x10000 *
+ (mode ? (r < g[3] ? r * g[1]
+ : (g[0] ? pow(r, g[0]) * (1 + g[4]) - g[4]
+ : log(r) * g[2] + 1))
+ : (r < g[2] ? r / g[1]
+ : (g[0] ? pow((r + g[4]) / (1 + g[4]), 1 / g[0])
+ : exp((r - 1) / g[2])))));
+ }
+ }
+
+ assert(curve.size() == 65536);
+
+ return curve;
+}
+#undef SQR
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.h
new file mode 100644
index 00000000..6acc73e1
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/NefDecoder.h
@@ -0,0 +1,75 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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, ushort16
+#include "common/RawImage.h" // for RawImage
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "decoders/RawDecoder.h" // for RawDecoder::RawSlice
+#include "tiff/TiffIFD.h" // for TiffIFD (ptr only), TiffRo...
+#include <array> // for array
+#include <string> // for string
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class ByteStream;
+class CameraMetaData;
+class iPoint2D;
+class Buffer;
+
+class NefDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ NefDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+
+protected:
+ struct NefSlice final : RawSlice {};
+
+private:
+ int getDecoderVersion() const override { return 5; }
+ bool D100IsCompressed(uint32 offset);
+ bool NEFIsUncompressed(const TiffIFD* raw);
+ bool NEFIsUncompressedRGB(const TiffIFD* raw);
+ void DecodeUncompressed();
+ void DecodeD100Uncompressed();
+ void DecodeSNefUncompressed();
+ void readCoolpixSplitRaw(const ByteStream& input, const iPoint2D& size,
+ const iPoint2D& offset, int inputPitch);
+ void DecodeNikonSNef(ByteStream* input, uint32 w, uint32 h);
+ std::string getMode();
+ std::string getExtendedMode(const std::string &mode);
+ std::vector<ushort16> gammaCurve(double pwr, double ts, int mode, int imax);
+
+ // We use this for the D50 and D2X whacky WB "encryption"
+ static const std::array<uchar8, 256> serialmap;
+ static const std::array<uchar8, 256> keymap;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.cpp
new file mode 100644
index 00000000..14da3a87
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.cpp
@@ -0,0 +1,282 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decoders/OrfDecoder.h"
+#include "common/Common.h" // for uint32, uchar8
+#include "common/NORangesSet.h" // for set
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/OlympusDecompressor.h" // for OlympusDecompressor
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, getH...
+#include "metadata/ColorFilterArray.h" // for ColorFilterArray
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_...
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for STRIPOFFSETS
+#include <array> // for array
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool OrfDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "OLYMPUS IMAGING CORP." || make == "OLYMPUS CORPORATION" ||
+ make == "OLYMPUS OPTICAL CO.,LTD";
+}
+
+ByteStream OrfDecoder::handleSlices() const {
+ auto raw = mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);
+
+ if (counts->count != offsets->count) {
+ ThrowRDE(
+ "Byte count number does not match strip size: count:%u, strips:%u ",
+ counts->count, offsets->count);
+ }
+
+ const uint32 off = offsets->getU32(0);
+ uint32 size = counts->getU32(0);
+ auto end = [&off, &size]() -> uint32 { return off + size; };
+
+ for (uint32 i = 0; i < counts->count; i++) {
+ const auto offset = offsets->getU32(i);
+ const auto count = counts->getU32(i);
+ if (!mFile->isValid(offset, count))
+ ThrowRDE("Truncated file");
+
+ if (count < 1)
+ ThrowRDE("Empty slice");
+
+ if (i == 0)
+ continue;
+
+ if (offset < end())
+ ThrowRDE("Slices overlap");
+
+ // Now, everything would be great, but some uncompressed raws
+ // (packed_with_control i believe) have "padding" between at least
+ // the first two slices, and we need to account for it.
+ const uint32 padding = offset - end();
+
+ size += padding;
+ size += count;
+ }
+
+ ByteStream input(offsets->getRootIfdData());
+ input.setPosition(off);
+
+ return input.getStream(size);
+}
+
+RawImage OrfDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ if (1 != compression)
+ ThrowRDE("Unsupported compression");
+
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ if (!width || !height || width % 2 != 0 || width > 10400 || height > 7796)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ mRaw->dim = iPoint2D(width, height);
+
+ ByteStream input(handleSlices());
+
+ if (decodeUncompressed(input, width, height, input.getSize()))
+ return mRaw;
+
+ if (raw->getEntry(STRIPOFFSETS)->count != 1)
+ ThrowRDE("%u stripes, and not uncompressed. Unsupported.",
+ raw->getEntry(STRIPOFFSETS)->count);
+
+ OlympusDecompressor o(mRaw);
+ mRaw->createData();
+ o.decompress(std::move(input));
+
+ return mRaw;
+}
+
+bool OrfDecoder::decodeUncompressed(const ByteStream& s, uint32 w, uint32 h,
+ uint32 size) {
+ UncompressedDecompressor u(s, mRaw);
+ // FIXME: most of this logic should be in UncompressedDecompressor,
+ // one way or another.
+
+ if (size == h * ((w * 12 / 8) + ((w + 2) / 10))) {
+ // 12-bit packed 'with control' raw
+ mRaw->createData();
+ u.decode12BitRaw<Endianness::little, false, true>(w, h);
+ return true;
+ }
+
+ if (size == w * h * 12 / 8) { // We're in a 12-bit packed raw
+ iPoint2D dimensions(w, h);
+ iPoint2D pos(0, 0);
+ mRaw->createData();
+ u.readUncompressedRaw(dimensions, pos, w * 12 / 8, 12, BitOrder_MSB32);
+ return true;
+ }
+
+ if (size == w * h * 2) { // We're in an unpacked raw
+ mRaw->createData();
+ // FIXME: seems fishy
+ if (s.getByteOrder() == getHostEndianness())
+ u.decodeRawUnpacked<12, Endianness::little>(w, h);
+ else
+ u.decode12BitRawUnpackedLeftAligned<Endianness::big>(w, h);
+ return true;
+ }
+
+ if (size >
+ w * h * 3 / 2) { // We're in one of those weird interlaced packed raws
+ mRaw->createData();
+ u.decode12BitRaw<Endianness::big, true>(w, h);
+ return true;
+ }
+
+ // Does not appear to be uncomporessed. Maybe it's compressed?
+ return false;
+}
+
+void OrfDecoder::parseCFA() {
+ if (!mRootIFD->hasEntryRecursive(EXIFCFAPATTERN))
+ ThrowRDE("No EXIFCFAPATTERN entry found!");
+
+ TiffEntry* CFA = mRootIFD->getEntryRecursive(EXIFCFAPATTERN);
+ if (CFA->type != TiffDataType::TIFF_UNDEFINED || CFA->count != 8) {
+ ThrowRDE("Bad EXIFCFAPATTERN entry (type %u, count %u).", CFA->type,
+ CFA->count);
+ }
+
+ iPoint2D cfaSize(CFA->getU16(0), CFA->getU16(1));
+ if (cfaSize != iPoint2D{2, 2})
+ ThrowRDE("Bad CFA size: (%i, %i)", cfaSize.x, cfaSize.y);
+
+ mRaw->cfa.setSize(cfaSize);
+
+ auto int2enum = [](uchar8 i) -> CFAColor {
+ switch (i) {
+ case 0:
+ return CFA_RED;
+ case 1:
+ return CFA_GREEN;
+ case 2:
+ return CFA_BLUE;
+ default:
+ ThrowRDE("Unexpected CFA color: %u", i);
+ }
+ };
+
+ for (int y = 0; y < cfaSize.y; y++) {
+ for (int x = 0; x < cfaSize.x; x++) {
+ uchar8 c1 = CFA->getByte(4 + x + y * cfaSize.x);
+ CFAColor c2 = int2enum(c1);
+ mRaw->cfa.setColorAt(iPoint2D(x, y), c2);
+ }
+ }
+}
+
+void OrfDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ parseCFA();
+
+ setMetaData(meta, "", iso);
+
+ if (mRootIFD->hasEntryRecursive(OLYMPUSREDMULTIPLIER) &&
+ mRootIFD->hasEntryRecursive(OLYMPUSBLUEMULTIPLIER)) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(
+ mRootIFD->getEntryRecursive(OLYMPUSREDMULTIPLIER)->getU16());
+ mRaw->metadata.wbCoeffs[1] = 256.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(
+ mRootIFD->getEntryRecursive(OLYMPUSBLUEMULTIPLIER)->getU16());
+ } else if (mRootIFD->hasEntryRecursive(OLYMPUSIMAGEPROCESSING)) {
+ // Newer cameras process the Image Processing SubIFD in the makernote
+ TiffEntry* img_entry = mRootIFD->getEntryRecursive(OLYMPUSIMAGEPROCESSING);
+ // get makernote ifd with containing Buffer
+ NORangesSet<Buffer> ifds;
+
+ TiffRootIFD image_processing(nullptr, &ifds, img_entry->getRootIfdData(),
+ img_entry->getU32());
+
+ // Get the WB
+ if (image_processing.hasEntry(static_cast<TiffTag>(0x0100))) {
+ TiffEntry* wb = image_processing.getEntry(static_cast<TiffTag>(0x0100));
+ if (wb->count == 2 || wb->count == 4) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = 256.0F;
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(1);
+ }
+ }
+
+ // Get the black levels
+ if (image_processing.hasEntry(static_cast<TiffTag>(0x0600))) {
+ TiffEntry* blackEntry =
+ image_processing.getEntry(static_cast<TiffTag>(0x0600));
+ // Order is assumed to be RGGB
+ if (blackEntry->count == 4) {
+ for (int i = 0; i < 4; i++) {
+ auto c = mRaw->cfa.getColorAt(i & 1, i >> 1);
+ int j;
+ switch (c) {
+ case CFA_RED:
+ j = 0;
+ break;
+ case CFA_GREEN:
+ j = i < 2 ? 1 : 2;
+ break;
+ case CFA_BLUE:
+ j = 3;
+ break;
+ default:
+ ThrowRDE("Unexpected CFA color: %u", c);
+ }
+
+ mRaw->blackLevelSeparate[i] = blackEntry->getU16(j);
+ }
+ // Adjust whitelevel based on the read black (we assume the dynamic
+ // range is the same)
+ mRaw->whitePoint -= (mRaw->blackLevel - mRaw->blackLevelSeparate[0]);
+ }
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.h
new file mode 100644
index 00000000..ba255486
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/OrfDecoder.h
@@ -0,0 +1,56 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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 "common/RawImage.h" // for RawImage
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "io/ByteStream.h" // for ByteStream
+#include "tiff/TiffIFD.h" // for TiffRootIFD (ptr only)
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class OrfDecoder final : public AbstractTiffDecoder
+{
+ ByteStream handleSlices() const;
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ OrfDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+private:
+ void parseCFA();
+
+ int getDecoderVersion() const override { return 3; }
+ bool decodeUncompressed(const ByteStream& s, uint32 w, uint32 h, uint32 size);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.cpp
new file mode 100644
index 00000000..2a4d3658
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.cpp
@@ -0,0 +1,128 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+
+ 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/PefDecoder.h"
+#include "common/Common.h" // for uint32, BitOrder_MSB
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/PentaxDecompressor.h" // for PentaxDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_UNDEFINED
+#include "tiff/TiffIFD.h" // for TiffRootIFD, TiffIFD
+#include "tiff/TiffTag.h" // for TiffTag, ISOSPEEDRATINGS
+#include <array> // for array
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+bool PefDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "PENTAX Corporation" ||
+ make == "RICOH IMAGING COMPANY, LTD." || make == "PENTAX";
+}
+
+RawImage PefDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+
+ if (1 == compression || compression == 32773) {
+ decodeUncompressed(raw, BitOrder_MSB);
+ return mRaw;
+ }
+
+ if (65535 != compression)
+ ThrowRDE("Unsupported compression");
+
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(STRIPBYTECOUNTS);
+
+ if (offsets->count != 1) {
+ ThrowRDE("Multiple Strips found: %u", offsets->count);
+ }
+ if (counts->count != offsets->count) {
+ ThrowRDE(
+ "Byte count number does not match strip size: count:%u, strips:%u ",
+ counts->count, offsets->count);
+ }
+ ByteStream bs(mFile, offsets->getU32(), counts->getU32());
+
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+
+ mRaw->dim = iPoint2D(width, height);
+
+ ByteStream* metaData = nullptr;
+ ByteStream stream;
+ if (getRootIFD()->hasEntryRecursive(static_cast<TiffTag>(0x220))) {
+ /* Attempt to read huffman table, if found in makernote */
+ TiffEntry* t = getRootIFD()->getEntryRecursive(static_cast<TiffTag>(0x220));
+ if (t->type != TIFF_UNDEFINED)
+ ThrowRDE("Unknown Huffman table type.");
+
+ stream = t->getData();
+ metaData = &stream;
+ }
+
+ PentaxDecompressor p(mRaw, metaData);
+ mRaw->createData();
+ p.decompress(bs);
+
+ return mRaw;
+}
+
+void PefDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ setMetaData(meta, "", iso);
+
+ // Read black level
+ if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x200))) {
+ TiffEntry* black = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x200));
+ if (black->count == 4) {
+ for (int i = 0; i < 4; i++)
+ mRaw->blackLevelSeparate[i] = black->getU32(i);
+ }
+ }
+
+ // Set the whitebalance
+ if (mRootIFD->hasEntryRecursive(static_cast<TiffTag>(0x0201))) {
+ TiffEntry* wb = mRootIFD->getEntryRecursive(static_cast<TiffTag>(0x0201));
+ if (wb->count == 4) {
+ mRaw->metadata.wbCoeffs[0] = wb->getU32(0);
+ mRaw->metadata.wbCoeffs[1] = wb->getU32(1);
+ mRaw->metadata.wbCoeffs[2] = wb->getU32(3);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.h
new file mode 100644
index 00000000..99b8f3e8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/PefDecoder.h
@@ -0,0 +1,48 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class PefDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ PefDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 3; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.cpp
new file mode 100644
index 00000000..519c5814
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.cpp
@@ -0,0 +1,352 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+
+ 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/RafDecoder.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "common/Point.h" // for iPoint2D, iRecta...
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/FujiDecompressor.h" // for FujiDecompressor
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, getH...
+#include "metadata/BlackArea.h" // for BlackArea
+#include "metadata/Camera.h" // for Camera, Hints
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "metadata/CameraSensorInfo.h" // for CameraSensorInfo
+#include "metadata/ColorFilterArray.h" // for ColorFilterArray
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for FUJI_RAWIMAGEFUL...
+#include <array> // for array
+#include <cassert> // for assert
+#include <cstdio> // for size_t
+#include <cstring> // for memcmp
+#include <memory> // for unique_ptr
+#include <string> // for string, operator==
+#include <vector> // for vector
+
+namespace rawspeed {
+
+bool RafDecoder::isRAF(const Buffer* input) {
+ static const std::array<char, 16> magic = {{'F', 'U', 'J', 'I', 'F', 'I', 'L',
+ 'M', 'C', 'C', 'D', '-', 'R', 'A',
+ 'W', ' '}};
+ const unsigned char* data = input->getData(0, magic.size());
+ return 0 == memcmp(data, magic.data(), magic.size());
+}
+
+bool RafDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "FUJIFILM";
+}
+
+RawImage RafDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(FUJI_STRIPOFFSETS);
+ uint32 height = 0;
+ uint32 width = 0;
+
+ if (raw->hasEntry(FUJI_RAWIMAGEFULLHEIGHT)) {
+ height = raw->getEntry(FUJI_RAWIMAGEFULLHEIGHT)->getU32();
+ width = raw->getEntry(FUJI_RAWIMAGEFULLWIDTH)->getU32();
+ } else if (raw->hasEntry(IMAGEWIDTH)) {
+ TiffEntry *e = raw->getEntry(IMAGEWIDTH);
+ height = e->getU16(0);
+ width = e->getU16(1);
+ } else
+ ThrowRDE("Unable to locate image size");
+
+ if (width == 0 || height == 0 || width > 9216 || height > 6210)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (raw->hasEntry(FUJI_LAYOUT)) {
+ TiffEntry *e = raw->getEntry(FUJI_LAYOUT);
+ alt_layout = !(e->getByte(0) >> 7);
+ }
+
+ TiffEntry *offsets = raw->getEntry(FUJI_STRIPOFFSETS);
+ TiffEntry *counts = raw->getEntry(FUJI_STRIPBYTECOUNTS);
+
+ if (offsets->count != 1 || counts->count != 1)
+ ThrowRDE("Multiple Strips found: %u %u", offsets->count, counts->count);
+
+ ByteStream input(offsets->getRootIfdData());
+ input = input.getSubStream(offsets->getU32(), counts->getU32());
+
+ if (isCompressed()) {
+ mRaw->metadata.mode = "compressed";
+
+ mRaw->dim = iPoint2D(width, height);
+
+ FujiDecompressor f(mRaw, input);
+
+ mRaw->createData();
+
+ f.decompress();
+
+ return mRaw;
+ }
+
+ // x-trans sensors report 14bpp, but data isn't packed
+ // thus, unless someone has any better ideas, let's autodetect it.
+ int bps;
+
+ // Some fuji SuperCCD cameras include a second raw image next to the first one
+ // that is identical but darker to the first. The two combined can produce
+ // a higher dynamic range image. Right now we're ignoring it.
+ bool double_width;
+
+ assert(!isCompressed());
+
+ if (8UL * counts->getU32() >= 2UL * 16UL * width * height) {
+ bps = 16;
+ double_width = true;
+ } else if (8UL * counts->getU32() >= 2UL * 14UL * width * height) {
+ bps = 14;
+ double_width = true;
+ } else if (8UL * counts->getU32() >= 2UL * 12UL * width * height) {
+ bps = 12;
+ double_width = true;
+ } else if (8UL * counts->getU32() >= 16UL * width * height) {
+ bps = 16;
+ double_width = false;
+ } else if (8UL * counts->getU32() >= 14UL * width * height) {
+ bps = 14;
+ double_width = false;
+ } else if (8UL * counts->getU32() >= 12UL * width * height) {
+ bps = 12;
+ double_width = false;
+ } else {
+ ThrowRDE("Can not detect bitdepth. StripByteCounts = %u, width = %u, "
+ "height = %u",
+ counts->getU32(), width, height);
+ }
+
+ double_width = hints.has("double_width_unpacked");
+ const uint32 real_width = double_width ? 2U * width : width;
+
+ mRaw->dim = iPoint2D(real_width, height);
+ mRaw->createData();
+
+ UncompressedDecompressor u(input, mRaw);
+
+ if (double_width) {
+ u.decodeRawUnpacked<16, Endianness::little>(width * 2, height);
+ } else if (input.getByteOrder() == Endianness::big &&
+ getHostEndianness() == Endianness::little) {
+ // FIXME: ^ that if seems fishy
+ u.decodeRawUnpacked<16, Endianness::big>(width, height);
+ } else {
+ iPoint2D pos(0, 0);
+ if (hints.has("jpeg32_bitorder")) {
+ u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps,
+ BitOrder_MSB32);
+ } else {
+ u.readUncompressedRaw(mRaw->dim, pos, width * bps / 8, bps, BitOrder_LSB);
+ }
+ }
+
+ return mRaw;
+}
+
+void RafDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ if (!this->checkCameraSupported(meta, mRootIFD->getID(), ""))
+ ThrowRDE("Unknown camera. Will not guess.");
+
+ if (isCompressed()) {
+ mRaw->metadata.mode = "compressed";
+
+ auto id = mRootIFD->getID();
+ const Camera* cam = meta->getCamera(id.make, id.model, mRaw->metadata.mode);
+ if (!cam)
+ ThrowRDE("Couldn't find camera %s %s", id.make.c_str(), id.model.c_str());
+
+ mRaw->cfa = cam->cfa;
+ }
+}
+
+void RafDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+ mRaw->metadata.isoSpeed = iso;
+
+ // This is where we'd normally call setMetaData but since we may still need
+ // to rotate the image for SuperCCD cameras we do everything ourselves
+ auto id = mRootIFD->getID();
+ const Camera* cam = meta->getCamera(id.make, id.model, mRaw->metadata.mode);
+ if (!cam)
+ ThrowRDE("Couldn't find camera");
+
+ assert(cam != nullptr);
+
+ iPoint2D new_size(mRaw->dim);
+ iPoint2D crop_offset = iPoint2D(0,0);
+
+ if (applyCrop) {
+ new_size = cam->cropSize;
+ crop_offset = cam->cropPos;
+ bool double_width = hints.has("double_width_unpacked");
+ // If crop size is negative, use relative cropping
+ if (new_size.x <= 0)
+ new_size.x = mRaw->dim.x / (double_width ? 2 : 1) - cam->cropPos.x + new_size.x;
+ else
+ new_size.x /= (double_width ? 2 : 1);
+ if (new_size.y <= 0)
+ new_size.y = mRaw->dim.y - cam->cropPos.y + new_size.y;
+ }
+
+ bool rotate = hints.has("fuji_rotate");
+ rotate = rotate && fujiRotate;
+
+ // Rotate 45 degrees - could be multithreaded.
+ if (rotate && !this->uncorrectedRawValues) {
+ // Calculate the 45 degree rotated size;
+ uint32 rotatedsize;
+ uint32 rotationPos;
+ if (alt_layout) {
+ rotatedsize = new_size.y+new_size.x/2;
+ rotationPos = new_size.x/2 - 1;
+ }
+ else {
+ rotatedsize = new_size.x+new_size.y/2;
+ rotationPos = new_size.x - 1;
+ }
+
+ iPoint2D final_size(rotatedsize, rotatedsize-1);
+ RawImage rotated = RawImage::create(final_size, TYPE_USHORT16, 1);
+ rotated->clearArea(iRectangle2D(iPoint2D(0,0), rotated->dim));
+ rotated->metadata = mRaw->metadata;
+ rotated->metadata.fujiRotationPos = rotationPos;
+
+ int dest_pitch = static_cast<int>(rotated->pitch) / 2;
+ auto* dst = reinterpret_cast<ushort16*>(rotated->getData(0, 0));
+
+ for (int y = 0; y < new_size.y; y++) {
+ auto* src = reinterpret_cast<ushort16*>(
+ mRaw->getData(crop_offset.x, crop_offset.y + y));
+ for (int x = 0; x < new_size.x; x++) {
+ int h;
+ int w;
+ if (alt_layout) { // Swapped x and y
+ h = rotatedsize - (new_size.y + 1 - y + (x >> 1));
+ w = ((x+1) >> 1) + y;
+ } else {
+ h = new_size.x - 1 - x + (y >> 1);
+ w = ((y+1) >> 1) + x;
+ }
+ if (h < rotated->dim.y && w < rotated->dim.x)
+ dst[w + h * dest_pitch] = src[x];
+ else
+ ThrowRDE("Trying to write out of bounds");
+ }
+ }
+ mRaw = rotated;
+ } else if (applyCrop) {
+ mRaw->subFrame(iRectangle2D(crop_offset, new_size));
+ }
+
+ const CameraSensorInfo *sensor = cam->getSensorInfo(iso);
+ mRaw->blackLevel = sensor->mBlackLevel;
+
+ // at least the (bayer sensor) X100 comes with a tag like this:
+ if (mRootIFD->hasEntryRecursive(FUJI_BLACKLEVEL)) {
+ TiffEntry* sep_black = mRootIFD->getEntryRecursive(FUJI_BLACKLEVEL);
+ if (sep_black->count == 4)
+ {
+ for(int k=0;k<4;k++)
+ mRaw->blackLevelSeparate[k] = sep_black->getU32(k);
+ } else if (sep_black->count == 36) {
+ for (int& k : mRaw->blackLevelSeparate)
+ k = 0;
+
+ for (int y = 0; y < 6; y++) {
+ for (int x = 0; x < 6; x++)
+ mRaw->blackLevelSeparate[2 * (y % 2) + (x % 2)] +=
+ sep_black->getU32(6 * y + x);
+ }
+
+ for (int& k : mRaw->blackLevelSeparate)
+ k /= 9;
+ }
+ }
+
+ mRaw->whitePoint = sensor->mWhiteLevel;
+ mRaw->blackAreas = cam->blackAreas;
+ mRaw->cfa = cam->cfa;
+ mRaw->metadata.canonical_make = cam->canonical_make;
+ mRaw->metadata.canonical_model = cam->canonical_model;
+ mRaw->metadata.canonical_alias = cam->canonical_alias;
+ mRaw->metadata.canonical_id = cam->canonical_id;
+ mRaw->metadata.make = id.make;
+ mRaw->metadata.model = id.model;
+
+ if (mRootIFD->hasEntryRecursive(FUJI_WB_GRBLEVELS)) {
+ TiffEntry *wb = mRootIFD->getEntryRecursive(FUJI_WB_GRBLEVELS);
+ if (wb->count == 3) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(2);
+ }
+ } else if (mRootIFD->hasEntryRecursive(FUJIOLDWB)) {
+ TiffEntry *wb = mRootIFD->getEntryRecursive(FUJIOLDWB);
+ if (wb->count == 8) {
+ mRaw->metadata.wbCoeffs[0] = wb->getFloat(1);
+ mRaw->metadata.wbCoeffs[1] = wb->getFloat(0);
+ mRaw->metadata.wbCoeffs[2] = wb->getFloat(3);
+ }
+ }
+}
+
+int RafDecoder::isCompressed() {
+ auto raw = mRootIFD->getIFDWithTag(FUJI_STRIPOFFSETS);
+ uint32 height = 0;
+ uint32 width = 0;
+
+ if (raw->hasEntry(FUJI_RAWIMAGEFULLHEIGHT)) {
+ height = raw->getEntry(FUJI_RAWIMAGEFULLHEIGHT)->getU32();
+ width = raw->getEntry(FUJI_RAWIMAGEFULLWIDTH)->getU32();
+ } else if (raw->hasEntry(IMAGEWIDTH)) {
+ TiffEntry* e = raw->getEntry(IMAGEWIDTH);
+ height = e->getU16(0);
+ width = e->getU16(1);
+ } else
+ ThrowRDE("Unable to locate image size");
+
+ if (width == 0 || height == 0 || width > 9216 || height > 6210)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ uint32 count = raw->getEntry(FUJI_STRIPBYTECOUNTS)->getU32();
+
+ // The uncompressed raf's can be 12/14 bpp, so if it is less than that,
+ // then we are likely in compressed raf.
+ // FIXME: this can't be the correct way to detect this. But i'm not seeing
+ // anything in the diff between exiv2/exiftool dumps of {un,}compressed raws.
+ // Maybe we are supposed to check for valid FujiDecompressor::FujiHeader ?
+ return count * 8 / (width * height) < 12;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.h
new file mode 100644
index 00000000..3821f37e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/RafDecoder.h
@@ -0,0 +1,56 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2013 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFD (ptr only)
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class RafDecoder final : public AbstractTiffDecoder
+{
+ bool alt_layout = false;
+
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ RafDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+ static bool isRAF(const Buffer* input);
+
+protected:
+ int getDecoderVersion() const override { return 1; }
+
+private:
+ int isCompressed();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.cpp
new file mode 100644
index 00000000..ee7a8267
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.cpp
@@ -0,0 +1,304 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/RawDecoder.h"
+#include "common/Common.h" // for uint32, roundUpD...
+#include "common/Point.h" // for iPoint2D, iRecta...
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/FileIOException.h" // for FileIOException
+#include "io/IOException.h" // for IOException
+#include "metadata/BlackArea.h" // for BlackArea
+#include "metadata/Camera.h" // for Camera, Hints
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "metadata/CameraSensorInfo.h" // for CameraSensorInfo
+#include "metadata/ColorFilterArray.h" // for ColorFilterArray
+#include "parsers/TiffParserException.h" // for TiffParserException
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffIFD
+#include "tiff/TiffTag.h" // for BITSPERSAMPLE
+#include <array> // for array
+#include <cassert> // for assert
+#include <string> // for string, basic_st...
+#include <vector> // for vector
+
+using std::vector;
+using std::string;
+
+namespace rawspeed {
+
+RawDecoder::RawDecoder(const Buffer* file)
+ : mRaw(RawImage::create()), mFile(file) {
+ failOnUnknown = false;
+ interpolateBadPixels = true;
+ applyStage1DngOpcodes = true;
+ applyCrop = true;
+ uncorrectedRawValues = false;
+ fujiRotate = true;
+}
+
+void RawDecoder::decodeUncompressed(const TiffIFD *rawIFD, BitOrder order) {
+ TiffEntry *offsets = rawIFD->getEntry(STRIPOFFSETS);
+ TiffEntry *counts = rawIFD->getEntry(STRIPBYTECOUNTS);
+ uint32 yPerSlice = rawIFD->getEntry(ROWSPERSTRIP)->getU32();
+ uint32 width = rawIFD->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = rawIFD->getEntry(IMAGELENGTH)->getU32();
+ uint32 bitPerPixel = rawIFD->getEntry(BITSPERSAMPLE)->getU32();
+
+ if (width == 0 || height == 0 || width > 5632 || height > 3720)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ mRaw->dim = iPoint2D(width, height);
+
+ if (counts->count != offsets->count) {
+ ThrowRDE("Byte count number does not match strip size: "
+ "count:%u, stips:%u ",
+ counts->count, offsets->count);
+ }
+
+ if (yPerSlice == 0 || yPerSlice > static_cast<uint32>(mRaw->dim.y) ||
+ roundUpDivision(mRaw->dim.y, yPerSlice) != counts->count) {
+ ThrowRDE("Invalid y per slice %u or strip count %u (height = %u)",
+ yPerSlice, counts->count, mRaw->dim.y);
+ }
+
+ switch (bitPerPixel) {
+ case 12:
+ case 14:
+ break;
+ default:
+ ThrowRDE("Unexpected bits per pixel: %u.", bitPerPixel);
+ }
+
+ vector<RawSlice> slices;
+ slices.reserve(counts->count);
+ uint32 offY = 0;
+
+ for (uint32 s = 0; s < counts->count; s++) {
+ RawSlice slice;
+ slice.offset = offsets->getU32(s);
+ slice.count = counts->getU32(s);
+
+ if (slice.count < 1)
+ ThrowRDE("Slice %u is empty", s);
+
+ if (offY + yPerSlice > height)
+ slice.h = height - offY;
+ else
+ slice.h = yPerSlice;
+
+ offY += yPerSlice;
+
+ if (!mFile->isValid(slice.offset, slice.count))
+ ThrowRDE("Slice offset/count invalid");
+
+ slices.push_back(slice);
+ }
+
+ if (slices.empty())
+ ThrowRDE("No valid slices found. File probably truncated.");
+
+ assert(height <= offY);
+ assert(slices.size() == counts->count);
+
+ mRaw->createData();
+
+ // Default white level is (2 ** BitsPerSample) - 1
+ mRaw->whitePoint = (1UL << bitPerPixel) - 1UL;
+
+ offY = 0;
+ for (const RawSlice& slice : slices) {
+ UncompressedDecompressor u(*mFile, slice.offset, slice.count, mRaw);
+ iPoint2D size(width, slice.h);
+ iPoint2D pos(0, offY);
+ bitPerPixel = static_cast<int>(
+ static_cast<uint64>(static_cast<uint64>(slice.count) * 8U) /
+ (slice.h * width));
+ const auto inputPitch = width * bitPerPixel / 8;
+ if (!inputPitch)
+ ThrowRDE("Bad input pitch. Can not decode anything.");
+
+ u.readUncompressedRaw(size, pos, inputPitch, bitPerPixel, order);
+
+ offY += slice.h;
+ }
+}
+
+void RawDecoder::askForSamples(const CameraMetaData* meta, const string& make,
+ const string& model, const string& mode) const {
+ if ("dng" == mode)
+ return;
+
+ writeLog(DEBUG_PRIO_WARNING,
+ "Unable to find camera in database: '%s' '%s' "
+ "'%s'\nPlease consider providing samples on "
+ "<https://raw.pixls.us/>, thanks!",
+ make.c_str(), model.c_str(), mode.c_str());
+}
+
+bool RawDecoder::checkCameraSupported(const CameraMetaData* meta,
+ const string& make, const string& model,
+ const string& mode) {
+ mRaw->metadata.make = make;
+ mRaw->metadata.model = model;
+ const Camera* cam = meta->getCamera(make, model, mode);
+ if (!cam) {
+ askForSamples(meta, make, model, mode);
+
+ if (failOnUnknown)
+ ThrowRDE("Camera '%s' '%s', mode '%s' not supported, and not allowed to guess. Sorry.", make.c_str(),
model.c_str(), mode.c_str());
+
+ // Assume the camera can be decoded, but return false, so decoders can see that we are unsure.
+ return false;
+ }
+
+ if (!cam->supported)
+ ThrowRDE("Camera not supported (explicit). Sorry.");
+
+ if (cam->decoderVersion > getDecoderVersion())
+ ThrowRDE("Camera not supported in this version. Update RawSpeed for support.");
+
+ hints = cam->hints;
+ return true;
+}
+
+void RawDecoder::setMetaData(const CameraMetaData* meta, const string& make,
+ const string& model, const string& mode,
+ int iso_speed) {
+ mRaw->metadata.isoSpeed = iso_speed;
+ const Camera* cam = meta->getCamera(make, model, mode);
+ if (!cam) {
+ askForSamples(meta, make, model, mode);
+
+ if (failOnUnknown)
+ ThrowRDE("Camera '%s' '%s', mode '%s' not supported, and not allowed to guess. Sorry.", make.c_str(),
model.c_str(), mode.c_str());
+
+ return;
+ }
+
+ // Only override CFA with the data from cameras.xml if it actually contained
+ // the CFA.
+ if (cam->cfa.getSize().area() > 0)
+ mRaw->cfa = cam->cfa;
+
+ mRaw->metadata.canonical_make = cam->canonical_make;
+ mRaw->metadata.canonical_model = cam->canonical_model;
+ mRaw->metadata.canonical_alias = cam->canonical_alias;
+ mRaw->metadata.canonical_id = cam->canonical_id;
+ mRaw->metadata.make = make;
+ mRaw->metadata.model = model;
+ mRaw->metadata.mode = mode;
+
+ if (applyCrop) {
+ iPoint2D new_size = cam->cropSize;
+
+ // If crop size is negative, use relative cropping
+ if (new_size.x <= 0)
+ new_size.x = mRaw->dim.x - cam->cropPos.x + new_size.x;
+
+ if (new_size.y <= 0)
+ new_size.y = mRaw->dim.y - cam->cropPos.y + new_size.y;
+
+ mRaw->subFrame(iRectangle2D(cam->cropPos, new_size));
+ }
+
+ const CameraSensorInfo *sensor = cam->getSensorInfo(iso_speed);
+ mRaw->blackLevel = sensor->mBlackLevel;
+ mRaw->whitePoint = sensor->mWhiteLevel;
+ mRaw->blackAreas = cam->blackAreas;
+ if (mRaw->blackAreas.empty() && !sensor->mBlackLevelSeparate.empty()) {
+ auto cfaArea = mRaw->cfa.getSize().area();
+ if (mRaw->isCFA && cfaArea <= sensor->mBlackLevelSeparate.size()) {
+ for (auto i = 0UL; i < cfaArea; i++) {
+ mRaw->blackLevelSeparate[i] = sensor->mBlackLevelSeparate[i];
+ }
+ } else if (!mRaw->isCFA && mRaw->getCpp() <= sensor->mBlackLevelSeparate.size()) {
+ for (uint32 i = 0; i < mRaw->getCpp(); i++) {
+ mRaw->blackLevelSeparate[i] = sensor->mBlackLevelSeparate[i];
+ }
+ }
+ }
+
+ // Allow overriding individual blacklevels. Values are in CFA order
+ // (the same order as the in the CFA tag)
+ // A hint could be:
+ // <Hint name="override_cfa_black" value="10,20,30,20"/>
+ string cfa_black = hints.get("override_cfa_black", string());
+ if (!cfa_black.empty()) {
+ vector<string> v = splitString(cfa_black, ',');
+ if (v.size() != 4) {
+ mRaw->setError("Expected 4 values '10,20,30,20' as values for override_cfa_black hint.");
+ } else {
+ for (int i = 0; i < 4; i++) {
+ mRaw->blackLevelSeparate[i] = stoi(v[i]);
+ }
+ }
+ }
+}
+
+rawspeed::RawImage RawDecoder::decodeRaw() {
+ try {
+ RawImage raw = decodeRawInternal();
+ raw->checkMemIsInitialized();
+
+ raw->metadata.pixelAspectRatio =
+ hints.get("pixel_aspect_ratio", raw->metadata.pixelAspectRatio);
+ if (interpolateBadPixels) {
+ raw->fixBadPixels();
+ raw->checkMemIsInitialized();
+ }
+
+ return raw;
+ } catch (TiffParserException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (FileIOException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (IOException &e) {
+ ThrowRDE("%s", e.what());
+ }
+}
+
+void RawDecoder::decodeMetaData(const CameraMetaData* meta) {
+ try {
+ decodeMetaDataInternal(meta);
+ } catch (TiffParserException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (FileIOException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (IOException &e) {
+ ThrowRDE("%s", e.what());
+ }
+}
+
+void RawDecoder::checkSupport(const CameraMetaData* meta) {
+ try {
+ checkSupportInternal(meta);
+ } catch (TiffParserException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (FileIOException &e) {
+ ThrowRDE("%s", e.what());
+ } catch (IOException &e) {
+ ThrowRDE("%s", e.what());
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.h
new file mode 100644
index 00000000..d0ec9805
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoder.h
@@ -0,0 +1,166 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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, BitOrder
+#include "common/RawImage.h" // for RawImage
+#include "metadata/Camera.h" // for Hints
+#include <string> // for string
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class TiffIFD;
+
+class RawDecoder
+{
+public:
+ /* Construct decoder instance - Buffer is a filemap of the file to be decoded
+ */
+ /* The Buffer is not owned by this class, will not be deleted, and must remain
+ */
+ /* valid while this object exists */
+ explicit RawDecoder(const Buffer* file);
+ virtual ~RawDecoder() = default;
+
+ /* Check if the decoder can decode the image from this camera */
+ /* A RawDecoderException will be thrown if the camera isn't supported */
+ /* Unknown cameras does NOT generate any specific feedback */
+ /* This function must be overridden by actual decoders */
+ void checkSupport(const CameraMetaData* meta);
+
+ /* Attempt to decode the image */
+ /* A RawDecoderException will be thrown if the image cannot be decoded, */
+ /* and there will not be any data in the mRaw image. */
+ RawImage decodeRaw();
+
+ /* This will apply metadata information from the camera database, */
+ /* such as crop, black+white level, etc. */
+ /* This function is expected to use the protected "setMetaData" */
+ /* after retrieving make, model and mode if applicate. */
+ /* If meta-data is set during load, this function can be empty. */
+ /* The image is expected to be cropped after this, but black/whitelevel */
+ /* compensation is not expected to be applied to the image */
+ void decodeMetaData(const CameraMetaData* meta);
+
+ /* Allows access to the root IFD structure */
+ /* If image isn't TIFF based NULL will be returned */
+ virtual TiffIFD *getRootIFD() { return nullptr; }
+
+ /* The decoded image - undefined if image has not or could not be decoded. */
+ /* Remember this is automatically refcounted, so a reference is retained until this class is destroyed */
+ RawImage mRaw;
+
+ /* You can set this if you do not want Rawspeed to attempt to decode images, */
+ /* where it does not have reliable information about CFA, cropping, black and white point */
+ /* It is pretty safe to leave this disabled (default behaviour), but if you do not want to */
+ /* support unknown cameras, you can enable this */
+ /* DNGs are always attempted to be decoded, so this variable has no effect on DNGs */
+ bool failOnUnknown;
+
+ /* Set how to handle bad pixels. */
+ /* If you disable this parameter, no bad pixel interpolation will be done */
+ bool interpolateBadPixels;
+
+ /* Apply stage 1 DNG opcodes. */
+ /* This usually maps out bad pixels, etc */
+ bool applyStage1DngOpcodes;
+
+ /* Apply crop - if false uncropped image is delivered */
+ bool applyCrop;
+
+ /* This will skip all corrections, and deliver the raw data */
+ /* This will skip any compression curves or other things that */
+ /* is needed to get the correct values */
+ /* Only enable if you are sure that is what you want */
+ bool uncorrectedRawValues;
+
+ /* Should Fuji images be rotated? */
+ bool fujiRotate;
+
+ struct {
+ /* Should Quadrant Multipliers be applied to the IIQ raws? */
+ bool quadrantMultipliers = true;
+
+ // Is *any* of the corrections enabled?
+ explicit operator bool() const { return quadrantMultipliers /*|| ...*/; }
+ } iiq;
+
+ /* Retrieve the main RAW chunk */
+ /* Returns NULL if unknown */
+ virtual Buffer* getCompressedData() { return nullptr; }
+
+protected:
+ /* Attempt to decode the image */
+ /* A RawDecoderException will be thrown if the image cannot be decoded, */
+ /* and there will not be any data in the mRaw image. */
+ /* This function must be overridden by actual decoders. */
+ virtual RawImage decodeRawInternal() = 0;
+ virtual void decodeMetaDataInternal(const CameraMetaData* meta) = 0;
+ virtual void checkSupportInternal(const CameraMetaData* meta) = 0;
+
+ /* Ask for sample submisson, if makes sense */
+ void askForSamples(const CameraMetaData* meta, const std::string& make,
+ const std::string& model, const std::string& mode) const;
+
+ /* Check the camera and mode against the camera database. */
+ /* A RawDecoderException will be thrown if the camera isn't supported */
+ /* Unknown cameras does NOT generate any errors, but returns false */
+ bool checkCameraSupported(const CameraMetaData* meta, const std::string& make,
+ const std::string& model, const std::string& mode);
+
+ /* Helper function for decodeMetaData(), that find the camera in the CameraMetaData DB */
+ /* and sets common settings such as crop, black- white level, and sets CFA information */
+ virtual void setMetaData(const CameraMetaData* meta, const std::string& make,
+ const std::string& model, const std::string& mode,
+ int iso_speed = 0);
+
+ /* Generic decompressor for uncompressed images */
+ /* order: Order of the bits - see Common.h for possibilities. */
+ void decodeUncompressed(const TiffIFD* rawIFD, BitOrder order);
+
+ /* The Raw input file to be decoded */
+ const Buffer* mFile;
+
+ /* Decoder version */
+ /* This can be used to avoid newer version of an xml file to indicate that a file */
+ /* can be decoded, when a specific version of the code is needed */
+ /* Higher number in camera xml file: Files for this camera will not be decoded */
+ /* Higher number in code than xml: Image will be decoded. */
+ virtual int getDecoderVersion() const = 0;
+
+ /* Hints set for the camera after checkCameraSupported has been called from the implementation*/
+ Hints hints;
+
+ struct RawSlice;
+};
+
+struct RawDecoder::RawSlice {
+ uint32 h = 0;
+ uint32 offset = 0;
+ uint32 count = 0;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/RawDecoderException.h
b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoderException.h
new file mode 100644
index 00000000..c29d1d09
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/RawDecoderException.h
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 ThrowException, RawspeedException
+#include <string> // for string
+
+namespace rawspeed {
+
+class RawDecoderException : public RawspeedException {
+public:
+ explicit RawDecoderException(const std::string& msg)
+ : RawspeedException(msg) {}
+ explicit RawDecoderException(const char* msg) : RawspeedException(msg) {}
+};
+
+#define ThrowRDE(...) \
+ ThrowExceptionHelper(rawspeed::RawDecoderException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.cpp
new file mode 100644
index 00000000..f6bace06
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.cpp
@@ -0,0 +1,261 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/Rw2Decoder.h"
+#include "common/Common.h" // for writeLog, uint32
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/PanasonicDecompressor.h" // for PanasonicDecompr...
+#include "decompressors/PanasonicDecompressorV5.h" // for PanasonicDecompr...
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endi...
+#include "metadata/Camera.h" // for Hints
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, Color...
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD, Tif...
+#include "tiff/TiffTag.h" // for TiffTag, PANASON...
+#include <array> // for array
+#include <cmath> // for fabs
+#include <memory> // for unique_ptr
+#include <string> // for string, operator==
+
+using std::string;
+using std::fabs;
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool Rw2Decoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "Panasonic" || make == "LEICA";
+}
+
+RawImage Rw2Decoder::decodeRawInternal() {
+
+ const TiffIFD* raw = nullptr;
+ bool isOldPanasonic = ! mRootIFD->hasEntryRecursive(PANASONIC_STRIPOFFSET);
+
+ if (! isOldPanasonic)
+ raw = mRootIFD->getIFDWithTag(PANASONIC_STRIPOFFSET);
+ else
+ raw = mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ uint32 height = raw->getEntry(static_cast<TiffTag>(3))->getU16();
+ uint32 width = raw->getEntry(static_cast<TiffTag>(2))->getU16();
+
+ if (isOldPanasonic) {
+ if (width == 0 || height == 0 || width > 4330 || height > 2751)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ TiffEntry *offsets = raw->getEntry(STRIPOFFSETS);
+
+ if (offsets->count != 1) {
+ ThrowRDE("Multiple Strips found: %u", offsets->count);
+ }
+ uint32 offset = offsets->getU32();
+ if (!mFile->isValid(offset))
+ ThrowRDE("Invalid image data offset, cannot decode.");
+
+ mRaw->dim = iPoint2D(width, height);
+
+ uint32 size = mFile->getSize() - offset;
+
+ UncompressedDecompressor u(ByteStream(mFile, offset), mRaw);
+
+ if (size >= width*height*2) {
+ // It's completely unpacked little-endian
+ mRaw->createData();
+ u.decodeRawUnpacked<12, Endianness::little>(width, height);
+ } else if (size >= width*height*3/2) {
+ // It's a packed format
+ mRaw->createData();
+ u.decode12BitRaw<Endianness::little, false, true>(width, height);
+ } else {
+ uint32 section_split_offset = 0;
+ PanasonicDecompressor p(mRaw, ByteStream(mFile, offset),
+ hints.has("zero_is_not_bad"),
+ section_split_offset);
+ mRaw->createData();
+ p.decompress();
+ }
+ } else {
+ mRaw->dim = iPoint2D(width, height);
+
+ TiffEntry *offsets = raw->getEntry(PANASONIC_STRIPOFFSET);
+
+ if (offsets->count != 1) {
+ ThrowRDE("Multiple Strips found: %u", offsets->count);
+ }
+
+ uint32 offset = offsets->getU32();
+
+ ByteStream bs(mFile, offset);
+
+ bool v5Processing = raw->hasEntry(PANASONIC_RAWFORMAT) &&
+ raw->getEntry(PANASONIC_RAWFORMAT)->getU16() == 5;
+
+ rawspeed::ushort16 bitsPerSample = 12;
+ if (raw->hasEntry(PANASONIC_BITSPERSAMPLE)) {
+ bitsPerSample = raw->getEntry(PANASONIC_BITSPERSAMPLE)->getU16();
+ }
+
+ if (v5Processing) {
+ PanasonicDecompressorV5 v5(mRaw, bs, bitsPerSample);
+ mRaw->createData();
+ v5.decompress();
+ } else {
+ uint32 section_split_offset = 0x1FF8;
+ PanasonicDecompressor p(mRaw, bs, hints.has("zero_is_not_bad"),
+ section_split_offset);
+ mRaw->createData();
+ p.decompress();
+ }
+ }
+
+ return mRaw;
+}
+
+void Rw2Decoder::checkSupportInternal(const CameraMetaData* meta) {
+ auto id = mRootIFD->getID();
+ if (!checkCameraSupported(meta, id, guessMode()))
+ checkCameraSupported(meta, id, "");
+}
+
+void Rw2Decoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_BLUE, CFA_GREEN, CFA_GREEN, CFA_RED);
+
+ auto id = mRootIFD->getID();
+ string mode = guessMode();
+ int iso = 0;
+ if (mRootIFD->hasEntryRecursive(PANASONIC_ISO_SPEED))
+ iso = mRootIFD->getEntryRecursive(PANASONIC_ISO_SPEED)->getU32();
+
+ if (this->checkCameraSupported(meta, id, mode)) {
+ setMetaData(meta, id, mode, iso);
+ } else {
+ mRaw->metadata.mode = mode;
+ writeLog(DEBUG_PRIO_EXTRA, "Mode not found in DB: %s", mode.c_str());
+ setMetaData(meta, id, "", iso);
+ }
+
+ const TiffIFD* raw = mRootIFD->hasEntryRecursive(PANASONIC_STRIPOFFSET)
+ ? mRootIFD->getIFDWithTag(PANASONIC_STRIPOFFSET)
+ : mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ // Read blacklevels
+ if (raw->hasEntry(static_cast<TiffTag>(0x1c)) &&
+ raw->hasEntry(static_cast<TiffTag>(0x1d)) &&
+ raw->hasEntry(static_cast<TiffTag>(0x1e))) {
+ const auto getBlack = [&raw](TiffTag t) -> int {
+ const auto val = raw->getEntry(t)->getU32();
+ int out;
+ if (__builtin_sadd_overflow(val, 15, &out))
+ ThrowRDE("Integer overflow when calculating black level");
+ return out;
+ };
+
+ const int blackRed = getBlack(static_cast<TiffTag>(0x1c));
+ const int blackGreen = getBlack(static_cast<TiffTag>(0x1d));
+ const int blackBlue = getBlack(static_cast<TiffTag>(0x1e));
+
+ for(int i = 0; i < 2; i++) {
+ for(int j = 0; j < 2; j++) {
+ const int k = i + 2 * j;
+ const CFAColor c = mRaw->cfa.getColorAt(i, j);
+ switch (c) {
+ case CFA_RED:
+ mRaw->blackLevelSeparate[k] = blackRed;
+ break;
+ case CFA_GREEN:
+ mRaw->blackLevelSeparate[k] = blackGreen;
+ break;
+ case CFA_BLUE:
+ mRaw->blackLevelSeparate[k] = blackBlue;
+ break;
+ default:
+ ThrowRDE("Unexpected CFA color %s.",
+ ColorFilterArray::colorToString(c).c_str());
+ }
+ }
+ }
+ }
+
+ // Read WB levels
+ if (raw->hasEntry(static_cast<TiffTag>(0x0024)) &&
+ raw->hasEntry(static_cast<TiffTag>(0x0025)) &&
+ raw->hasEntry(static_cast<TiffTag>(0x0026))) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(
+ raw->getEntry(static_cast<TiffTag>(0x0024))->getU16());
+ mRaw->metadata.wbCoeffs[1] = static_cast<float>(
+ raw->getEntry(static_cast<TiffTag>(0x0025))->getU16());
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(
+ raw->getEntry(static_cast<TiffTag>(0x0026))->getU16());
+ } else if (raw->hasEntry(static_cast<TiffTag>(0x0011)) &&
+ raw->hasEntry(static_cast<TiffTag>(0x0012))) {
+ mRaw->metadata.wbCoeffs[0] = static_cast<float>(
+ raw->getEntry(static_cast<TiffTag>(0x0011))->getU16());
+ mRaw->metadata.wbCoeffs[1] = 256.0F;
+ mRaw->metadata.wbCoeffs[2] = static_cast<float>(
+ raw->getEntry(static_cast<TiffTag>(0x0012))->getU16());
+ }
+}
+
+std::string Rw2Decoder::guessMode() {
+ float ratio = 3.0F / 2.0F; // Default
+
+ if (!mRaw->isAllocated())
+ return "";
+
+ ratio = static_cast<float>(mRaw->dim.x) / static_cast<float>(mRaw->dim.y);
+
+ float min_diff = fabs(ratio - 16.0F / 9.0F);
+ std::string closest_match = "16:9";
+
+ float t = fabs(ratio - 3.0F / 2.0F);
+ if (t < min_diff) {
+ closest_match = "3:2";
+ min_diff = t;
+ }
+
+ t = fabs(ratio - 4.0F / 3.0F);
+ if (t < min_diff) {
+ closest_match = "4:3";
+ min_diff = t;
+ }
+
+ t = fabs(ratio - 1.0F);
+ if (t < min_diff) {
+ closest_match = "1:1";
+ min_diff = t;
+ }
+ writeLog(DEBUG_PRIO_EXTRA, "Mode guess: '%s'", closest_match.c_str());
+ return closest_match;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.h
new file mode 100644
index 00000000..cceb1a94
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/Rw2Decoder.h
@@ -0,0 +1,54 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFD (ptr only)
+#include <string> // for string
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class Rw2Decoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ Rw2Decoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 3; }
+
+private:
+ std::string guessMode();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.cpp
new file mode 100644
index 00000000..a031f212
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.cpp
@@ -0,0 +1,56 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decoders/SimpleTiffDecoder.h"
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for RawDecoderException (ptr o...
+#include "io/Buffer.h" // for Buffer
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffIFD
+#include "tiff/TiffTag.h" // for TiffTag::IMAGELENGTH, Tiff...
+
+namespace rawspeed {
+
+void SimpleTiffDecoder::prepareForRawDecoding() {
+ raw = getIFDWithLargestImage();
+ width = raw->getEntry(IMAGEWIDTH)->getU32();
+ height = raw->getEntry(IMAGELENGTH)->getU32();
+ off = raw->getEntry(STRIPOFFSETS)->getU32();
+ c2 = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+
+ if (!mFile->isValid(off, c2))
+ ThrowRDE("Image is truncated.");
+
+ if (c2 == 0)
+ ThrowRDE("No image data found.");
+
+ if (0 == width || 0 == height)
+ ThrowRDE("Image has zero size.");
+
+ checkImageDimensions();
+
+ mRaw->dim = iPoint2D(width, height);
+ mRaw->createData();
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.h
new file mode 100644
index 00000000..c4c85142
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/SimpleTiffDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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/Common.h" // for uint32
+#include "decoders/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffIFD (ptr only), TiffRo...
+#include <utility> // for move
+
+namespace rawspeed {
+
+class Buffer;
+
+class SimpleTiffDecoder : public AbstractTiffDecoder {
+ virtual void checkImageDimensions() {}
+
+public:
+ SimpleTiffDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ void prepareForRawDecoding();
+
+protected:
+ const TiffIFD* raw;
+ uint32 width;
+ uint32 height;
+ uint32 off;
+ uint32 c2;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.cpp
new file mode 100644
index 00000000..ca73ed6f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.cpp
@@ -0,0 +1,180 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2010 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+
+ 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/SrwDecoder.h"
+#include "common/Common.h" // for uint32, BitOrder_LSB
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/SamsungV0Decompressor.h" // for SamsungV0Decompressor
+#include "decompressors/SamsungV1Decompressor.h" // for SamsungV1Decompressor
+#include "decompressors/SamsungV2Decompressor.h" // for SamsungV2Decompressor
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endiann...
+#include "metadata/Camera.h" // for Hints
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_LONG
+#include "tiff/TiffIFD.h" // for TiffRootIFD, TiffIFD
+#include "tiff/TiffTag.h" // for STRIPOFFSETS, BITSP...
+#include <memory> // for unique_ptr
+#include <sstream> // for operator<<, ostring...
+#include <string> // for string, operator==
+#include <vector> // for vector
+
+namespace rawspeed {
+
+bool SrwDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "SAMSUNG";
+}
+
+RawImage SrwDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(STRIPOFFSETS);
+
+ int compression = raw->getEntry(COMPRESSION)->getU32();
+ const int bits = raw->getEntry(BITSPERSAMPLE)->getU32();
+
+ if (12 != bits && 14 != bits)
+ ThrowRDE("Unsupported bits per sample");
+
+ if (32769 != compression && 32770 != compression && 32772 != compression && 32773 != compression)
+ ThrowRDE("Unsupported compression");
+
+ uint32 nslices = raw->getEntry(STRIPOFFSETS)->count;
+ if (nslices != 1)
+ ThrowRDE("Only one slice supported, found %u", nslices);
+
+ const auto wrongComp =
+ 32770 == compression && !raw->hasEntry(static_cast<TiffTag>(40976));
+ if (32769 == compression || wrongComp) {
+ bool bit_order = hints.get("msb_override", wrongComp ? bits == 12 : false);
+ this->decodeUncompressed(raw, bit_order ? BitOrder_MSB : BitOrder_LSB);
+ return mRaw;
+ }
+
+ const uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ const uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ mRaw->dim = iPoint2D(width, height);
+
+ if (32770 == compression)
+ {
+ const TiffEntry* sliceOffsets = raw->getEntry(static_cast<TiffTag>(40976));
+ if (sliceOffsets->type != TIFF_LONG || sliceOffsets->count != 1)
+ ThrowRDE("Entry 40976 is corrupt");
+
+ ByteStream bso(DataBuffer(*mFile, Endianness::little));
+ bso.skipBytes(sliceOffsets->getU32());
+ bso = bso.getStream(height, 4);
+
+ const uint32 offset = raw->getEntry(STRIPOFFSETS)->getU32();
+ const uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+ Buffer rbuf(mFile->getSubView(offset, count));
+ ByteStream bsr(DataBuffer(rbuf, Endianness::little));
+
+ SamsungV0Decompressor s0(mRaw, bso, bsr);
+
+ mRaw->createData();
+
+ s0.decompress();
+
+ return mRaw;
+ }
+ if (32772 == compression)
+ {
+ uint32 offset = raw->getEntry(STRIPOFFSETS)->getU32();
+ uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+ const ByteStream bs(mFile, offset, count);
+
+ SamsungV1Decompressor s1(mRaw, &bs, bits);
+
+ mRaw->createData();
+
+ s1.decompress();
+
+ return mRaw;
+ }
+ if (32773 == compression)
+ {
+ uint32 offset = raw->getEntry(STRIPOFFSETS)->getU32();
+ uint32 count = raw->getEntry(STRIPBYTECOUNTS)->getU32();
+ const ByteStream bs(mFile, offset, count);
+
+ SamsungV2Decompressor s2(mRaw, bs, bits);
+
+ mRaw->createData();
+
+ s2.decompress();
+
+ return mRaw;
+ }
+ ThrowRDE("Unsupported compression");
+}
+
+std::string SrwDecoder::getMode() {
+ std::vector<const TiffIFD*> data = mRootIFD->getIFDsWithTag(CFAPATTERN);
+ std::ostringstream mode;
+ if (!data.empty() && data[0]->hasEntryRecursive(BITSPERSAMPLE)) {
+ mode << data[0]->getEntryRecursive(BITSPERSAMPLE)->getU32() << "bit";
+ return mode.str();
+ }
+ return "";
+}
+
+void SrwDecoder::checkSupportInternal(const CameraMetaData* meta) {
+ auto id = mRootIFD->getID();
+ std::string mode = getMode();
+ if (meta->hasCamera(id.make, id.model, mode))
+ this->checkCameraSupported(meta, id, getMode());
+ else
+ this->checkCameraSupported(meta, id, "");
+}
+
+void SrwDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ int iso = 0;
+ if (mRootIFD->hasEntryRecursive(ISOSPEEDRATINGS))
+ iso = mRootIFD->getEntryRecursive(ISOSPEEDRATINGS)->getU32();
+
+ auto id = mRootIFD->getID();
+ std::string mode = getMode();
+ if (meta->hasCamera(id.make, id.model, mode))
+ setMetaData(meta, id, mode, iso);
+ else
+ setMetaData(meta, id, "", iso);
+
+ // Set the whitebalance
+ if (mRootIFD->hasEntryRecursive(SAMSUNG_WB_RGGBLEVELSUNCORRECTED) &&
+ mRootIFD->hasEntryRecursive(SAMSUNG_WB_RGGBLEVELSBLACK)) {
+ TiffEntry *wb_levels = mRootIFD->getEntryRecursive(SAMSUNG_WB_RGGBLEVELSUNCORRECTED);
+ TiffEntry *wb_black = mRootIFD->getEntryRecursive(SAMSUNG_WB_RGGBLEVELSBLACK);
+ if (wb_levels->count == 4 && wb_black->count == 4) {
+ mRaw->metadata.wbCoeffs[0] = wb_levels->getFloat(0) - wb_black->getFloat(0);
+ mRaw->metadata.wbCoeffs[1] = wb_levels->getFloat(1) - wb_black->getFloat(1);
+ mRaw->metadata.wbCoeffs[2] = wb_levels->getFloat(3) - wb_black->getFloat(3);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.h
new file mode 100644
index 00000000..9510608c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/SrwDecoder.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2010 Klaus Post
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffIFD (ptr only), TiffRo...
+#include <string> // for string
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class SrwDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ SrwDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+ void checkSupportInternal(const CameraMetaData* meta) override;
+
+private:
+ int getDecoderVersion() const override { return 3; }
+ std::string getMode();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.cpp
b/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.cpp
new file mode 100644
index 00000000..6ff7d82a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.cpp
@@ -0,0 +1,91 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/ThreefrDecoder.h"
+#include "common/Common.h" // for uint32
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HasselbladDecompressor.h" // for HasselbladDecompre...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "metadata/Camera.h" // for Hints
+#include "metadata/ColorFilterArray.h" // for CFA_GREEN, CFA_BLUE
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffIFD.h" // for TiffRootIFD, TiffIFD
+#include "tiff/TiffTag.h" // for ASSHOTNEUTRAL, STR...
+#include <memory> // for unique_ptr
+#include <string> // for operator==, string
+
+namespace rawspeed {
+
+class CameraMetaData;
+
+bool ThreefrDecoder::isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file) {
+ const auto id = rootIFD->getID();
+ const std::string& make = id.make;
+
+ // FIXME: magic
+
+ return make == "Hasselblad";
+}
+
+RawImage ThreefrDecoder::decodeRawInternal() {
+ auto raw = mRootIFD->getIFDWithTag(STRIPOFFSETS, 1);
+ uint32 width = raw->getEntry(IMAGEWIDTH)->getU32();
+ uint32 height = raw->getEntry(IMAGELENGTH)->getU32();
+ uint32 off = raw->getEntry(STRIPOFFSETS)->getU32();
+ // STRIPBYTECOUNTS is strange/invalid for the existing 3FR samples...
+
+ const ByteStream bs(mFile->getSubView(off), 0);
+
+ mRaw->dim = iPoint2D(width, height);
+
+ HasselbladDecompressor l(bs, mRaw);
+ mRaw->createData();
+
+ int pixelBaseOffset = hints.get("pixelBaseOffset", 0);
+ l.decode(pixelBaseOffset);
+
+ return mRaw;
+}
+
+void ThreefrDecoder::decodeMetaDataInternal(const CameraMetaData* meta) {
+ mRaw->cfa.setCFA(iPoint2D(2,2), CFA_RED, CFA_GREEN, CFA_GREEN, CFA_BLUE);
+
+ setMetaData(meta, "", 0);
+
+ // Fetch the white balance
+ if (mRootIFD->hasEntryRecursive(ASSHOTNEUTRAL)) {
+ TiffEntry *wb = mRootIFD->getEntryRecursive(ASSHOTNEUTRAL);
+ if (wb->count == 3) {
+ for (uint32 i = 0; i < 3; i++) {
+ const float div = wb->getFloat(i);
+ if (div == 0.0F)
+ ThrowRDE("Can not decode WB, multiplier is zero/");
+
+ mRaw->metadata.wbCoeffs[i] = 1.0F / div;
+ }
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.h
b/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.h
new file mode 100644
index 00000000..beff7d0d
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/ThreefrDecoder.h
@@ -0,0 +1,49 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/AbstractTiffDecoder.h" // for AbstractTiffDecoder
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <utility> // for move
+
+namespace rawspeed {
+
+class CameraMetaData;
+class Buffer;
+
+class ThreefrDecoder final : public AbstractTiffDecoder
+{
+public:
+ static bool isAppropriateDecoder(const TiffRootIFD* rootIFD,
+ const Buffer* file);
+ ThreefrDecoder(TiffRootIFDOwner&& root, const Buffer* file)
+ : AbstractTiffDecoder(move(root), file) {}
+
+ RawImage decodeRawInternal() override;
+ void decodeMetaDataInternal(const CameraMetaData* meta) override;
+
+protected:
+ int getDecoderVersion() const override { return 0; }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decoders/meson.build
b/subprojects/rawspeed/src/librawspeed/decoders/meson.build
new file mode 100644
index 00000000..d11f2009
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decoders/meson.build
@@ -0,0 +1,55 @@
+sources = files(
+ 'AbstractTiffDecoder.cpp',
+ 'AbstractTiffDecoder.h',
+ 'ArwDecoder.cpp',
+ 'ArwDecoder.h',
+ 'Cr2Decoder.cpp',
+ 'Cr2Decoder.h',
+ 'CrwDecoder.cpp',
+ 'CrwDecoder.h',
+ 'DcrDecoder.cpp',
+ 'DcrDecoder.h',
+ 'DcsDecoder.cpp',
+ 'DcsDecoder.h',
+ 'DngDecoder.cpp',
+ 'DngDecoder.h',
+ 'ErfDecoder.cpp',
+ 'ErfDecoder.h',
+ 'IiqDecoder.cpp',
+ 'IiqDecoder.h',
+ 'KdcDecoder.cpp',
+ 'KdcDecoder.h',
+ 'MefDecoder.cpp',
+ 'MefDecoder.h',
+ 'MosDecoder.cpp',
+ 'MosDecoder.h',
+ 'MrwDecoder.cpp',
+ 'MrwDecoder.h',
+ 'NakedDecoder.cpp',
+ 'NakedDecoder.h',
+ 'NefDecoder.cpp',
+ 'NefDecoder.h',
+ 'OrfDecoder.cpp',
+ 'OrfDecoder.h',
+ 'PefDecoder.cpp',
+ 'PefDecoder.h',
+ 'RafDecoder.cpp',
+ 'RafDecoder.h',
+ 'RawDecoder.cpp',
+ 'RawDecoder.h',
+ 'RawDecoderException.h',
+ 'Rw2Decoder.cpp',
+ 'Rw2Decoder.h',
+ 'SimpleTiffDecoder.cpp',
+ 'SimpleTiffDecoder.h',
+ 'SrwDecoder.cpp',
+ 'SrwDecoder.h',
+ 'ThreefrDecoder.cpp',
+ 'ThreefrDecoder.h',
+)
+
+librawspeed_decoders = static_library(
+ 'rawspeed-decoders',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDecompressor.h
new file mode 100644
index 00000000..3538ca50
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDecompressor.h
@@ -0,0 +1,27 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+
+namespace rawspeed {
+
+class AbstractDecompressor {};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.cpp
new file mode 100644
index 00000000..5d5b5d9b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.cpp
@@ -0,0 +1,212 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017-2018 Roman Lebeedv
+
+ 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 "rawspeedconfig.h"
+#include "decompressors/AbstractDngDecompressor.h"
+#include "common/Common.h" // for BitOrder_LSB
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImageData
+#include "decoders/RawDecoderException.h" // for RawDecoderException
+#include "decompressors/DeflateDecompressor.h" // for DeflateDecompressor
+#include "decompressors/JpegDecompressor.h" // for JpegDecompressor
+#include "decompressors/LJpegDecompressor.h" // for LJpegDecompressor
+#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
+#include "decompressors/VC5Decompressor.h" // for VC5Decompressor
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endi...
+#include "io/IOException.h" // for IOException, Thr...
+#include <cassert> // for assert
+#include <cstdio> // for size_t
+#include <limits> // for numeric_limits
+#include <memory> // for unique_ptr
+#include <vector> // for vector
+
+namespace rawspeed {
+
+template <> void AbstractDngDecompressor::decompressThread<1>() const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto e = slices.cbegin(); e < slices.cend(); ++e) {
+ UncompressedDecompressor decompressor(e->bs, mRaw);
+
+ iPoint2D tileSize(e->width, e->height);
+ iPoint2D pos(e->offX, e->offY);
+
+ bool big_endian = e->bs.getByteOrder() == Endianness::big;
+
+ // DNG spec says that if not 8 or 16 bit/sample, always use big endian
+ if (mBps != 8 && mBps != 16)
+ big_endian = true;
+
+ try {
+ const uint32 inputPixelBits = mRaw->getCpp() * mBps;
+
+ if (e->dsc.tileW > std::numeric_limits<int>::max() / inputPixelBits)
+ ThrowIOE("Integer overflow when calculating input pitch");
+
+ const int inputPitchBits = inputPixelBits * e->dsc.tileW;
+ assert(inputPitchBits > 0);
+
+ if (inputPitchBits % 8 != 0) {
+ ThrowRDE("Bad combination of cpp (%u), bps (%u) and width (%u), the "
+ "pitch is %u bits, which is not a multiple of 8 (1 byte)",
+ mRaw->getCpp(), mBps, e->width, inputPitchBits);
+ }
+
+ const int inputPitch = inputPitchBits / 8;
+ if (inputPitch == 0)
+ ThrowRDE("Data input pitch is too short. Can not decode!");
+
+ decompressor.readUncompressedRaw(tileSize, pos, inputPitch, mBps,
+ big_endian ? BitOrder_MSB
+ : BitOrder_LSB);
+ } catch (RawDecoderException& err) {
+ mRaw->setError(err.what());
+ } catch (IOException& err) {
+ mRaw->setError(err.what());
+ }
+ }
+}
+
+template <> void AbstractDngDecompressor::decompressThread<7>() const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto e = slices.cbegin(); e < slices.cend(); ++e) {
+ try {
+ LJpegDecompressor d(e->bs, mRaw);
+ d.decode(e->offX, e->offY, e->width, e->height, mFixLjpeg);
+ } catch (RawDecoderException& err) {
+ mRaw->setError(err.what());
+ } catch (IOException& err) {
+ mRaw->setError(err.what());
+ }
+ }
+}
+
+#ifdef HAVE_ZLIB
+template <> void AbstractDngDecompressor::decompressThread<8>() const noexcept {
+ std::unique_ptr<unsigned char[]> uBuffer; // NOLINT
+
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto e = slices.cbegin(); e < slices.cend(); ++e) {
+ DeflateDecompressor z(e->bs, mRaw, mPredictor, mBps);
+ try {
+ z.decode(&uBuffer, iPoint2D(e->dsc.tileW, e->dsc.tileH),
+ iPoint2D(e->width, e->height), iPoint2D(e->offX, e->offY));
+ } catch (RawDecoderException& err) {
+ mRaw->setError(err.what());
+ } catch (IOException& err) {
+ mRaw->setError(err.what());
+ }
+ }
+}
+#endif
+
+template <> void AbstractDngDecompressor::decompressThread<9>() const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto e = slices.cbegin(); e < slices.cend(); ++e) {
+ try {
+ VC5Decompressor d(e->bs, mRaw);
+ d.decode(e->offX, e->offY, e->width, e->height);
+ } catch (RawDecoderException& err) {
+ mRaw->setError(err.what());
+ } catch (IOException& err) {
+ mRaw->setError(err.what());
+ }
+ }
+}
+
+#ifdef HAVE_JPEG
+template <>
+void AbstractDngDecompressor::decompressThread<0x884c>() const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto e = slices.cbegin(); e < slices.cend(); ++e) {
+ JpegDecompressor j(e->bs, mRaw);
+ try {
+ j.decode(e->offX, e->offY);
+ } catch (RawDecoderException& err) {
+ mRaw->setError(err.what());
+ } catch (IOException& err) {
+ mRaw->setError(err.what());
+ }
+ }
+}
+#endif
+
+void AbstractDngDecompressor::decompressThread() const noexcept {
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.y > 0);
+ assert(mRaw->getCpp() > 0 && mRaw->getCpp() <= 4);
+ assert(mBps > 0 && mBps <= 32);
+
+ if (compression == 1) {
+ /* Uncompressed */
+ decompressThread<1>();
+ } else if (compression == 7) {
+ /* Lossless JPEG */
+ decompressThread<7>();
+ } else if (compression == 8) {
+ /* Deflate compression */
+#ifdef HAVE_ZLIB
+ decompressThread<8>();
+#else
+#pragma message \
+ "ZLIB is not present! Deflate compression will not be supported!"
+ mRaw->setError("deflate support is disabled.");
+#endif
+ } else if (compression == 9) {
+ /* GOPRO VC-5 */
+ decompressThread<9>();
+ } else if (compression == 0x884c) {
+ /* Lossy DNG */
+#ifdef HAVE_JPEG
+ decompressThread<0x884c>();
+#else
+#pragma message "JPEG is not present! Lossy JPEG DNG will not be supported!"
+ mRaw->setError("jpeg support is disabled.");
+#endif
+ } else
+ mRaw->setError("AbstractDngDecompressor: Unknown compression");
+}
+
+void AbstractDngDecompressor::decompress() const {
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) num_threads( \
+ rawspeed_get_number_of_processor_cores()) if (slices.size() > 1)
+#endif
+ decompressThread();
+
+ std::string firstErr;
+ if (mRaw->isTooManyErrors(1, &firstErr)) {
+ ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
+ firstErr.c_str());
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.h
new file mode 100644
index 00000000..b902f0fe
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractDngDecompressor.h
@@ -0,0 +1,145 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017-2018 Roman Lebeedv
+
+ 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 "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include <cassert> // for assert
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+struct DngTilingDescription final {
+ // The dimensions of the whole image.
+ const iPoint2D& dim;
+
+ // How many horizontal pixels does one tile represent?
+ const uint32 tileW;
+
+ // How many vertical pixels does one tile represent?
+ const uint32 tileH;
+
+ // How many tiles per row is there?
+ const uint32 tilesX;
+
+ // How many rows is there?
+ const uint32 tilesY;
+
+ // How many tiles are there total?
+ const unsigned numTiles;
+
+ DngTilingDescription(const iPoint2D& dim_, uint32 tileW_, uint32 tileH_)
+ : dim(dim_), tileW(tileW_), tileH(tileH_),
+ tilesX(roundUpDivision(dim.x, tileW)),
+ tilesY(roundUpDivision(dim.y, tileH)), numTiles(tilesX * tilesY) {
+ assert(dim.area() > 0);
+ assert(tileW > 0);
+ assert(tileH > 0);
+ assert(tilesX > 0);
+ assert(tilesY > 0);
+ assert(tileW * tilesX >= static_cast<unsigned>(dim.x));
+ assert(tileH * tilesY >= static_cast<unsigned>(dim.y));
+ assert(tileW * (tilesX - 1) < static_cast<unsigned>(dim.x));
+ assert(tileH * (tilesY - 1) < static_cast<unsigned>(dim.y));
+ assert(numTiles > 0);
+ }
+};
+
+struct DngSliceElement final {
+ const DngTilingDescription& dsc;
+
+ // Which slice is this?
+ const unsigned n;
+
+ // The actual data of the tile.
+ const ByteStream bs;
+
+ // Which tile is this?
+ const unsigned column;
+ const unsigned row;
+
+ const bool lastColumn;
+ const bool lastRow;
+
+ // Where does it start?
+ const unsigned offX;
+ const unsigned offY;
+
+ // What's it's actual size?
+ const unsigned width;
+ const unsigned height;
+
+ DngSliceElement(const DngTilingDescription& dsc_, unsigned n_, ByteStream bs_)
+ : dsc(dsc_), n(n_), bs(std::move(bs_)), column(n % dsc.tilesX),
+ row(n / dsc.tilesX), lastColumn((column + 1) == dsc.tilesX),
+ lastRow((row + 1) == dsc.tilesY), offX(dsc.tileW * column),
+ offY(dsc.tileH * row),
+ width(!lastColumn ? dsc.tileW : dsc.dim.x - offX),
+ height(!lastRow ? dsc.tileH : dsc.dim.y - offY) {
+ assert(n < dsc.numTiles);
+ assert(bs.getRemainSize() > 0);
+ assert(column < dsc.tilesX);
+ assert(row < dsc.tilesY);
+ assert(offX < static_cast<unsigned>(dsc.dim.x));
+ assert(offY < static_cast<unsigned>(dsc.dim.y));
+ assert(width > 0);
+ assert(height > 0);
+ assert(offX + width <= static_cast<unsigned>(dsc.dim.x));
+ assert(offY + height <= static_cast<unsigned>(dsc.dim.y));
+ assert(!lastColumn || (offX + width == static_cast<unsigned>(dsc.dim.x)));
+ assert(!lastRow || (offY + height == static_cast<unsigned>(dsc.dim.y)));
+ }
+};
+
+class AbstractDngDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+
+ template <int compression> void decompressThread() const noexcept;
+
+ void decompressThread() const noexcept;
+
+public:
+ AbstractDngDecompressor(const RawImage& img, DngTilingDescription dsc_,
+ int compression_, bool mFixLjpeg_, uint32 mBps_,
+ uint32 mPredictor_)
+ : mRaw(img), dsc(dsc_), compression(compression_), mFixLjpeg(mFixLjpeg_),
+ mBps(mBps_), mPredictor(mPredictor_) {}
+
+ void decompress() const;
+
+ const DngTilingDescription dsc;
+
+ std::vector<DngSliceElement> slices;
+
+ const int compression;
+ const bool mFixLjpeg = false;
+ const uint32 mBps;
+ const uint32 mPredictor;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractHuffmanTable.h
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractHuffmanTable.h
new file mode 100644
index 00000000..a62c1e9a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractHuffmanTable.h
@@ -0,0 +1,247 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+ 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
+ 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 uchar8, uint32, ushort16
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/Buffer.h" // for Buffer
+#include <algorithm> // for copy, adjacent_find, max_e...
+#include <cassert> // for assert
+#include <cstddef> // for size_t
+#include <functional> // for less, less_equal
+#include <iterator> // for back_insert_iterator, back...
+#include <numeric> // for accumulate
+#include <vector> // for vector, operator==
+
+namespace rawspeed {
+
+class AbstractHuffmanTable {
+public:
+ struct CodeSymbol final {
+ ushort16 code; // the code (bit pattern found inside the stream)
+ uchar8 code_len; // the code length in bits, valid values are 1..16
+
+ CodeSymbol() = default;
+
+ CodeSymbol(ushort16 code_, uchar8 code_len_)
+ : code(code_), code_len(code_len_) {
+ assert(code_len > 0);
+ assert(code_len <= 16);
+ assert(code <= ((1U << code_len) - 1U));
+ }
+
+ static bool HaveCommonPrefix(const CodeSymbol& symbol,
+ const CodeSymbol& partial) {
+ assert(partial.code_len <= symbol.code_len);
+
+ auto getNHighBits = [](const CodeSymbol& s, unsigned bits) -> ushort16 {
+ const auto shift = s.code_len - bits;
+ return s.code >> shift;
+ };
+
+ const auto s0 = getNHighBits(symbol, partial.code_len);
+ const auto s1 = partial.code;
+
+ return s0 == s1;
+ }
+ };
+
+protected:
+ inline size_t __attribute__((pure)) maxCodePlusDiffLength() const {
+ return nCodesPerLength.size() - 1 +
+ *(std::max_element(codeValues.cbegin(), codeValues.cend()));
+ }
+
+ // These two fields directly represent the contents of a JPEG DHT field
+
+ // 1. The number of codes there are per bit length, this is index 1 based.
+ // (there are always 0 codes of length 0)
+ std::vector<unsigned int> nCodesPerLength; // index is length of code
+
+ inline unsigned int __attribute__((pure)) maxCodesCount() const {
+ return std::accumulate(nCodesPerLength.begin(), nCodesPerLength.end(), 0U);
+ }
+
+ // 2. This is the actual huffman encoded data, i.e. the 'alphabet'. Each value
+ // is the number of bits following the code that encode the difference to the
+ // last pixel. Valid values are in the range 0..16.
+ // signExtended() is used to decode the difference bits to a signed int.
+ std::vector<uchar8> codeValues; // index is just sequential number
+
+ static void VerifyCodeSymbols(const std::vector<CodeSymbol>& symbols) {
+#ifndef NDEBUG
+ // The code symbols are ordered so that all the code values are strictly
+ // increasing and code lenghts are not decreasing.
+ const auto symbolSort = [](const CodeSymbol& lhs,
+ const CodeSymbol& rhs) -> bool {
+ return std::less<>()(lhs.code, rhs.code) &&
+ std::less_equal<>()(lhs.code_len, rhs.code_len);
+ };
+#endif
+ assert(std::adjacent_find(symbols.cbegin(), symbols.cend(),
+ [&symbolSort](const CodeSymbol& lhs,
+ const CodeSymbol& rhs) -> bool {
+ return !symbolSort(lhs, rhs);
+ }) == symbols.cend() &&
+ "all code symbols are globally ordered");
+
+ // No two symbols should have the same prefix (high bytes)
+ // Only analyze the lower triangular matrix, excluding diagonal
+ for (auto sId = 0UL; sId < symbols.size(); sId++) {
+ for (auto pId = 0UL; pId < sId; pId++)
+ assert(!CodeSymbol::HaveCommonPrefix(symbols[sId], symbols[pId]));
+ }
+ }
+
+ std::vector<CodeSymbol> generateCodeSymbols() const {
+ std::vector<CodeSymbol> symbols;
+
+ assert(!nCodesPerLength.empty());
+ assert(maxCodesCount() > 0);
+
+ const auto maxCodeLength = nCodesPerLength.size() - 1U;
+ assert(codeValues.size() == maxCodesCount());
+
+ // reserve all the memory. avoids lots of small allocs
+ symbols.reserve(maxCodesCount());
+
+ // Figure C.1: make table of Huffman code length for each symbol
+ // Figure C.2: generate the codes themselves
+ uint32 code = 0;
+ for (unsigned int l = 1; l <= maxCodeLength; ++l) {
+ for (unsigned int i = 0; i < nCodesPerLength[l]; ++i) {
+ assert(code <= 0xffff);
+
+ symbols.emplace_back(code, l);
+ code++;
+ }
+
+ code <<= 1;
+ }
+
+ assert(symbols.size() == maxCodesCount());
+ VerifyCodeSymbols(symbols);
+
+ return symbols;
+ }
+
+public:
+ bool operator==(const AbstractHuffmanTable& other) const {
+ return nCodesPerLength == other.nCodesPerLength &&
+ codeValues == other.codeValues;
+ }
+
+ uint32 setNCodesPerLength(const Buffer& data) {
+ assert(data.getSize() == 16);
+
+ nCodesPerLength.resize(17, 0);
+ std::copy(data.begin(), data.end(), &nCodesPerLength[1]);
+ assert(nCodesPerLength[0] == 0);
+
+ // trim empty entries from the codes per length table on the right
+ while (!nCodesPerLength.empty() && nCodesPerLength.back() == 0)
+ nCodesPerLength.pop_back();
+
+ if (nCodesPerLength.empty())
+ ThrowRDE("Codes-per-length table is empty");
+
+ assert(nCodesPerLength.back() > 0);
+
+ const auto count = maxCodesCount();
+ assert(count > 0);
+
+ if (count > 162)
+ ThrowRDE("Too big code-values table");
+
+ // We are at the Root node, len is 1, there are two possible child Nodes
+ unsigned maxCodes = 2;
+
+ for (auto codeLen = 1UL; codeLen < nCodesPerLength.size(); codeLen++) {
+ // we have codeLen bits. make sure that that code count can actually fit
+ // E.g. for len 1 we could have two codes: 0b0 and 0b1
+ // (but in that case there can be no other codes (with higher lenghts))
+ const auto maxCodesInCurrLen = (1U << codeLen);
+ const auto nCodes = nCodesPerLength[codeLen];
+ if (nCodes > maxCodesInCurrLen) {
+ ThrowRDE("Corrupt Huffman. Can never have %u codes in %lu-bit len",
+ nCodes, codeLen);
+ }
+
+ // Also, check that we actually can have this much leafs for this lenght
+ if (nCodes > maxCodes) {
+ ThrowRDE(
+ "Corrupt Huffman. Can only fit %u out of %u codes in %lu-bit len",
+ maxCodes, nCodes, codeLen);
+ }
+
+ // There are nCodes leafs on this level, and those can not be branches
+ maxCodes -= nCodes;
+ // On the next level, rest can be branches, and can have two child Nodes
+ maxCodes *= 2;
+ }
+
+ return count;
+ }
+
+ void setCodeValues(const Buffer& data) {
+ // spec says max 16 but Hasselblad ignores that -> allow 17
+ // Canon's old CRW really ignores this ...
+ assert(data.getSize() <= 162);
+ assert(data.getSize() == maxCodesCount());
+
+ codeValues.clear();
+ codeValues.reserve(maxCodesCount());
+ std::copy(data.begin(), data.end(), std::back_inserter(codeValues));
+ assert(codeValues.size() == maxCodesCount());
+
+ for (const auto cValue : codeValues) {
+ if (cValue > 16)
+ ThrowRDE("Corrupt Huffman. Code value %u is bigger than 16", cValue);
+ }
+ }
+
+ // WARNING: the caller should check that len != 0 before calling the function
+ inline static int __attribute__((const))
+ signExtended(uint32 diff, uint32 len) {
+ int32 ret = diff;
+#if 0
+#define _X(x) (1 << x) - 1
+ constexpr static int offset[16] = {
+ 0, _X(1), _X(2), _X(3), _X(4), _X(5), _X(6), _X(7),
+ _X(8), _X(9), _X(10), _X(11), _X(12), _X(13), _X(14), _X(15)};
+#undef _X
+ if ((diff & (1 << (len - 1))) == 0)
+ ret -= offset[len];
+#else
+ if ((diff & (1 << (len - 1))) == 0)
+ ret -= (1 << len) - 1;
+#endif
+ return ret;
+ }
+};
+
+inline bool operator==(const AbstractHuffmanTable::CodeSymbol& lhs,
+ const AbstractHuffmanTable::CodeSymbol& rhs) {
+ return lhs.code == rhs.code && lhs.code_len == rhs.code_len;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.cpp
new file mode 100644
index 00000000..a56c3056
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.cpp
@@ -0,0 +1,267 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/AbstractLJpegDecompressor.h"
+#include "common/Common.h" // for uint32, uchar8
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable
+#include "decompressors/HuffmanTable.h" // for HuffmanTable, Huffma...
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianne...
+#include <array> // for array
+#include <cassert> // for assert
+#include <memory> // for unique_ptr, make_unique
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+AbstractLJpegDecompressor::AbstractLJpegDecompressor(ByteStream bs,
+ const RawImage& img)
+ : input(std::move(bs)), mRaw(img) {
+ input.setByteOrder(Endianness::big);
+
+ if (mRaw->dim.x == 0 || mRaw->dim.y == 0)
+ ThrowRDE("Image has zero size");
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // Yeah, sure, here it would be just dumb to leave this for production :)
+ if (mRaw->dim.x > 8896 || mRaw->dim.y > 6304) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+#endif
+}
+
+void AbstractLJpegDecompressor::decode() {
+ if (getNextMarker(false) != M_SOI)
+ ThrowRDE("Image did not start with SOI. Probably not an LJPEG");
+
+ struct FoundMarkers {
+ bool DHT = false;
+ bool SOF = false;
+ bool SOS = false;
+ } FoundMarkers;
+
+ JpegMarker m;
+ do {
+ m = getNextMarker(true);
+
+ if (m == M_EOI)
+ break;
+
+ ByteStream data(input.getStream(input.peekU16()));
+ data.skipBytes(2); // headerLength
+
+ switch (m) {
+ case M_DHT:
+ if (FoundMarkers.SOS)
+ ThrowRDE("Found second DHT marker after SOS");
+ // there can be more than one DHT markers.
+ // FIXME: do we really want to reparse and use the last one?
+ parseDHT(data);
+ FoundMarkers.DHT = true;
+ break;
+ case M_SOF3:
+ if (FoundMarkers.SOS)
+ ThrowRDE("Found second SOF marker after SOS");
+ if (FoundMarkers.SOF)
+ ThrowRDE("Found second SOF marker");
+ // SOF is not required to be after DHT
+ parseSOF(data, &frame);
+ FoundMarkers.SOF = true;
+ break;
+ case M_SOS:
+ if (FoundMarkers.SOS)
+ ThrowRDE("Found second SOS marker");
+ if (!FoundMarkers.DHT)
+ ThrowRDE("Did not find DHT marker before SOS.");
+ if (!FoundMarkers.SOF)
+ ThrowRDE("Did not find SOF marker before SOS.");
+ parseSOS(data);
+ FoundMarkers.SOS = true;
+ break;
+ case M_DQT:
+ ThrowRDE("Not a valid RAW file.");
+ default: // Just let it skip to next marker
+ break;
+ }
+ } while (m != M_EOI);
+
+ if (!FoundMarkers.SOS)
+ ThrowRDE("Did not find SOS marker.");
+}
+
+void AbstractLJpegDecompressor::parseSOF(ByteStream sofInput, SOFInfo* sof) {
+ sof->prec = sofInput.getByte();
+ sof->h = sofInput.getU16();
+ sof->w = sofInput.getU16();
+ sof->cps = sofInput.getByte();
+
+ if (sof->prec < 2 || sof->prec > 16)
+ ThrowRDE("Invalid precision (%u).", sof->prec);
+
+ if (sof->h == 0 || sof->w == 0)
+ ThrowRDE("Frame width or height set to zero");
+
+ if (sof->cps > 4 || sof->cps < 1)
+ ThrowRDE("Only from 1 to 4 components are supported.");
+
+ if (sof->cps < mRaw->getCpp()) {
+ ThrowRDE("Component count should be no less than sample count (%u vs %u).",
+ sof->cps, mRaw->getCpp());
+ }
+
+ if (sof->cps > static_cast<uint32>(mRaw->dim.x)) {
+ ThrowRDE("Component count should be no greater than row length (%u vs %u).",
+ sof->cps, mRaw->dim.x);
+ }
+
+ if (sofInput.getRemainSize() != 3 * sof->cps)
+ ThrowRDE("Header size mismatch.");
+
+ for (uint32 i = 0; i < sof->cps; i++) {
+ sof->compInfo[i].componentId = sofInput.getByte();
+
+ uint32 subs = sofInput.getByte();
+ frame.compInfo[i].superV = subs & 0xf;
+ frame.compInfo[i].superH = subs >> 4;
+
+ if (frame.compInfo[i].superV < 1 || frame.compInfo[i].superV > 4)
+ ThrowRDE("Horizontal sampling factor is invalid.");
+
+ if (frame.compInfo[i].superH < 1 || frame.compInfo[i].superH > 4)
+ ThrowRDE("Horizontal sampling factor is invalid.");
+
+ uint32 Tq = sofInput.getByte();
+ if (Tq != 0)
+ ThrowRDE("Quantized components not supported.");
+ }
+
+ sof->initialized = true;
+
+ mRaw->metadata.subsampling.x = sof->compInfo[0].superH;
+ mRaw->metadata.subsampling.y = sof->compInfo[0].superV;
+}
+
+void AbstractLJpegDecompressor::parseSOS(ByteStream sos) {
+ assert(frame.initialized);
+
+ if (sos.getRemainSize() != 1 + 2 * frame.cps + 3)
+ ThrowRDE("Invalid SOS header length.");
+
+ uint32 soscps = sos.getByte();
+ if (frame.cps != soscps)
+ ThrowRDE("Component number mismatch.");
+
+ for (uint32 i = 0; i < frame.cps; i++) {
+ uint32 cs = sos.getByte();
+ uint32 td = sos.getByte() >> 4;
+
+ if (td >= huff.size() || !huff[td])
+ ThrowRDE("Invalid Huffman table selection.");
+
+ int ciIndex = -1;
+ for (uint32 j = 0; j < frame.cps; ++j) {
+ if (frame.compInfo[j].componentId == cs)
+ ciIndex = j;
+ }
+
+ if (ciIndex == -1)
+ ThrowRDE("Invalid Component Selector");
+
+ frame.compInfo[ciIndex].dcTblNo = td;
+ }
+
+ // Get predictor, see table H.1 from the JPEG spec
+ predictorMode = sos.getByte();
+ // The spec says predictoreMode is in [0..7], but Hasselblad uses '8'.
+ if (predictorMode > 8)
+ ThrowRDE("Invalid predictor mode.");
+
+ // Se + Ah Not used in LJPEG
+ if (sos.getByte() != 0)
+ ThrowRDE("Se/Ah not zero.");
+
+ Pt = sos.getByte(); // Point Transform
+ if (Pt > 15)
+ ThrowRDE("Invalid Point transform.");
+
+ decodeScan();
+}
+
+void AbstractLJpegDecompressor::parseDHT(ByteStream dht) {
+ while (dht.getRemainSize() > 0) {
+ uint32 b = dht.getByte();
+
+ uint32 htClass = b >> 4;
+ if (htClass != 0)
+ ThrowRDE("Unsupported Table class.");
+
+ uint32 htIndex = b & 0xf;
+ if (htIndex >= huff.size())
+ ThrowRDE("Invalid huffman table destination id.");
+
+ if (huff[htIndex] != nullptr)
+ ThrowRDE("Duplicate table definition");
+
+ // copy 16 bytes from input stream to number of codes per length table
+ uint32 nCodes = ht_.setNCodesPerLength(dht.getBuffer(16));
+
+ // spec says 16 different codes is max but Hasselblad violates that -> 17
+ if (nCodes > 17)
+ ThrowRDE("Invalid DHT table.");
+
+ // copy nCodes bytes from input stream to code values table
+ ht_.setCodeValues(dht.getBuffer(nCodes));
+
+ // see if we already have a HuffmanTable with the same codes
+ for (const auto& i : huffmanTableStore)
+ if (*i == ht_)
+ huff[htIndex] = i.get();
+
+ if (!huff[htIndex]) {
+ // setup new ht_ and put it into the store
+ auto dHT = std::make_unique<HuffmanTable>(ht_);
+ dHT->setup(fullDecodeHT, fixDng16Bug);
+ huff[htIndex] = dHT.get();
+ huffmanTableStore.emplace_back(std::move(dHT));
+ }
+ }
+}
+
+JpegMarker AbstractLJpegDecompressor::getNextMarker(bool allowskip) {
+ uchar8 c0;
+ uchar8 c1 = input.getByte();
+ do {
+ c0 = c1;
+ c1 = input.getByte();
+ } while (allowskip && !(c0 == 0xFF && c1 != 0 && c1 != 0xFF));
+
+ if (!(c0 == 0xFF && c1 != 0 && c1 != 0xFF))
+ ThrowRDE("(Noskip) Expected marker not found. Propably corrupt file.");
+
+ return static_cast<JpegMarker>(c1);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.h
new file mode 100644
index 00000000..53a6d8d4
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractLJpegDecompressor.h
@@ -0,0 +1,199 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, ushort16
+#include "common/RawImage.h" // for RawImage
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array
+#include <memory> // for unique_ptr
+#include <vector> // for vector
+
+/*
+ * The following enum and two structs are stolen from the IJG JPEG library
+ * Comments added by tm. See also Copyright in HuffmanTable.h.
+ */
+
+namespace rawspeed {
+
+enum JpegMarker { /* JPEG marker codes */
+ M_STUFF = 0x00,
+ M_SOF0 = 0xc0, /* baseline DCT */
+ M_SOF1 = 0xc1, /* extended sequential DCT */
+ M_SOF2 = 0xc2, /* progressive DCT */
+ M_SOF3 = 0xc3, /* lossless (sequential) */
+
+ M_SOF5 = 0xc5, /* differential sequential DCT */
+ M_SOF6 = 0xc6, /* differential progressive DCT */
+ M_SOF7 = 0xc7, /* differential lossless */
+
+ M_JPG = 0xc8, /* JPEG extensions */
+ M_SOF9 = 0xc9, /* extended sequential DCT */
+ M_SOF10 = 0xca, /* progressive DCT */
+ M_SOF11 = 0xcb, /* lossless (sequential) */
+
+ M_SOF13 = 0xcd, /* differential sequential DCT */
+ M_SOF14 = 0xce, /* differential progressive DCT */
+ M_SOF15 = 0xcf, /* differential lossless */
+
+ M_DHT = 0xc4, /* define Huffman tables */
+
+ M_DAC = 0xcc, /* define arithmetic conditioning table */
+
+ M_RST0 = 0xd0, /* restart */
+ M_RST1 = 0xd1, /* restart */
+ M_RST2 = 0xd2, /* restart */
+ M_RST3 = 0xd3, /* restart */
+ M_RST4 = 0xd4, /* restart */
+ M_RST5 = 0xd5, /* restart */
+ M_RST6 = 0xd6, /* restart */
+ M_RST7 = 0xd7, /* restart */
+
+ M_SOI = 0xd8, /* start of image */
+ M_EOI = 0xd9, /* end of image */
+ M_SOS = 0xda, /* start of scan */
+ M_DQT = 0xdb, /* define quantization tables */
+ M_DNL = 0xdc, /* define number of lines */
+ M_DRI = 0xdd, /* define restart interval */
+ M_DHP = 0xde, /* define hierarchical progression */
+ M_EXP = 0xdf, /* expand reference image(s) */
+
+ M_APP0 = 0xe0, /* application marker, used for JFIF */
+ M_APP1 = 0xe1, /* application marker */
+ M_APP2 = 0xe2, /* application marker */
+ M_APP3 = 0xe3, /* application marker */
+ M_APP4 = 0xe4, /* application marker */
+ M_APP5 = 0xe5, /* application marker */
+ M_APP6 = 0xe6, /* application marker */
+ M_APP7 = 0xe7, /* application marker */
+ M_APP8 = 0xe8, /* application marker */
+ M_APP9 = 0xe9, /* application marker */
+ M_APP10 = 0xea, /* application marker */
+ M_APP11 = 0xeb, /* application marker */
+ M_APP12 = 0xec, /* application marker */
+ M_APP13 = 0xed, /* application marker */
+ M_APP14 = 0xee, /* application marker, used by Adobe */
+ M_APP15 = 0xef, /* application marker */
+
+ M_JPG0 = 0xf0, /* reserved for JPEG extensions */
+ M_JPG13 = 0xfd, /* reserved for JPEG extensions */
+ M_COM = 0xfe, /* comment */
+
+ M_TEM = 0x01, /* temporary use */
+ M_FILL = 0xFF
+
+
+};
+
+/*
+* The following structure stores basic information about one component.
+*/
+struct JpegComponentInfo {
+ /*
+ * These values are fixed over the whole image.
+ * They are read from the SOF marker.
+ */
+ uint32 componentId = ~0U; /* identifier for this component (0..255) */
+
+ /*
+ * Huffman table selector (0..3). The value may vary
+ * between scans. It is read from the SOS marker.
+ */
+ uint32 dcTblNo = ~0U;
+ uint32 superH = ~0U; // Horizontal Supersampling
+ uint32 superV = ~0U; // Vertical Supersampling
+};
+
+class SOFInfo {
+public:
+ std::array<JpegComponentInfo, 4> compInfo;
+ uint32 w = 0; // Width
+ uint32 h = 0; // Height
+ uint32 cps = 0; // Components
+ uint32 prec = 0; // Precision
+ bool initialized = false;
+};
+
+class AbstractLJpegDecompressor : public AbstractDecompressor {
+ // std::vector of unique HTs, to not recreate HT, but cache them
+ std::vector<std::unique_ptr<HuffmanTable>> huffmanTableStore;
+ HuffmanTable ht_; // temporary table, used
+
+ uint32 Pt = 0;
+ std::array<HuffmanTable*, 4> huff{{}}; // 4 pointers into the store
+
+public:
+ AbstractLJpegDecompressor(ByteStream bs, const RawImage& img);
+
+ virtual ~AbstractLJpegDecompressor() = default;
+
+protected:
+ bool fixDng16Bug = false; // DNG v1.0.x compatibility
+ bool fullDecodeHT = true; // FullDecode Huffman
+
+ void decode();
+ void parseSOF(ByteStream data, SOFInfo* i);
+ void parseSOS(ByteStream data);
+ void parseDHT(ByteStream data);
+ JpegMarker getNextMarker(bool allowskip);
+
+ template <int N_COMP>
+ std::array<HuffmanTable*, N_COMP> getHuffmanTables() const {
+ std::array<HuffmanTable*, N_COMP> ht;
+ for (int i = 0; i < N_COMP; ++i) {
+ const unsigned dcTblNo = frame.compInfo[i].dcTblNo;
+ const unsigned dcTbls = huff.size();
+ if (dcTblNo >= dcTbls) {
+ ThrowRDE("Decoding table %u for comp %i does not exist (tables = %u)",
+ dcTblNo, i, dcTbls);
+ }
+ ht[i] = huff[dcTblNo];
+ }
+
+ return ht;
+ }
+
+ template <int N_COMP>
+ __attribute__((pure)) std::array<ushort16, N_COMP>
+ getInitialPredictors() const {
+ std::array<ushort16, N_COMP> pred;
+ if (frame.prec < (Pt + 1)) {
+ ThrowRDE("Invalid precision (%u) and point transform (%u) combination!",
+ frame.prec, Pt);
+ }
+ pred.fill(1 << (frame.prec - Pt - 1));
+ return pred;
+ }
+
+ virtual void decodeScan() = 0;
+
+ ByteStream input;
+ RawImage mRaw;
+
+ SOFInfo frame;
+ uint32 predictorMode = 0;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/AbstractSamsungDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractSamsungDecompressor.h
new file mode 100644
index 00000000..653e1be5
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/AbstractSamsungDecompressor.h
@@ -0,0 +1,36 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+
+namespace rawspeed {
+
+class AbstractSamsungDecompressor : public AbstractDecompressor {
+protected:
+ RawImage mRaw;
+
+public:
+ explicit AbstractSamsungDecompressor(const RawImage& raw) : mRaw(raw) {}
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/BinaryHuffmanTree.h
b/subprojects/rawspeed/src/librawspeed/decompressors/BinaryHuffmanTree.h
new file mode 100644
index 00000000..67fa3287
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/BinaryHuffmanTree.h
@@ -0,0 +1,240 @@
+/*
+ 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 <cassert> // for assert
+#include <initializer_list> // IWYU pragma: keep
+#include <memory> // for unique_ptr
+#include <vector> // for vector
+
+namespace rawspeed {
+
+template <typename T>
+class BinaryHuffmanTree final /* : public BinarySearchTree */ {
+public:
+ // User-provided default constructor to appease clang-3.5
+ BinaryHuffmanTree() {} // NOLINT hicpp-use-equals-default
+
+ struct Branch;
+ struct Leaf;
+
+ struct Node {
+ enum class Type { Branch, Leaf };
+
+ explicit virtual operator Type() const = 0;
+
+ Branch& getAsBranch() {
+ assert(Node::Type::Branch == static_cast<Node::Type>(*this));
+ return static_cast<Branch&>(*this);
+ }
+
+ Leaf& getAsLeaf() {
+ assert(Node::Type::Leaf == static_cast<Node::Type>(*this));
+ return static_cast<Leaf&>(*this);
+ }
+
+ virtual ~Node() = default;
+ };
+
+ struct Branch final : public Node {
+ explicit operator typename Node::Type() const override {
+ return Node::Type::Branch;
+ }
+
+ std::unique_ptr<Node> zero;
+ std::unique_ptr<Node> one;
+
+ template <typename Lambda> bool forEachNode(Lambda l) const;
+ template <typename Lambda> bool forEachNode(Lambda l);
+
+ bool hasLeafs() const;
+
+ static bool pruneLeaflessBranches(std::unique_ptr<Node>* n);
+ };
+
+ struct Leaf final : public Node {
+ explicit operator typename Node::Type() const override {
+ return Node::Type::Leaf;
+ }
+
+ T value;
+
+ Leaf() = default;
+
+ explicit Leaf(T value_) : value(value_) {}
+ };
+
+ std::unique_ptr<Node> root;
+
+ std::vector<Branch*> getAllBranchesOfDepth(int depth);
+ std::vector<std::unique_ptr<Node>*> getAllVacantNodesAtDepth(int depth);
+ void pruneLeaflessBranches();
+};
+
+template <typename T>
+template <typename Lambda>
+bool BinaryHuffmanTree<T>::Branch::forEachNode(Lambda l) const {
+ bool done = false;
+ // NOTE: The order *IS* important! Left to right, zero to one!
+ for (const auto* node : {&zero, &one}) {
+ done = l(node);
+ if (done)
+ return done;
+ }
+ return done;
+}
+
+template <typename T>
+template <typename Lambda>
+bool BinaryHuffmanTree<T>::Branch::forEachNode(Lambda l) {
+ bool done = false;
+ // NOTE: The order *IS* important! Left to right, zero to one!
+ for (auto* node : {&zero, &one}) {
+ done = l(node);
+ if (done)
+ return done;
+ }
+ return done;
+}
+
+template <typename T> bool BinaryHuffmanTree<T>::Branch::hasLeafs() const {
+ return forEachNode([](const std::unique_ptr<Node>* n) {
+ assert(n);
+ if (!(*n)) // If the node is empty, then it certainly does not have leafs
+ return false;
+ return Node::Type::Leaf == static_cast<typename Node::Type>(**n);
+ });
+}
+
+template <typename T>
+bool BinaryHuffmanTree<T>::Branch::pruneLeaflessBranches(
+ std::unique_ptr<Node>* top) {
+ if (!top)
+ return false;
+
+ bool foundLeafs = false; // Any leafs in this branch?
+ (*top)->getAsBranch().forEachNode([&foundLeafs](std::unique_ptr<Node>* n) {
+ assert(n);
+ if (!(*n))
+ return false; // Nothing to do here, node is empty already, keep going.
+ switch (static_cast<typename Node::Type>(**n)) {
+ case Node::Type::Branch:
+ // Recurse. Any leafs in this branch?
+ if (Branch::pruneLeaflessBranches(n))
+ foundLeafs = true;
+ else
+ n->reset(); // Aha, dead branch, prune it!
+ break;
+ case Node::Type::Leaf:
+ foundLeafs = true; // Ok, this is a Leaf, great.
+ break;
+ }
+ return false; // keep going.
+ });
+
+ if (!foundLeafs)
+ top->reset();
+
+ return foundLeafs;
+}
+
+template <typename T>
+std::vector<typename BinaryHuffmanTree<T>::Branch*>
+BinaryHuffmanTree<T>::getAllBranchesOfDepth(int depth) {
+ assert(depth >= 0);
+
+ if (0 == depth) {
+ // The root (depth == 0) is is special, and is *always* a Branch.
+ if (!root)
+ root = std::make_unique<Branch>();
+ return {&root->getAsBranch()};
+ }
+
+ // Recursively get all branches of previous depth
+ auto prevBranches = getAllBranchesOfDepth(depth - 1);
+
+ // Early return in case of no branches on previous depth
+ if (prevBranches.empty())
+ return {};
+
+ // We will have at most twice as much branches as at the previous depth.
+ decltype(prevBranches) branches;
+ branches.reserve(2U * prevBranches.size());
+
+ for (const auto& prevBranch : prevBranches) {
+ assert(prevBranch);
+
+ prevBranch->forEachNode([&branches](std::unique_ptr<Node>* n) {
+ assert(n);
+ // If the Node is vacant, make it a branch.
+ // The user was supposed to create all the required Leafs before.
+ // We shall prune Leaf-less branches at the end
+ if (!(*n))
+ *n = std::make_unique<Branch>();
+ // If this is a branch, add it to the list.
+ if (Node::Type::Branch == static_cast<typename Node::Type>(**n))
+ branches.emplace_back(&((*n)->getAsBranch()));
+ return false; // keep going;
+ });
+ }
+ assert(branches.size() <= 2U * prevBranches.size());
+
+ return branches;
+}
+
+template <typename T>
+std::vector<std::unique_ptr<typename BinaryHuffmanTree<T>::Node>*>
+BinaryHuffmanTree<T>::getAllVacantNodesAtDepth(int depth) {
+ assert(depth > 0);
+
+ // Get all branches of previous depth
+ auto prevBranches = getAllBranchesOfDepth(depth - 1);
+
+ // Early return in case of no branches on previous depth
+ if (prevBranches.empty())
+ return {};
+
+ // We will have at most two nodes per each branch on the previous depth.
+ std::vector<std::unique_ptr<BinaryHuffmanTree<T>::Node>*> nodes;
+ nodes.reserve(2U * prevBranches.size());
+
+ for (const auto& prevBranch : prevBranches) {
+ assert(prevBranch);
+
+ auto& b = prevBranch->getAsBranch();
+
+ b.forEachNode([&nodes](std::unique_ptr<Node>* n) {
+ assert(n);
+ if (!(*n)) // If there is no node already, then record it.
+ nodes.emplace_back(n);
+ return false; // keep going;
+ });
+ }
+ assert(nodes.size() <= 2U * prevBranches.size());
+
+ return nodes;
+}
+
+template <typename T> void BinaryHuffmanTree<T>::pruneLeaflessBranches() {
+ Branch::pruneLeaflessBranches(&root);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.cpp
new file mode 100644
index 00000000..bce287cf
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.cpp
@@ -0,0 +1,251 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+ 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
+ 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/Cr2Decompressor.h"
+#include "common/Common.h" // for unroll_loop, uint32, ushort16
+#include "common/Point.h" // for iPoint2D, iPoint2D::area_type
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpJPEG.h" // for BitPumpJPEG, BitStream<>::...
+#include <algorithm> // for copy_n
+#include <cassert> // for assert
+#include <initializer_list> // for initializer_list
+
+using std::copy_n;
+
+namespace rawspeed {
+
+class ByteStream;
+
+Cr2Decompressor::Cr2Decompressor(const ByteStream& bs, const RawImage& img)
+ : AbstractLJpegDecompressor(bs, img) {
+ if (mRaw->getDataType() != TYPE_USHORT16)
+ ThrowRDE("Unexpected data type");
+
+ if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == 2) ||
+ (mRaw->getCpp() == 3 && mRaw->getBpp() == 6)))
+ ThrowRDE("Unexpected cpp: %u", mRaw->getCpp());
+
+ if (!mRaw->dim.x || !mRaw->dim.y || mRaw->dim.x > 8896 ||
+ mRaw->dim.y > 5920) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+}
+
+void Cr2Decompressor::decodeScan()
+{
+ if (predictorMode != 1)
+ ThrowRDE("Unsupported predictor mode.");
+
+ if (slicing.empty()) {
+ const int slicesWidth = frame.w * frame.cps;
+ if (slicesWidth > mRaw->dim.x)
+ ThrowRDE("Don't know slicing pattern, and failed to guess it.");
+
+ slicing = Cr2Slicing(/*numSlices=*/1, /*sliceWidth=don't care*/ 0,
+ /*lastSliceWidth=*/slicesWidth);
+ }
+
+ bool isSubSampled = false;
+ for (uint32 i = 0; i < frame.cps; i++)
+ isSubSampled = isSubSampled || frame.compInfo[i].superH != 1 ||
+ frame.compInfo[i].superV != 1;
+
+ if (isSubSampled) {
+ if (mRaw->isCFA)
+ ThrowRDE("Cannot decode subsampled image to CFA data");
+
+ if (mRaw->getCpp() != frame.cps)
+ ThrowRDE("Subsampled component count does not match image.");
+
+ if (frame.cps != 3)
+ ThrowRDE("Unsupported number of subsampled components: %u", frame.cps);
+
+ // see http://lclevy.free.fr/cr2/#sraw for overview table
+ bool isSupported = frame.compInfo[0].superH == 2;
+
+ isSupported = isSupported && (frame.compInfo[0].superV == 1 ||
+ frame.compInfo[0].superV == 2);
+
+ for (uint32 i = 1; i < frame.cps; i++)
+ isSupported = isSupported && frame.compInfo[i].superH == 1 &&
+ frame.compInfo[i].superV == 1;
+
+ if (!isSupported) {
+ ThrowRDE("Unsupported subsampling ([[%u, %u], [%u, %u], [%u, %u]])",
+ frame.compInfo[0].superH, frame.compInfo[0].superV,
+ frame.compInfo[1].superH, frame.compInfo[1].superV,
+ frame.compInfo[2].superH, frame.compInfo[2].superV);
+ }
+
+ if (frame.compInfo[0].superV == 2)
+ decodeN_X_Y<3, 2, 2>(); // Cr2 sRaw1/mRaw
+ else {
+ assert(frame.compInfo[0].superV == 1);
+ decodeN_X_Y<3, 2, 1>(); // Cr2 sRaw2/sRaw
+ }
+ } else {
+ switch (frame.cps) {
+ case 2:
+ decodeN_X_Y<2, 1, 1>();
+ break;
+ case 4:
+ decodeN_X_Y<4, 1, 1>();
+ break;
+ default:
+ ThrowRDE("Unsupported number of components: %u", frame.cps);
+ }
+ }
+}
+
+void Cr2Decompressor::decode(const Cr2Slicing& slicing_) {
+ slicing = slicing_;
+ for (auto sliceId = 0; sliceId < slicing.numSlices; sliceId++) {
+ const auto sliceWidth = slicing.widthOfSlice(sliceId);
+ if (sliceWidth <= 0)
+ ThrowRDE("Bad slice width: %i", sliceWidth);
+ }
+
+ AbstractLJpegDecompressor::decode();
+}
+
+// N_COMP == number of components (2, 3 or 4)
+// X_S_F == x/horizontal sampling factor (1 or 2)
+// Y_S_F == y/vertical sampling factor (1 or 2)
+
+template <int N_COMP, int X_S_F, int Y_S_F>
+void Cr2Decompressor::decodeN_X_Y()
+{
+ // To understand the CR2 slice handling and sampling factor behavior, see
+ // https://github.com/lclevy/libcraw2/blob/master/docs/cr2_lossless.pdf?raw=true
+
+ // inner loop decodes one group of pixels at a time
+ // * for <N,1,1>: N = N*1*1 (full raw)
+ // * for <3,2,1>: 6 = 3*2*1
+ // * for <3,2,2>: 12 = 3*2*2
+ // and advances x by N_COMP*X_S_F and y by Y_S_F
+ constexpr int xStepSize = N_COMP * X_S_F;
+ constexpr int yStepSize = Y_S_F;
+
+ auto ht = getHuffmanTables<N_COMP>();
+ auto pred = getInitialPredictors<N_COMP>();
+ auto predNext = reinterpret_cast<ushort16*>(mRaw->getDataUncropped(0, 0));
+
+ BitPumpJPEG bitStream(input);
+
+ uint32 pixelPitch = mRaw->pitch / 2; // Pitch in pixel
+ if (frame.cps != 3 && frame.w * frame.cps > 2 * frame.h) {
+ // Fix Canon double height issue where Canon doubled the width and halfed
+ // the height (e.g. with 5Ds), ask Canon. frame.w needs to stay as is here
+ // because the number of pixels after which the predictor gets updated is
+ // still the doubled width.
+ // see: FIX_CANON_HALF_HEIGHT_DOUBLE_WIDTH
+ frame.h *= 2;
+ }
+
+ if (X_S_F == 2 && Y_S_F == 1)
+ {
+ // fix the inconsistent slice width in sRaw mode, ask Canon.
+ for (auto* width : {&slicing.sliceWidth, &slicing.lastSliceWidth})
+ *width = (*width) * 3 / 2;
+ }
+
+ for (const auto& width : {slicing.sliceWidth, slicing.lastSliceWidth}) {
+ if (width > mRaw->dim.x)
+ ThrowRDE("Slice is longer than image's height, which is unsupported.");
+ if (width % xStepSize != 0) {
+ ThrowRDE("Slice width (%u) should be multiple of pixel group size (%u)",
+ width, xStepSize);
+ }
+ }
+
+ if (iPoint2D::area_type(frame.h) * slicing.totalWidth() <
+ mRaw->getCpp() * mRaw->dim.area())
+ ThrowRDE("Incorrrect slice height / slice widths! Less than image size.");
+
+ unsigned processedPixels = 0;
+ unsigned processedLineSlices = 0;
+ for (auto sliceId = 0; sliceId < slicing.numSlices; sliceId++) {
+ const unsigned sliceWidth = slicing.widthOfSlice(sliceId);
+
+ assert(frame.h % yStepSize == 0);
+ for (unsigned y = 0; y < frame.h; y += yStepSize) {
+ // Fix for Canon 80D mraw format.
+ // In that format, `frame` is 4032x3402, while `mRaw` is 4536x3024.
+ // Consequently, the slices in `frame` wrap around plus there are few
+ // 'extra' sliced lines because sum(slicesW) * sliceH > mRaw->dim.area()
+ // Those would overflow, hence the break.
+ // see FIX_CANON_FRAME_VS_IMAGE_SIZE_MISMATCH
+ unsigned destY = processedLineSlices % mRaw->dim.y;
+ unsigned destX = processedLineSlices / mRaw->dim.y *
+ slicing.widthOfSlice(0) / mRaw->getCpp();
+ if (destX >= static_cast<unsigned>(mRaw->dim.x))
+ break;
+ auto dest =
+ reinterpret_cast<ushort16*>(mRaw->getDataUncropped(destX, destY));
+
+ assert(sliceWidth % xStepSize == 0);
+ if (X_S_F == 1) {
+ if (destX + sliceWidth > static_cast<unsigned>(mRaw->dim.x))
+ ThrowRDE("Bad slice width / frame size / image size combination.");
+ if (((sliceId + 1) == slicing.numSlices) &&
+ ((destX + sliceWidth) < static_cast<unsigned>(mRaw->dim.x)))
+ ThrowRDE("Unsufficient slices - do not fill the entire image");
+ } else {
+ // FIXME.
+ }
+ for (unsigned x = 0; x < sliceWidth; x += xStepSize) {
+ // check if we processed one full raw row worth of pixels
+ if (processedPixels == frame.w) {
+ // if yes -> update predictor by going back exactly one row,
+ // no matter where we are right now.
+ // makes no sense from an image compression point of view, ask Canon.
+ copy_n(predNext, N_COMP, pred.data());
+ predNext = dest;
+ processedPixels = 0;
+ }
+
+ if (X_S_F == 1) { // will be optimized out
+ unroll_loop<N_COMP>([&](int i) {
+ dest[i] = pred[i] += ht[i]->decodeNext(bitStream);
+ });
+ } else {
+ unroll_loop<Y_S_F>([&](int i) {
+ dest[0 + i*pixelPitch] = pred[0] += ht[0]->decodeNext(bitStream);
+ dest[3 + i*pixelPitch] = pred[0] += ht[0]->decodeNext(bitStream);
+ });
+
+ dest[1] = pred[1] += ht[1]->decodeNext(bitStream);
+ dest[2] = pred[2] += ht[2]->decodeNext(bitStream);
+ }
+
+ dest += xStepSize;
+ processedPixels += X_S_F;
+ }
+
+ processedLineSlices += yStepSize;
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.h
new file mode 100644
index 00000000..9165212c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/Cr2Decompressor.h
@@ -0,0 +1,84 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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 ushort16
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractLJpegDecompressor.h" // for AbstractLJpegDe...
+#include <cassert> // for assert
+
+namespace rawspeed {
+
+class ByteStream;
+class RawImage;
+
+class Cr2Slicing {
+ int numSlices = 0;
+ int sliceWidth = 0;
+ int lastSliceWidth = 0;
+
+ friend class Cr2Decompressor;
+
+public:
+ Cr2Slicing() = default;
+
+ Cr2Slicing(ushort16 numSlices_, ushort16 sliceWidth_,
+ ushort16 lastSliceWidth_)
+ : numSlices(numSlices_), sliceWidth(sliceWidth_),
+ lastSliceWidth(lastSliceWidth_) {
+ if (numSlices < 1)
+ ThrowRDE("Bad slice count: %u", numSlices);
+ }
+
+ bool empty() const {
+ return 0 == numSlices && 0 == sliceWidth && 0 == lastSliceWidth;
+ }
+
+ unsigned widthOfSlice(int sliceId) const {
+ assert(sliceId >= 0);
+ assert(sliceId < numSlices);
+ if ((sliceId + 1) == numSlices)
+ return lastSliceWidth;
+ return sliceWidth;
+ }
+
+ unsigned totalWidth() const {
+ int width = 0;
+ for (auto sliceId = 0; sliceId < numSlices; sliceId++)
+ width += widthOfSlice(sliceId);
+ return width;
+ }
+};
+
+class Cr2Decompressor final : public AbstractLJpegDecompressor
+{
+ Cr2Slicing slicing;
+
+ void decodeScan() override;
+ template<int N_COMP, int X_S_F, int Y_S_F> void decodeN_X_Y();
+
+public:
+ Cr2Decompressor(const ByteStream& bs, const RawImage& img);
+ void decode(const Cr2Slicing& slicing);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.cpp
new file mode 100644
index 00000000..6457568c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.cpp
@@ -0,0 +1,335 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2015-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
+*/
+
+#include "decompressors/CrwDecompressor.h"
+#include "common/Common.h" // for uint32, uchar8, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpJPEG.h" // for BitPumpJPEG, BitStream<>::...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array, array<>::value_type
+#include <cassert> // for assert
+
+using std::array;
+
+namespace rawspeed {
+
+CrwDecompressor::CrwDecompressor(const RawImage& img, uint32 dec_table,
+ bool lowbits_, ByteStream rawData)
+ : mRaw(img), lowbits(lowbits_) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const uint32 width = mRaw->dim.x;
+ const uint32 height = mRaw->dim.y;
+
+ if (width == 0 || height == 0 || width % 4 != 0 || width > 4104 ||
+ height > 3048 || (height * width) % 64 != 0)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (lowbits) {
+ // If there are low bits, the first part (size is calculatable) is low bits
+ // Each block is 4 pairs of 2 bits, so we have 1 block per 4 pixels
+ const unsigned lBlocks = 1 * height * width / 4;
+ assert(lBlocks > 0);
+ lowbitInput = rawData.getStream(lBlocks);
+ }
+
+ // We always ignore next 514 bytes of 'padding'. No idea what is in there.
+ rawData.skipBytes(514);
+
+ // Rest is the high bits.
+ rawInput = rawData.getStream(rawData.getRemainSize());
+
+ mHuff = initHuffTables(dec_table);
+}
+
+HuffmanTable CrwDecompressor::makeDecoder(const uchar8* ncpl,
+ const uchar8* values) {
+ assert(ncpl);
+
+ HuffmanTable ht;
+ auto count = ht.setNCodesPerLength(Buffer(ncpl, 16));
+ ht.setCodeValues(Buffer(values, count));
+ ht.setup(false, false);
+
+ return ht;
+}
+
+CrwDecompressor::crw_hts CrwDecompressor::initHuffTables(uint32 table) {
+ if (table > 2)
+ ThrowRDE("Wrong table number: %u", table);
+
+ // NCodesPerLength
+ static const std::array<std::array<uchar8, 16>, 3> first_tree_ncpl = {{
+ {0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 2, 2, 3, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 6, 3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ }};
+
+ static const std::array<std::array<uchar8, 13>, 3> first_tree_len = {{
+ {0x4, 0x3, 0x5, 0x6, 0x2, 0x7, 0x1, 0x8, 0x9, 0x0, 0xa, 0xb, 0xf},
+ {0x3, 0x2, 0x4, 0x1, 0x5, 0x0, 0x6, 0x7, 0x9, 0x8, 0xa, 0xb, 0xf},
+ {0x6, 0x5, 0x7, 0x4, 0x8, 0x3, 0x9, 0x2, 0x0, 0xa, 0x1, 0xb, 0xf},
+ }};
+
+ static const std::array<std::array<uchar8, 13>, 3> first_tree_index = {{
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf},
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf},
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf},
+ }};
+
+ // NCodesPerLength
+ static const std::array<std::array<uchar8, 16>, 3> second_tree_ncpl = {{
+ {0, 2, 2, 2, 1, 4, 2, 1, 2, 5, 1, 1, 0, 0, 0, 139},
+ {0, 2, 2, 1, 4, 1, 4, 1, 3, 3, 1, 0, 0, 0, 0, 140},
+ {0, 0, 6, 2, 1, 3, 3, 2, 5, 1, 2, 2, 8, 10, 0, 117},
+ }};
+
+ static const std::array<std::array<uchar8, 164>, 3> second_tree_len = {{
+ {0x3, 0x4, 0x2, 0x5, 0x1, 0x6, 0x7, 0x8, 0x2, 0x3, 0x1, 0x4, 0x9, 0x5,
+ 0x2, 0x0, 0x1, 0x6, 0xa, 0x0, 0x3, 0x7, 0x4, 0x1, 0x2, 0x8, 0x9, 0x3,
+ 0x5, 0x1, 0x4, 0x2, 0x5, 0x1, 0x6, 0x7, 0x8, 0x9, 0x9, 0x6, 0xa, 0x9,
+ 0x6, 0x7, 0x8, 0x7, 0x2, 0x5, 0x8, 0x3, 0x6, 0x9, 0x7, 0x4, 0x1, 0x9,
+ 0x1, 0x8, 0x5, 0x6, 0x7, 0x9, 0x7, 0x3, 0x7, 0x4, 0x6, 0x8, 0x7, 0x8,
+ 0x5, 0x9, 0x9, 0x1, 0xa, 0x8, 0x8, 0x5, 0x9, 0x6, 0x7, 0x8, 0x7, 0x6,
+ 0x5, 0x4, 0x9, 0x8, 0x1, 0x5, 0x6, 0x4, 0x8, 0x1, 0xa, 0x4, 0x2, 0x9,
+ 0x7, 0x6, 0x4, 0x5, 0xa, 0x7, 0x3, 0x9, 0x8, 0x6, 0x2, 0x7, 0x5, 0x8,
+ 0x9, 0x1, 0x4, 0x1, 0x9, 0xa, 0x2, 0x5, 0x6, 0x7, 0x3, 0x8, 0x1, 0x6,
+ 0xa, 0x4, 0x1, 0xa, 0xa, 0x6, 0x3, 0x1, 0x3, 0x5, 0xa, 0x2, 0xa, 0xa,
+ 0x4, 0x4, 0x3, 0x5, 0x5, 0x3, 0x2, 0x4, 0x2, 0xa, 0xa, 0x4, 0x2, 0xa,
+ 0x3, 0x3, 0x2, 0x3, 0xa, 0x2, 0x2, 0x3, 0xf, 0xf},
+ {0x2, 0x3, 0x1, 0x4, 0x5, 0x2, 0x1, 0x6, 0x3, 0x7, 0x8, 0x4, 0x2, 0x9,
+ 0x1, 0x0, 0x3, 0x5, 0x1, 0x2, 0xa, 0x6, 0x0, 0x4, 0x3, 0x1, 0x2, 0x9,
+ 0x7, 0x5, 0x8, 0x1, 0x4, 0x3, 0x2, 0x9, 0x5, 0x1, 0x9, 0x1, 0x2, 0x6,
+ 0x3, 0x6, 0x8, 0xa, 0x7, 0x1, 0x7, 0x1, 0x9, 0x5, 0x5, 0x8, 0x2, 0x9,
+ 0x1, 0x1, 0x4, 0x9, 0x4, 0x8, 0x1, 0xa, 0x7, 0x1, 0x1, 0x9, 0x9, 0x7,
+ 0x3, 0xa, 0x9, 0x6, 0x6, 0x8, 0xa, 0xa, 0x8, 0x9, 0xa, 0x5, 0x4, 0x6,
+ 0x5, 0x1, 0x6, 0x6, 0x6, 0x6, 0x9, 0x5, 0x9, 0x5, 0x5, 0x4, 0x7, 0x7,
+ 0xa, 0x7, 0x8, 0x3, 0x7, 0x8, 0x9, 0x7, 0x7, 0xa, 0x8, 0x2, 0x4, 0xa,
+ 0x4, 0x6, 0x5, 0xa, 0x4, 0x4, 0x6, 0x2, 0x3, 0x8, 0x5, 0x8, 0x4, 0x5,
+ 0x6, 0x9, 0x2, 0x3, 0x3, 0x2, 0x6, 0x7, 0x3, 0xa, 0x4, 0x5, 0x7, 0x8,
+ 0x8, 0xa, 0x7, 0x7, 0x4, 0x4, 0x2, 0x8, 0x5, 0xa, 0xa, 0x8, 0x3, 0x6,
+ 0x9, 0x2, 0x3, 0x2, 0x2, 0x3, 0xa, 0x3, 0xf, 0xf},
+ {0x4, 0x5, 0x3, 0x6, 0x2, 0x7, 0x1, 0x8, 0x9, 0x2, 0x3, 0x4, 0x1, 0x5,
+ 0xa, 0x6, 0x7, 0x0, 0x0, 0x2, 0x1, 0x8, 0x3, 0x9, 0x4, 0x2, 0x1, 0x5,
+ 0x3, 0x8, 0x7, 0x4, 0x5, 0x6, 0x9, 0x9, 0x7, 0x8, 0x9, 0x8, 0x6, 0x8,
+ 0x7, 0x1, 0x9, 0x7, 0x6, 0x2, 0x6, 0x9, 0xa, 0x5, 0x8, 0x7, 0x9, 0x8,
+ 0x4, 0x6, 0x9, 0x7, 0x7, 0x9, 0xa, 0x5, 0x8, 0x6, 0x7, 0x9, 0x9, 0x8,
+ 0x8, 0x2, 0x7, 0x8, 0x5, 0x4, 0x1, 0x6, 0x9, 0x8, 0xa, 0x6, 0x7, 0x5,
+ 0xa, 0x5, 0x5, 0x6, 0x6, 0x4, 0x9, 0x4, 0x3, 0xa, 0x8, 0x3, 0x5, 0x7,
+ 0x4, 0x6, 0x7, 0xa, 0x4, 0xa, 0x9, 0x8, 0x8, 0x7, 0xa, 0xa, 0x3, 0xa,
+ 0x1, 0x7, 0x4, 0x6, 0x5, 0x9, 0x2, 0x6, 0x1, 0x1, 0x3, 0x6, 0xa, 0x2,
+ 0x5, 0x2, 0x3, 0x5, 0x2, 0x4, 0x4, 0xa, 0x4, 0x5, 0x3, 0x2, 0x1, 0x5,
+ 0x3, 0xa, 0x4, 0xa, 0x2, 0x1, 0x4, 0x1, 0x3, 0x3, 0xa, 0x3, 0x2, 0x2,
+ 0x1, 0x3, 0x2, 0x1, 0x1, 0x3, 0x2, 0x1, 0xf, 0xf},
+ }};
+
+ static const std::array<std::array<uchar8, 164>, 3> second_tree_index = {{
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x1,
+ 0x2, 0x0, 0x2, 0x1, 0x0, 0xf, 0x2, 0x1, 0x2, 0x3, 0x3, 0x1, 0x1, 0x3,
+ 0x2, 0x4, 0x3, 0x4, 0x3, 0x5, 0x3, 0x3, 0x3, 0x2, 0x7, 0x2, 0x1, 0x3,
+ 0x5, 0x5, 0x2, 0x2, 0x5, 0x5, 0x5, 0x4, 0x7, 0x5, 0x7, 0x5, 0x6, 0xf,
+ 0x7, 0x7, 0x7, 0x9, 0x9, 0x4, 0xb, 0x5, 0xd, 0x7, 0xb, 0x9, 0x4, 0x4,
+ 0x9, 0x6, 0x9, 0x9, 0xf, 0xb, 0x6, 0xb, 0xb, 0xd, 0xf, 0xd, 0x6, 0x4,
+ 0x4, 0x9, 0x8, 0xf, 0x8, 0xd, 0xf, 0xb, 0x8, 0xb, 0x2, 0x4, 0x7, 0xd,
+ 0x8, 0x6, 0xd, 0xf, 0x3, 0xa, 0x7, 0xa, 0xa, 0x8, 0x6, 0xc, 0x6, 0xc,
+ 0xc, 0xa, 0xf, 0xd, 0xe, 0x5, 0x9, 0x8, 0xa, 0xe, 0x9, 0xe, 0xc, 0xc,
+ 0x7, 0x6, 0xe, 0x4, 0x6, 0xe, 0xb, 0xf, 0xd, 0xa, 0x8, 0xb, 0x9, 0xb,
+ 0x8, 0xa, 0x6, 0xe, 0xc, 0xf, 0xd, 0xc, 0x8, 0xa, 0xd, 0xe, 0xf, 0xc,
+ 0x8, 0xa, 0xa, 0xc, 0xe, 0xc, 0xe, 0xe, 0xf, 0xf},
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2, 0x0,
+ 0x2, 0x0, 0x2, 0x1, 0x3, 0x3, 0x0, 0x1, 0xf, 0x2, 0x3, 0x4, 0x4, 0x1,
+ 0x1, 0x2, 0x1, 0x5, 0x3, 0x4, 0x5, 0x2, 0x3, 0x6, 0x3, 0x7, 0x6, 0x3,
+ 0x5, 0x2, 0x3, 0x1, 0x3, 0x8, 0x2, 0x9, 0x7, 0x5, 0x4, 0x2, 0x7, 0x5,
+ 0xa, 0xb, 0x4, 0x6, 0x5, 0x5, 0xd, 0xf, 0x5, 0xe, 0xf, 0xb, 0x4, 0x4,
+ 0x6, 0x6, 0xf, 0x5, 0x4, 0xa, 0x2, 0x4, 0x7, 0x9, 0x3, 0x7, 0x7, 0x8,
+ 0x6, 0xc, 0x7, 0xb, 0x9, 0xd, 0x8, 0x8, 0xc, 0xf, 0x9, 0xb, 0xc, 0xf,
+ 0x8, 0x9, 0xb, 0x7, 0xb, 0xd, 0xd, 0x8, 0xa, 0x7, 0x4, 0x8, 0x8, 0xe,
+ 0xf, 0xa, 0xc, 0x5, 0x9, 0xa, 0xc, 0x9, 0xc, 0x6, 0xb, 0xc, 0xe, 0xe,
+ 0xe, 0xe, 0xa, 0xa, 0xe, 0xc, 0x6, 0x6, 0x9, 0xa, 0xd, 0xd, 0xe, 0xf,
+ 0x8, 0x9, 0xd, 0x7, 0xc, 0x6, 0xe, 0x9, 0xa, 0xc, 0xd, 0xe, 0xf, 0xf,
+ 0xa, 0xb, 0xb, 0xf, 0xd, 0x8, 0xb, 0xd, 0xf, 0xf},
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x0, 0x1, 0x1, 0xf, 0x0, 0x2, 0x2, 0x1, 0x2, 0x1, 0x2, 0x3, 0x3, 0x2,
+ 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x7, 0x5, 0x5, 0x5, 0x2, 0x5, 0x7,
+ 0x2, 0x4, 0x2, 0x7, 0x2, 0x4, 0x7, 0x9, 0x1, 0x5, 0x9, 0x9, 0xf, 0x4,
+ 0x5, 0x9, 0x8, 0x4, 0xb, 0x4, 0xf, 0x7, 0x6, 0xb, 0x6, 0x6, 0xb, 0xb,
+ 0xd, 0x5, 0xd, 0x8, 0xb, 0x7, 0x5, 0x4, 0xd, 0xf, 0x3, 0xd, 0x8, 0x4,
+ 0x7, 0x9, 0xd, 0xf, 0x8, 0xb, 0xa, 0x9, 0x5, 0x2, 0xa, 0x4, 0xf, 0xf,
+ 0xd, 0x6, 0xa, 0x5, 0x4, 0x8, 0xc, 0xe, 0xc, 0xe, 0x9, 0x6, 0x7, 0x4,
+ 0x6, 0xc, 0xf, 0xc, 0x6, 0xe, 0x7, 0xe, 0x7, 0x9, 0x9, 0xa, 0xd, 0x9,
+ 0x8, 0x6, 0xf, 0xc, 0xb, 0xa, 0x8, 0xb, 0x6, 0xa, 0xb, 0xd, 0x8, 0xe,
+ 0xd, 0xa, 0xc, 0xc, 0xf, 0xb, 0xe, 0xd, 0x8, 0x6, 0xe, 0xc, 0xe, 0x8,
+ 0xf, 0xa, 0xc, 0xa, 0xc, 0xe, 0xa, 0xe, 0xf, 0xf},
+ }};
+
+ array<array<HuffmanTable, 2>, 2> mHuff = {{
+ {{makeDecoder(first_tree_ncpl[table].data(),
+ first_tree_len[table].data()),
+ makeDecoder(first_tree_ncpl[table].data(),
+ first_tree_index[table].data())}},
+ {{makeDecoder(second_tree_ncpl[table].data(),
+ second_tree_len[table].data()),
+ makeDecoder(second_tree_ncpl[table].data(),
+ second_tree_index[table].data())}},
+ }};
+
+ return mHuff;
+}
+
+// FIXME: this function is horrible.
+inline void CrwDecompressor::decodeBlock(std::array<int, 64>* diffBuf,
+ const crw_hts& mHuff,
+ BitPumpJPEG* lPump,
+ BitPumpJPEG* iPump) {
+ assert(diffBuf);
+ assert(lPump);
+
+ // decode the block
+ for (int i = 0; i < 64; i++) {
+ const int len = mHuff[i > 0][0].decodeLength(*lPump);
+ const int index = mHuff[i > 0][1].decodeLength(*iPump);
+ assert(len >= 0 && index >= 0);
+
+ if (len == 0 && index == 0 && i)
+ break;
+
+ if (len == 0xf && index == 0xf)
+ continue;
+
+ i += index;
+
+ if (len == 0)
+ continue;
+
+ int diff = lPump->getBits(len);
+ iPump->fill(len);
+ iPump->skipBits(len);
+
+ if (i >= 64)
+ break;
+
+ diff = HuffmanTable::signExtended(diff, len);
+
+ (*diffBuf)[i] = diff;
+ }
+}
+
+// FIXME: this function is horrible.
+void CrwDecompressor::decompress() {
+ const uint32 height = mRaw->dim.y;
+ const uint32 width = mRaw->dim.x;
+
+ {
+ assert(width > 0);
+ assert(width % 4 == 0);
+ assert(height > 0);
+
+ // Each block encodes 64 pixels
+
+ assert((height * width) % 64 == 0);
+ const unsigned hBlocks = height * width / 64;
+ assert(hBlocks > 0);
+
+ BitPumpJPEG lPump(rawInput);
+ BitPumpJPEG iPump(rawInput);
+
+ int carry = 0;
+ std::array<int, 2> base;
+
+ uint32 j = 0;
+ ushort16* dest = nullptr;
+ uint32 i = 0;
+
+ for (unsigned block = 0; block < hBlocks; block++) {
+ array<int, 64> diffBuf = {{}};
+ decodeBlock(&diffBuf, mHuff, &lPump, &iPump);
+
+ // predict and output the block
+
+ diffBuf[0] += carry;
+ carry = diffBuf[0];
+
+ for (uint32 k = 0; k < 64; k++) {
+ if (i % width == 0) {
+ // new line. sadly, does not always happen when k == 0.
+ i = 0;
+
+ dest = reinterpret_cast<ushort16*>(mRaw->getData(0, j));
+
+ j++;
+ base[0] = base[1] = 512;
+ }
+
+ assert(dest != nullptr);
+ base[k & 1] += diffBuf[k];
+
+ if (base[k & 1] >> 10)
+ ThrowRDE("Error decompressing");
+
+ *dest = base[k & 1];
+
+ i++;
+ dest++;
+ }
+ }
+ assert(j == height);
+ assert(i == width);
+ }
+
+ // Add the uncompressed 2 low bits to the decoded 8 high bits
+ if (lowbits) {
+ assert(width > 0);
+ assert(width % 4 == 0);
+ assert(height > 0);
+
+ for (uint32 j = 0; j < height; j++) {
+ auto* dest = reinterpret_cast<ushort16*>(mRaw->getData(0, j));
+
+ assert(width % 4 == 0);
+ for (uint32 i = 0; i < width; /* NOTE: i += 4 */) {
+ const uchar8 c = lowbitInput.getByte();
+ // LSB-packed: p3 << 6 | p2 << 4 | p1 << 2 | p0 << 0
+
+ // We have read 8 bits, which is 4 pairs of 2 bits. So process 4 pixels.
+ for (uint32 p = 0; p < 4; p++) {
+ ushort16 low = (c >> (2 * p)) & 0b11;
+ ushort16 val = (*dest << 2) | low;
+
+ if (width == 2672 && val < 512)
+ val += 2; // No idea why this is needed
+
+ *dest = val;
+ i++;
+ dest++;
+ }
+ }
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.h
new file mode 100644
index 00000000..2f10ff9e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/CrwDecompressor.h
@@ -0,0 +1,60 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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/Common.h" // for uchar8, uint32
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpJPEG.h" // for BitPumpJPEG
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array
+
+namespace rawspeed {
+
+class CrwDecompressor final : public AbstractDecompressor {
+ using crw_hts = std::array<std::array<HuffmanTable, 2>, 2>;
+
+ RawImage mRaw;
+ crw_hts mHuff;
+ const bool lowbits;
+
+ ByteStream lowbitInput;
+ ByteStream rawInput;
+
+public:
+ CrwDecompressor(const RawImage& img, uint32 dec_table_, bool lowbits_,
+ ByteStream rawData);
+
+ void decompress();
+
+private:
+ static HuffmanTable makeDecoder(const uchar8* ncpl, const uchar8* values);
+ static crw_hts initHuffTables(uint32 table);
+
+ inline static void decodeBlock(std::array<int, 64>* diffBuf,
+ const crw_hts& mHuff, BitPumpJPEG* lPump,
+ BitPumpJPEG* iPump);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.cpp
new file mode 100644
index 00000000..1fa1ee75
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.cpp
@@ -0,0 +1,257 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Vasily Khoruzhick
+
+ 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 "rawspeedconfig.h"
+
+#ifdef HAVE_ZLIB
+
+#include "decompressors/DeflateDecompressor.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/Endianness.h" // for getHostEndianness, Endiann...
+#include <cassert> // for assert
+#include <cstdio> // for size_t
+#include <zlib.h>
+
+namespace rawspeed {
+
+// decodeFPDeltaRow(): MIT License, copyright 2014 Javier Celaya
+// <jcelaya gmail com>
+static inline void decodeFPDeltaRow(unsigned char* src, unsigned char* dst,
+ size_t tileWidth, size_t realTileWidth,
+ unsigned int bytesps, int factor) {
+ // DecodeDeltaBytes
+ for (size_t col = factor; col < realTileWidth * bytesps; ++col) {
+ // Yes, this is correct, and is symmetrical with EncodeDeltaBytes in
+ // hdrmerge, and they both combined are lossless.
+ // This is indeed working in modulo-2^n arighmetics.
+ src[col] = static_cast<unsigned char>(src[col] + src[col - factor]);
+ }
+ // Reorder bytes into the image
+ // 16 and 32-bit versions depend on local architecture, 24-bit does not
+ if (bytesps == 3) {
+ for (size_t col = 0; col < tileWidth; ++col) {
+ dst[col * 3] = src[col];
+ dst[col * 3 + 1] = src[col + realTileWidth];
+ dst[col * 3 + 2] = src[col + realTileWidth * 2];
+ }
+ } else {
+ if (getHostEndianness() == Endianness::little) {
+ for (size_t col = 0; col < tileWidth; ++col) {
+ for (size_t byte = 0; byte < bytesps; ++byte)
+ dst[col * bytesps + byte] =
+ src[col + realTileWidth * (bytesps - byte - 1)];
+ }
+ } else {
+ for (size_t col = 0; col < tileWidth; ++col) {
+ for (size_t byte = 0; byte < bytesps; ++byte)
+ dst[col * bytesps + byte] = src[col + realTileWidth * byte];
+ }
+ }
+ }
+}
+
+static inline uint32 __attribute__((const)) fp16ToFloat(ushort16 fp16) {
+ // IEEE-754-2008: binary16:
+ // bit 15 - sign
+ // bits 14-10 - exponent (5 bit)
+ // bits 9-0 - fraction (10 bit)
+ //
+ // exp = 0, fract = +-0: zero
+ // exp = 0; fract != 0: subnormal numbers
+ // equation: -1 ^ sign * 2 ^ -14 * 0.fraction
+ // exp = 1..30: normalized value
+ // equation: -1 ^ sign * 2 ^ (exponent - 15) * 1.fraction
+ // exp = 31, fract = +-0: +-infinity
+ // exp = 31, fract != 0: NaN
+
+ uint32 sign = (fp16 >> 15) & 1;
+ uint32 fp16_exponent = (fp16 >> 10) & ((1 << 5) - 1);
+ uint32 fp16_fraction = fp16 & ((1 << 10) - 1);
+
+ // Normalized or zero
+ // binary32 equation: -1 ^ sign * 2 ^ (exponent - 127) * 1.fraction
+ // => exponent32 - 127 = exponent16 - 15, exponent32 = exponent16 + 127 - 15
+ uint32 fp32_exponent = fp16_exponent + 127 - 15;
+ uint32 fp32_fraction = fp16_fraction
+ << (23 - 10); // 23 is binary32 fraction size
+
+ if (fp16_exponent == 31) {
+ // Infinity or NaN
+ fp32_exponent = 255;
+ } else if (fp16_exponent == 0) {
+ if (fp16_fraction == 0) {
+ // +-Zero
+ fp32_exponent = 0;
+ fp32_fraction = 0;
+ } else {
+ // Subnormal numbers
+ // binary32 equation: -1 ^ sign * 2 ^ (exponent - 127) * 1.fraction
+ // binary16 equation: -1 ^ sign * 2 ^ -14 * 0.fraction, we can represent
+ // it as a normalized value in binary32, we have to shift fraction until
+ // we get 1.new_fraction and decrement exponent for each shift
+ fp32_exponent = -14 + 127;
+ while (!(fp32_fraction & (1 << 23))) {
+ fp32_exponent -= 1;
+ fp32_fraction <<= 1;
+ }
+ fp32_fraction &= ((1 << 23) - 1);
+ }
+ }
+ return (sign << 31) | (fp32_exponent << 23) | fp32_fraction;
+}
+
+static inline uint32 __attribute__((const)) fp24ToFloat(uint32 fp24) {
+ // binary24: Not a part of IEEE754-2008, but format is obvious,
+ // see https://en.wikipedia.org/wiki/Minifloat
+ // bit 23 - sign
+ // bits 22-16 - exponent (7 bit)
+ // bits 15-0 - fraction (16 bit)
+ //
+ // exp = 0, fract = +-0: zero
+ // exp = 0; fract != 0: subnormal numbers
+ // equation: -1 ^ sign * 2 ^ -62 * 0.fraction
+ // exp = 1..126: normalized value
+ // equation: -1 ^ sign * 2 ^ (exponent - 63) * 1.fraction
+ // exp = 127, fract = +-0: +-infinity
+ // exp = 127, fract != 0: NaN
+
+ uint32 sign = (fp24 >> 23) & 1;
+ uint32 fp24_exponent = (fp24 >> 16) & ((1 << 7) - 1);
+ uint32 fp24_fraction = fp24 & ((1 << 16) - 1);
+
+ // Normalized or zero
+ // binary32 equation: -1 ^ sign * 2 ^ (exponent - 127) * 1.fraction
+ // => exponent32 - 127 = exponent24 - 64, exponent32 = exponent16 + 127 - 63
+ uint32 fp32_exponent = fp24_exponent + 127 - 63;
+ uint32 fp32_fraction = fp24_fraction
+ << (23 - 16); // 23 is binary 32 fraction size
+
+ if (fp24_exponent == 127) {
+ // Infinity or NaN
+ fp32_exponent = 255;
+ } else if (fp24_exponent == 0) {
+ if (fp24_fraction == 0) {
+ // +-Zero
+ fp32_exponent = 0;
+ fp32_fraction = 0;
+ } else {
+ // Subnormal numbers
+ // binary32 equation: -1 ^ sign * 2 ^ (exponent - 127) * 1.fraction
+ // binary24 equation: -1 ^ sign * 2 ^ -62 * 0.fraction, we can represent
+ // it as a normalized value in binary32, we have to shift fraction until
+ // we get 1.new_fraction and decrement exponent for each shift
+ fp32_exponent = -62 + 127;
+ while (!(fp32_fraction & (1 << 23))) {
+ fp32_exponent -= 1;
+ fp32_fraction <<= 1;
+ }
+ fp32_fraction &= ((1 << 23) - 1);
+ }
+ }
+ return (sign << 31) | (fp32_exponent << 23) | fp32_fraction;
+}
+
+static inline void expandFP16(unsigned char* dst, int width) {
+ auto* dst16 = reinterpret_cast<ushort16*>(dst);
+ auto* dst32 = reinterpret_cast<uint32*>(dst);
+
+ for (int x = width - 1; x >= 0; x--)
+ dst32[x] = fp16ToFloat(dst16[x]);
+}
+
+static inline void expandFP24(unsigned char* dst, int width) {
+ auto* dst32 = reinterpret_cast<uint32*>(dst);
+ dst += (width - 1) * 3;
+ for (int x = width - 1; x >= 0; x--) {
+ dst32[x] = fp24ToFloat((dst[0] << 16) | (dst[1] << 8) | dst[2]);
+ dst -= 3;
+ }
+}
+
+void DeflateDecompressor::decode(
+ std::unique_ptr<unsigned char[]>* uBuffer, // NOLINT
+ iPoint2D maxDim, iPoint2D dim, iPoint2D off) {
+ uLongf dstLen = sizeof(float) * maxDim.area();
+
+ if (!*uBuffer)
+ *uBuffer =
+ std::unique_ptr<unsigned char[]>(new unsigned char[dstLen]); // NOLINT
+
+ const auto cSize = input.getRemainSize();
+ const unsigned char* cBuffer = input.getData(cSize);
+
+ int err = uncompress(uBuffer->get(), &dstLen, cBuffer, cSize);
+ if (err != Z_OK) {
+ ThrowRDE("failed to uncompress tile: %d (%s)", err, zError(err));
+ }
+
+ int predFactor = 0;
+ switch (predictor) {
+ case 3:
+ predFactor = 1;
+ break;
+ case 34894:
+ predFactor = 2;
+ break;
+ case 34895:
+ predFactor = 4;
+ break;
+ default:
+ predFactor = 0;
+ break;
+ }
+
+ int bytesps = bps / 8;
+
+ for (auto row = 0; row < dim.y; ++row) {
+ unsigned char* src = uBuffer->get() + row * maxDim.x * bytesps;
+ unsigned char* dst =
+ static_cast<unsigned char*>(mRaw->getData()) +
+ ((off.y + row) * mRaw->pitch + off.x * sizeof(float) * mRaw->getCpp());
+
+ if (predFactor)
+ decodeFPDeltaRow(src, dst, dim.x, maxDim.x, bytesps, predFactor);
+
+ assert(bytesps >= 2 && bytesps <= 4);
+ switch (bytesps) {
+ case 2:
+ expandFP16(dst, dim.x);
+ break;
+ case 3:
+ expandFP24(dst, dim.x);
+ break;
+ case 4:
+ // No need to expand FP32
+ break;
+ default:
+ __builtin_unreachable();
+ }
+ }
+}
+
+} // namespace rawspeed
+
+#else
+
+#pragma message \
+ "ZLIB is not present! Deflate compression will not be supported!"
+
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.h
new file mode 100644
index 00000000..07e39660
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/DeflateDecompressor.h
@@ -0,0 +1,59 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 "rawspeedconfig.h" // for HAVE_ZLIB
+
+#ifdef HAVE_ZLIB
+
+#include "common/Common.h" // for uint32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include <memory> // for unique_ptr
+#include <utility> // for move
+
+namespace rawspeed {
+
+class DeflateDecompressor final : public AbstractDecompressor {
+ ByteStream input;
+ RawImage mRaw;
+ int predictor;
+ int bps;
+
+public:
+ DeflateDecompressor(ByteStream bs, const RawImage& img, int predictor_,
+ int bps_)
+ : input(std::move(bs)), mRaw(img), predictor(predictor_), bps(bps_) {}
+
+ void decode(std::unique_ptr<unsigned char[]>* uBuffer, // NOLINT
+ iPoint2D maxDim, iPoint2D dim, iPoint2D off);
+};
+
+} // namespace rawspeed
+
+#else
+
+#pragma message \
+ "ZLIB is not present! Deflate compression will not be supported!"
+
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.cpp
new file mode 100644
index 00000000..ee1e284e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.cpp
@@ -0,0 +1,823 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2016 Alexey Danilchenko
+ Copyright (C) 2016 Alex Tutubalin
+ Copyright (C) 2017 Uwe Müssel
+ Copyright (C) 2017 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.1 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 "rawspeedconfig.h"
+#include "decompressors/FujiDecompressor.h"
+#include "common/Common.h" // for ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/Endianness.h" // for Endianness
+#include "metadata/ColorFilterArray.h" // for CFA_BLUE
+#include <algorithm> // for fill, min
+#include <cmath> // for abs
+#include <cstdlib> // for abs, size_t
+#include <cstring> // for memcpy
+
+namespace rawspeed {
+
+FujiDecompressor::FujiDecompressor(const RawImage& img, ByteStream input_)
+ : mRaw(img), input(std::move(input_)) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ input.setByteOrder(Endianness::big);
+
+ header = FujiHeader(&input);
+ if (!header)
+ ThrowRDE("compressed RAF header check");
+
+ if (mRaw->dim != iPoint2D(header.raw_width, header.raw_height))
+ ThrowRDE("RAF header specifies different dimensions!");
+
+ if (12 == header.raw_bits) {
+ ThrowRDE("Aha, finally, a 12-bit compressed RAF! Please consider providing "
+ "samples on <https://raw.pixls.us/>, thanks!");
+ }
+
+ for (int i = 0; i < 6; i++) {
+ for (int j = 0; j < 6; j++) {
+ const CFAColor c = mRaw->cfa.getColorAt(j, i);
+ switch (c) {
+ case CFA_RED:
+ case CFA_GREEN:
+ case CFA_BLUE:
+ CFA[i][j] = c;
+ break;
+ default:
+ ThrowRDE("Got unexpected color %u", c);
+ }
+ }
+ }
+
+ fuji_compressed_load_raw();
+}
+
+FujiDecompressor::fuji_compressed_params::fuji_compressed_params(
+ const FujiDecompressor& d) {
+ int cur_val;
+ char* qt;
+
+ if ((d.header.block_size % 3 && d.header.raw_type == 16) ||
+ (d.header.block_size & 1 && d.header.raw_type == 0)) {
+ ThrowRDE("fuji_block_checks");
+ }
+
+ q_table.resize(32768);
+
+ if (d.header.raw_type == 16) {
+ line_width = (d.header.block_size * 2) / 3;
+ } else {
+ line_width = d.header.block_size >> 1;
+ }
+
+ q_point[0] = 0;
+ q_point[1] = 0x12;
+ q_point[2] = 0x43;
+ q_point[3] = 0x114;
+ q_point[4] = (1 << d.header.raw_bits) - 1;
+ min_value = 0x40;
+
+ cur_val = -q_point[4];
+
+ for (qt = &q_table[0]; cur_val <= q_point[4]; ++qt, ++cur_val) {
+ if (cur_val <= -q_point[3]) {
+ *qt = -4;
+ } else if (cur_val <= -q_point[2]) {
+ *qt = -3;
+ } else if (cur_val <= -q_point[1]) {
+ *qt = -2;
+ } else if (cur_val < 0) {
+ *qt = -1;
+ } else if (cur_val == 0) {
+ *qt = 0;
+ } else if (cur_val < q_point[1]) {
+ *qt = 1;
+ } else if (cur_val < q_point[2]) {
+ *qt = 2;
+ } else if (cur_val < q_point[3]) {
+ *qt = 3;
+ } else {
+ *qt = 4;
+ }
+ }
+
+ // populting gradients
+ if (q_point[4] == 0x3FFF) {
+ total_values = 0x4000;
+ raw_bits = 14;
+ max_bits = 56;
+ maxDiff = 256;
+ } else if (q_point[4] == 0xFFF) {
+ ThrowRDE("Aha, finally, a 12-bit compressed RAF! Please consider providing "
+ "samples on <https://raw.pixls.us/>, thanks!");
+
+ /* kept for future, once there is a sample.
+ total_values = 4096;
+ raw_bits = 12;
+ max_bits = 48;
+ maxDiff = 64;
+ */
+ } else {
+ ThrowRDE("FUJI q_point");
+ }
+}
+
+void FujiDecompressor::fuji_compressed_block::reset(
+ const fuji_compressed_params* params) {
+ const bool reInit = !linealloc.empty();
+
+ linealloc.resize(_ltotal * (params->line_width + 2), 0);
+
+ if (reInit)
+ std::fill(linealloc.begin(), linealloc.end(), 0);
+
+ linebuf[_R0] = &linealloc[0];
+
+ for (int i = _R1; i <= _B4; i++) {
+ linebuf[i] = linebuf[i - 1] + params->line_width + 2;
+ }
+
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 41; i++) {
+ grad_even[j][i].value1 = params->maxDiff;
+ grad_even[j][i].value2 = 1;
+ grad_odd[j][i].value1 = params->maxDiff;
+ grad_odd[j][i].value2 = 1;
+ }
+ }
+}
+
+template <typename T>
+void FujiDecompressor::copy_line(fuji_compressed_block* info,
+ const FujiStrip& strip, int cur_line,
+ T&& idx) const {
+ std::array<ushort16*, 3> lineBufB;
+ std::array<ushort16*, 6> lineBufG;
+ std::array<ushort16*, 3> lineBufR;
+
+ for (int i = 0; i < 3; i++) {
+ lineBufR[i] = info->linebuf[_R2 + i] + 1;
+ lineBufB[i] = info->linebuf[_B2 + i] + 1;
+ }
+
+ for (int i = 0; i < 6; i++) {
+ lineBufG[i] = info->linebuf[_G2 + i] + 1;
+ }
+
+ for (int row_count = 0; row_count < FujiStrip::lineHeight(); row_count++) {
+ auto* const raw_block_data = reinterpret_cast<ushort16*>(
+ mRaw->getData(strip.offsetX(), strip.offsetY(cur_line) + row_count));
+
+ for (int pixel_count = 0; pixel_count < strip.width(); pixel_count++) {
+ ushort16* line_buf = nullptr;
+
+ switch (CFA[row_count][pixel_count % 6]) {
+ case CFA_RED: // red
+ line_buf = lineBufR[row_count >> 1];
+ break;
+
+ case CFA_GREEN: // green
+ line_buf = lineBufG[row_count];
+ break;
+
+ case CFA_BLUE: // blue
+ line_buf = lineBufB[row_count >> 1];
+ break;
+
+ default:
+ __builtin_unreachable();
+ }
+
+ raw_block_data[pixel_count] = line_buf[idx(pixel_count)];
+ }
+ }
+}
+
+void FujiDecompressor::copy_line_to_xtrans(fuji_compressed_block* info,
+ const FujiStrip& strip,
+ int cur_line) const {
+ auto index = [](int pixel_count) {
+ return (((pixel_count * 2 / 3) & 0x7FFFFFFE) | ((pixel_count % 3) & 1)) +
+ ((pixel_count % 3) >> 1);
+ };
+
+ copy_line(info, strip, cur_line, index);
+}
+
+void FujiDecompressor::copy_line_to_bayer(fuji_compressed_block* info,
+ const FujiStrip& strip,
+ int cur_line) const {
+ auto index = [](int pixel_count) { return pixel_count >> 1; };
+
+ copy_line(info, strip, cur_line, index);
+}
+
+inline void FujiDecompressor::fuji_zerobits(BitPumpMSB* pump,
+ int* count) const {
+ uchar8 zero = 0;
+ *count = 0;
+
+ while (zero == 0) {
+ zero = pump->getBits(1);
+
+ if (zero)
+ break;
+
+ ++*count;
+ }
+}
+
+int __attribute__((const))
+FujiDecompressor::bitDiff(int value1, int value2) const {
+ int decBits = 0;
+
+ if (value2 >= value1)
+ return decBits;
+
+ while (decBits <= 12) {
+ ++decBits;
+
+ if ((value2 << decBits) >= value1)
+ return decBits;
+ }
+
+ return decBits;
+}
+
+template <typename T1, typename T2>
+void FujiDecompressor::fuji_decode_sample(
+ T1&& func_0, T2&& func_1, fuji_compressed_block* info, ushort16* line_buf,
+ int* pos, std::array<int_pair, 41>* grads) const {
+ int interp_val = 0;
+
+ int sample = 0;
+ int code = 0;
+ ushort16* line_buf_cur = line_buf + *pos;
+
+ int grad;
+ int gradient;
+
+ func_0(line_buf_cur, &interp_val, &grad, &gradient);
+
+ fuji_zerobits(&(info->pump), &sample);
+
+ if (sample < common_info.max_bits - common_info.raw_bits - 1) {
+ int decBits = bitDiff((*grads)[gradient].value1, (*grads)[gradient].value2);
+ code = info->pump.getBits(decBits);
+ code += sample << decBits;
+ } else {
+ code = info->pump.getBits(common_info.raw_bits);
+ code++;
+ }
+
+ if (code < 0 || code >= common_info.total_values) {
+ ThrowRDE("fuji_decode_sample");
+ }
+
+ if (code & 1) {
+ code = -1 - code / 2;
+ } else {
+ code /= 2;
+ }
+
+ (*grads)[gradient].value1 += std::abs(code);
+
+ if ((*grads)[gradient].value2 == common_info.min_value) {
+ (*grads)[gradient].value1 >>= 1;
+ (*grads)[gradient].value2 >>= 1;
+ }
+
+ (*grads)[gradient].value2++;
+
+ interp_val = func_1(grad, interp_val, code);
+
+ if (interp_val < 0) {
+ interp_val += common_info.total_values;
+ } else if (interp_val > common_info.q_point[4]) {
+ interp_val -= common_info.total_values;
+ }
+
+ if (interp_val >= 0) {
+ line_buf_cur[0] = std::min(interp_val, common_info.q_point[4]);
+ } else {
+ line_buf_cur[0] = 0;
+ }
+
+ *pos += 2;
+}
+
+#define fuji_quant_gradient(v1, v2) \
+ (9 * ci.q_table[ci.q_point[4] + (v1)] + ci.q_table[ci.q_point[4] + (v2)])
+
+void FujiDecompressor::fuji_decode_sample_even(
+ fuji_compressed_block* info, ushort16* line_buf, int* pos,
+ std::array<int_pair, 41>* grads) const {
+ const auto& ci = common_info;
+ fuji_decode_sample(
+ [&ci](const ushort16* line_buf_cur, int* interp_val, int* grad,
+ int* gradient) {
+ int Rb = line_buf_cur[-2 - ci.line_width];
+ int Rc = line_buf_cur[-3 - ci.line_width];
+ int Rd = line_buf_cur[-1 - ci.line_width];
+ int Rf = line_buf_cur[-4 - 2 * ci.line_width];
+
+ int diffRcRb;
+ int diffRfRb;
+ int diffRdRb;
+
+ *grad = fuji_quant_gradient(Rb - Rf, Rc - Rb);
+ *gradient = std::abs(*grad);
+ diffRcRb = std::abs(Rc - Rb);
+ diffRfRb = std::abs(Rf - Rb);
+ diffRdRb = std::abs(Rd - Rb);
+
+ if (diffRcRb > diffRfRb && diffRcRb > diffRdRb) {
+ *interp_val = Rf + Rd + 2 * Rb;
+ } else if (diffRdRb > diffRcRb && diffRdRb > diffRfRb) {
+ *interp_val = Rf + Rc + 2 * Rb;
+ } else {
+ *interp_val = Rd + Rc + 2 * Rb;
+ }
+ },
+ [](int grad, int interp_val, int code) {
+ if (grad < 0) {
+ interp_val = (interp_val >> 2) - code;
+ } else {
+ interp_val = (interp_val >> 2) + code;
+ }
+
+ return interp_val;
+ },
+ info, line_buf, pos, grads);
+}
+
+void FujiDecompressor::fuji_decode_sample_odd(
+ fuji_compressed_block* info, ushort16* line_buf, int* pos,
+ std::array<int_pair, 41>* grads) const {
+ const auto& ci = common_info;
+ fuji_decode_sample(
+ [&ci](const ushort16* line_buf_cur, int* interp_val, int* grad,
+ int* gradient) {
+ int Ra = line_buf_cur[-1];
+ int Rb = line_buf_cur[-2 - ci.line_width];
+ int Rc = line_buf_cur[-3 - ci.line_width];
+ int Rd = line_buf_cur[-1 - ci.line_width];
+ int Rg = line_buf_cur[1];
+
+ *grad = fuji_quant_gradient(Rb - Rc, Rc - Ra);
+ *gradient = std::abs(*grad);
+
+ if ((Rb > Rc && Rb > Rd) || (Rb < Rc && Rb < Rd)) {
+ *interp_val = (Rg + Ra + 2 * Rb) >> 2;
+ } else {
+ *interp_val = (Ra + Rg) >> 1;
+ }
+ },
+ [](int grad, int interp_val, int code) {
+ if (grad < 0) {
+ interp_val -= code;
+ } else {
+ interp_val += code;
+ }
+
+ return interp_val;
+ },
+ info, line_buf, pos, grads);
+}
+
+#undef fuji_quant_gradient
+
+void FujiDecompressor::fuji_decode_interpolation_even(int line_width,
+ ushort16* line_buf,
+ int* pos) const {
+ ushort16* line_buf_cur = line_buf + *pos;
+ int Rb = line_buf_cur[-2 - line_width];
+ int Rc = line_buf_cur[-3 - line_width];
+ int Rd = line_buf_cur[-1 - line_width];
+ int Rf = line_buf_cur[-4 - 2 * line_width];
+ int diffRcRb = std::abs(Rc - Rb);
+ int diffRfRb = std::abs(Rf - Rb);
+ int diffRdRb = std::abs(Rd - Rb);
+
+ if (diffRcRb > diffRfRb && diffRcRb > diffRdRb) {
+ *line_buf_cur = (Rf + Rd + 2 * Rb) >> 2;
+ } else if (diffRdRb > diffRcRb && diffRdRb > diffRfRb) {
+ *line_buf_cur = (Rf + Rc + 2 * Rb) >> 2;
+ } else {
+ *line_buf_cur = (Rd + Rc + 2 * Rb) >> 2;
+ }
+
+ *pos += 2;
+}
+
+void FujiDecompressor::fuji_extend_generic(
+ std::array<ushort16*, _ltotal> linebuf, int line_width, int start,
+ int end) const {
+ for (int i = start; i <= end; i++) {
+ linebuf[i][0] = linebuf[i - 1][1];
+ linebuf[i][line_width + 1] = linebuf[i - 1][line_width];
+ }
+}
+
+void FujiDecompressor::fuji_extend_red(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const {
+ fuji_extend_generic(linebuf, line_width, _R2, _R4);
+}
+
+void FujiDecompressor::fuji_extend_green(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const {
+ fuji_extend_generic(linebuf, line_width, _G2, _G7);
+}
+
+void FujiDecompressor::fuji_extend_blue(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const {
+ fuji_extend_generic(linebuf, line_width, _B2, _B4);
+}
+
+void FujiDecompressor::xtrans_decode_block(fuji_compressed_block* info,
+ int cur_line) const {
+ struct ColorPos {
+ int even = 0;
+ int odd = 1;
+
+ void reset() {
+ even = 0;
+ odd = 1;
+ }
+ };
+
+ ColorPos r;
+ ColorPos g;
+ ColorPos b;
+
+ const int line_width = common_info.line_width;
+
+ // FIXME: GCC5 sucks.
+ // https://github.com/darktable-org/rawspeed/issues/112
+ // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=871250
+ // https://bugs.launchpad.net/linuxmint/+bug/1709234
+ auto pass = [&, line_width](auto&& even_func, _xt_lines c0, _xt_lines c1,
+ int grad, ColorPos& c0_pos, ColorPos& c1_pos) {
+ while (g.even < line_width || g.odd < line_width) {
+ if (g.even < line_width)
+ even_func(c0, c1, grad, c0_pos, c1_pos);
+
+ if (g.even > 8) {
+ fuji_decode_sample_odd(info, info->linebuf[c0] + 1, &c0_pos.odd,
+ &(info->grad_odd[grad]));
+ fuji_decode_sample_odd(info, info->linebuf[c1] + 1, &c1_pos.odd,
+ &(info->grad_odd[grad]));
+ }
+ }
+ };
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c0] + 1,
+ &c0_pos.even);
+ fuji_decode_sample_even(info, info->linebuf[c1] + 1, &c1_pos.even,
+ &(info->grad_even[grad]));
+ },
+ _R2, _G2, 0, r, g);
+
+ fuji_extend_red(info->linebuf, line_width);
+ fuji_extend_green(info->linebuf, line_width);
+
+ g.reset();
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ fuji_decode_sample_even(info, info->linebuf[c0] + 1, &c0_pos.even,
+ &(info->grad_even[grad]));
+ fuji_decode_interpolation_even(line_width, info->linebuf[c1] + 1,
+ &c1_pos.even);
+ },
+ _G3, _B2, 1, g, b);
+
+ fuji_extend_green(info->linebuf, line_width);
+ fuji_extend_blue(info->linebuf, line_width);
+
+ r.reset();
+ g.reset();
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ if (c0_pos.even & 3) {
+ fuji_decode_sample_even(info, info->linebuf[c0] + 1, &c0_pos.even,
+ &(info->grad_even[grad]));
+ } else {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c0] + 1,
+ &c0_pos.even);
+ }
+
+ fuji_decode_interpolation_even(line_width, info->linebuf[c1] + 1,
+ &c1_pos.even);
+ },
+ _R3, _G4, 2, r, g);
+
+ fuji_extend_red(info->linebuf, line_width);
+ fuji_extend_green(info->linebuf, line_width);
+
+ g.reset();
+ b.reset();
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ fuji_decode_sample_even(info, info->linebuf[c0] + 1, &c0_pos.even,
+ &(info->grad_even[grad]));
+
+ if ((c1_pos.even & 3) == 2) {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c1] + 1,
+ &c1_pos.even);
+ } else {
+ fuji_decode_sample_even(info, info->linebuf[c1] + 1, &c1_pos.even,
+ &(info->grad_even[grad]));
+ }
+ },
+ _G5, _B3, 0, g, b);
+
+ fuji_extend_green(info->linebuf, line_width);
+ fuji_extend_blue(info->linebuf, line_width);
+
+ r.reset();
+ g.reset();
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ if ((c0_pos.even & 3) == 2) {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c0] + 1,
+ &c0_pos.even);
+ } else {
+ fuji_decode_sample_even(info, info->linebuf[c0] + 1, &c0_pos.even,
+ &(info->grad_even[grad]));
+ }
+
+ fuji_decode_sample_even(info, info->linebuf[c1] + 1, &c1_pos.even,
+ &(info->grad_even[grad]));
+ },
+ _R4, _G6, 1, r, g);
+
+ fuji_extend_red(info->linebuf, line_width);
+ fuji_extend_green(info->linebuf, line_width);
+
+ g.reset();
+ b.reset();
+
+ pass(
+ [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c0] + 1,
+ &c0_pos.even);
+
+ if (c1_pos.even & 3) {
+ fuji_decode_sample_even(info, info->linebuf[c1] + 1, &c1_pos.even,
+ &(info->grad_even[grad]));
+ } else {
+ fuji_decode_interpolation_even(line_width, info->linebuf[c1] + 1,
+ &c1_pos.even);
+ }
+ },
+ _G7, _B4, 2, g, b);
+
+ fuji_extend_green(info->linebuf, line_width);
+ fuji_extend_blue(info->linebuf, line_width);
+}
+
+void FujiDecompressor::fuji_bayer_decode_block(fuji_compressed_block* info,
+ int cur_line) const {
+ struct ColorPos {
+ int even = 0;
+ int odd = 1;
+
+ void reset() {
+ even = 0;
+ odd = 1;
+ }
+ };
+
+ ColorPos r;
+ ColorPos g;
+ ColorPos b;
+
+ const int line_width = common_info.line_width;
+
+ auto pass = [&](_xt_lines c0, _xt_lines c1, int grad, ColorPos& c0_pos,
+ ColorPos& c1_pos) {
+ while (g.even < line_width || g.odd < line_width) {
+ if (g.even < line_width) {
+ fuji_decode_sample_even(info, info->linebuf[c0] + 1, &c0_pos.even,
+ &(info->grad_even[grad]));
+ fuji_decode_sample_even(info, info->linebuf[c1] + 1, &c1_pos.even,
+ &(info->grad_even[grad]));
+ }
+
+ if (g.even > 8) {
+ fuji_decode_sample_odd(info, info->linebuf[c0] + 1, &c0_pos.odd,
+ &(info->grad_odd[grad]));
+ fuji_decode_sample_odd(info, info->linebuf[c1] + 1, &c1_pos.odd,
+ &(info->grad_odd[grad]));
+ }
+ }
+ };
+
+ auto pass_RG = [&](_xt_lines c0, _xt_lines c1, int grad) {
+ pass(c0, c1, grad, r, g);
+
+ fuji_extend_red(info->linebuf, line_width);
+ fuji_extend_green(info->linebuf, line_width);
+ };
+
+ auto pass_GB = [&](_xt_lines c0, _xt_lines c1, int grad) {
+ pass(c0, c1, grad, g, b);
+
+ fuji_extend_green(info->linebuf, line_width);
+ fuji_extend_blue(info->linebuf, line_width);
+ };
+
+ pass_RG(_R2, _G2, 0);
+
+ g.reset();
+
+ pass_GB(_G3, _B2, 1);
+
+ r.reset();
+ g.reset();
+
+ pass_RG(_R3, _G4, 2);
+
+ g.reset();
+ b.reset();
+
+ pass_GB(_G5, _B3, 0);
+
+ r.reset();
+ g.reset();
+
+ pass_RG(_R4, _G6, 1);
+
+ g.reset();
+ b.reset();
+
+ pass_GB(_G7, _B4, 2);
+}
+
+void FujiDecompressor::fuji_decode_strip(
+ fuji_compressed_block* info_block, const FujiStrip& strip) const {
+ BitPumpMSB pump(strip.bs);
+
+ const unsigned line_size = sizeof(ushort16) * (common_info.line_width + 2);
+
+ struct i_pair {
+ int a;
+ int b;
+ };
+
+ const std::array<i_pair, 6> mtable = {
+ {{_R0, _R3}, {_R1, _R4}, {_G0, _G6}, {_G1, _G7}, {_B0, _B3}, {_B1, _B4}}};
+ const std::array<i_pair, 3> ztable = {{{_R2, 3}, {_G2, 6}, {_B2, 3}}};
+
+ for (int cur_line = 0; cur_line < strip.height(); cur_line++) {
+ if (header.raw_type == 16) {
+ xtrans_decode_block(info_block, cur_line);
+ } else {
+ fuji_bayer_decode_block(info_block, cur_line);
+ }
+
+ // copy data from line buffers and advance
+ for (auto i : mtable) {
+ memcpy(info_block->linebuf[i.a], info_block->linebuf[i.b], line_size);
+ }
+
+ if (header.raw_type == 16) {
+ copy_line_to_xtrans(info_block, strip, cur_line);
+ } else {
+ copy_line_to_bayer(info_block, strip, cur_line);
+ }
+
+ for (auto i : ztable) {
+ memset(info_block->linebuf[i.a], 0, i.b * line_size);
+ info_block->linebuf[i.a][0] = info_block->linebuf[i.a - 1][1];
+ info_block->linebuf[i.a][common_info.line_width + 1] =
+ info_block->linebuf[i.a - 1][common_info.line_width];
+ }
+ }
+}
+
+void FujiDecompressor::fuji_compressed_load_raw() {
+ common_info = fuji_compressed_params(*this);
+
+ // read block sizes
+ std::vector<uint32> block_sizes;
+ block_sizes.resize(header.blocks_in_row);
+ for (auto& block_size : block_sizes)
+ block_size = input.getU32();
+
+ // some padding?
+ const uint64 raw_offset = sizeof(uint32) * header.blocks_in_row;
+ if (raw_offset & 0xC) {
+ const int padding = 0x10 - (raw_offset & 0xC);
+ input.skipBytes(padding);
+ }
+
+ // calculating raw block offsets
+ strips.reserve(header.blocks_in_row);
+
+ int block = 0;
+ for (const auto& block_size : block_sizes) {
+ strips.emplace_back(header, block, input.getStream(block_size));
+ block++;
+ }
+}
+
+void FujiDecompressor::decompressThread() const noexcept {
+ fuji_compressed_block block_info;
+
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto strip = strips.cbegin(); strip < strips.cend(); ++strip) {
+ block_info.reset(&common_info);
+ block_info.pump = BitPumpMSB(strip->bs);
+ try {
+ fuji_decode_strip(&block_info, *strip);
+ } catch (RawspeedException& err) {
+ // Propagate the exception out of OpenMP magic.
+ mRaw->setError(err.what());
+ }
+ }
+}
+
+void FujiDecompressor::decompress() const {
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) \
+ num_threads(rawspeed_get_number_of_processor_cores())
+#endif
+ decompressThread();
+
+ std::string firstErr;
+ if (mRaw->isTooManyErrors(1, &firstErr)) {
+ ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
+ firstErr.c_str());
+ }
+}
+
+FujiDecompressor::FujiHeader::FujiHeader(ByteStream* bs) {
+ signature = bs->getU16();
+ version = bs->getByte();
+ raw_type = bs->getByte();
+ raw_bits = bs->getByte();
+ raw_height = bs->getU16();
+ raw_rounded_width = bs->getU16();
+ raw_width = bs->getU16();
+ block_size = bs->getU16();
+ blocks_in_row = bs->getByte();
+ total_lines = bs->getU16();
+}
+
+FujiDecompressor::FujiHeader::operator bool() const {
+ // general validation
+ const bool invalid =
+ (signature != 0x4953 || version != 1 || raw_height > 0x3000 ||
+ raw_height < FujiStrip::lineHeight() ||
+ raw_height % FujiStrip::lineHeight() || raw_width > 0x3000 ||
+ raw_width < 0x300 || raw_width % 24 || raw_rounded_width > 0x3000 ||
+ block_size != 0x300 || raw_rounded_width < block_size ||
+ raw_rounded_width % block_size ||
+ raw_rounded_width - raw_width >= block_size || blocks_in_row > 0x10 ||
+ blocks_in_row == 0 || blocks_in_row != raw_rounded_width / block_size ||
+ blocks_in_row != roundUpDivision(raw_width, block_size) ||
+ total_lines > 0x800 || total_lines == 0 ||
+ total_lines != raw_height / FujiStrip::lineHeight() ||
+ (raw_bits != 12 && raw_bits != 14) || (raw_type != 16 && raw_type != 0));
+
+ return !invalid;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.h
new file mode 100644
index 00000000..48c87eab
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/FujiDecompressor.h
@@ -0,0 +1,219 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Uwe Müssel
+ Copyright (C) 2017 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.1 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 ushort16
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include "io/ByteStream.h" // for ByteStream
+#include "metadata/ColorFilterArray.h" // for CFAColor
+#include <array> // for array
+#include <cassert> // for assert
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+class FujiDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+
+ void decompressThread() const noexcept;
+
+public:
+ struct FujiHeader {
+ FujiHeader() = default;
+
+ explicit FujiHeader(ByteStream* input_);
+ explicit __attribute__((pure)) operator bool() const; // validity check
+
+ ushort16 signature;
+ uchar8 version;
+ uchar8 raw_type;
+ uchar8 raw_bits;
+ ushort16 raw_height;
+ ushort16 raw_rounded_width;
+ ushort16 raw_width;
+ ushort16 block_size;
+ uchar8 blocks_in_row;
+ ushort16 total_lines;
+ };
+
+ FujiHeader header;
+
+ struct FujiStrip {
+ // part of which 'image' this block is
+ const FujiHeader& h;
+
+ // which strip is this, 0 .. h.blocks_in_row-1
+ const int n;
+
+ // the compressed data of this strip
+ const ByteStream bs;
+
+ FujiStrip(const FujiHeader& h_, int block, ByteStream bs_)
+ : h(h_), n(block), bs(std::move(bs_)) {
+ assert(n >= 0 && n < h.blocks_in_row);
+ }
+
+ // each strip's line corresponds to 6 output lines.
+ static int lineHeight() { return 6; }
+
+ // how many vertical lines does this block encode?
+ int height() const { return h.total_lines; }
+
+ // how many horizontal pixels does this block encode?
+ int width() const {
+ // if this is not the last block, we are good.
+ if ((n + 1) != h.blocks_in_row)
+ return h.block_size;
+
+ // ok, this is the last block...
+
+ assert(h.block_size * h.blocks_in_row >= h.raw_width);
+ return h.raw_width - offsetX();
+ }
+
+ // where vertically does this block start?
+ int offsetY(int line = 0) const {
+ assert(line >= 0 && line < height());
+ return lineHeight() * line;
+ }
+
+ // where horizontally does this block start?
+ int offsetX() const { return h.block_size * n; }
+ };
+
+ FujiDecompressor(const RawImage& img, ByteStream input);
+
+ void fuji_compressed_load_raw();
+
+ void decompress() const;
+
+protected:
+ struct fuji_compressed_params {
+ fuji_compressed_params() = default;
+
+ explicit fuji_compressed_params(const FujiDecompressor& d);
+
+ std::vector<char> q_table; /* quantization table */
+ std::array<int, 5> q_point; /* quantization points */
+ int max_bits;
+ int min_value;
+ int raw_bits;
+ int total_values;
+ int maxDiff;
+ ushort16 line_width;
+ };
+
+ fuji_compressed_params common_info;
+
+ struct int_pair {
+ int value1;
+ int value2;
+ };
+
+ enum _xt_lines {
+ _R0 = 0,
+ _R1,
+ _R2,
+ _R3,
+ _R4,
+ _G0,
+ _G1,
+ _G2,
+ _G3,
+ _G4,
+ _G5,
+ _G6,
+ _G7,
+ _B0,
+ _B1,
+ _B2,
+ _B3,
+ _B4,
+ _ltotal
+ };
+
+ struct fuji_compressed_block {
+ fuji_compressed_block() = default;
+
+ void reset(const fuji_compressed_params* params);
+
+ BitPumpMSB pump;
+
+ // tables of gradients
+ std::array<std::array<int_pair, 41>, 3> grad_even;
+ std::array<std::array<int_pair, 41>, 3> grad_odd;
+
+ std::vector<ushort16> linealloc;
+ std::array<ushort16*, _ltotal> linebuf;
+ };
+
+private:
+ ByteStream input;
+
+ std::array<std::array<CFAColor, 6>, 6> CFA;
+
+ std::vector<FujiStrip> strips;
+
+ void fuji_decode_strip(fuji_compressed_block* info_block,
+ const FujiStrip& strip) const;
+
+ template <typename T>
+ void copy_line(fuji_compressed_block* info, const FujiStrip& strip,
+ int cur_line, T&& idx) const;
+
+ void copy_line_to_xtrans(fuji_compressed_block* info, const FujiStrip& strip,
+ int cur_line) const;
+ void copy_line_to_bayer(fuji_compressed_block* info, const FujiStrip& strip,
+ int cur_line) const;
+
+ inline void fuji_zerobits(BitPumpMSB* pump, int* count) const;
+ int bitDiff(int value1, int value2) const;
+
+ template <typename T1, typename T2>
+ void fuji_decode_sample(T1&& func_0, T2&& func_1, fuji_compressed_block* info,
+ ushort16* line_buf, int* pos,
+ std::array<int_pair, 41>* grads) const;
+ void fuji_decode_sample_even(fuji_compressed_block* info, ushort16* line_buf,
+ int* pos, std::array<int_pair, 41>* grads) const;
+ void fuji_decode_sample_odd(fuji_compressed_block* info, ushort16* line_buf,
+ int* pos, std::array<int_pair, 41>* grads) const;
+
+ void fuji_decode_interpolation_even(int line_width, ushort16* line_buf,
+ int* pos) const;
+ void fuji_extend_generic(std::array<ushort16*, _ltotal> linebuf,
+ int line_width, int start, int end) const;
+ void fuji_extend_red(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const;
+ void fuji_extend_green(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const;
+ void fuji_extend_blue(std::array<ushort16*, _ltotal> linebuf,
+ int line_width) const;
+ void xtrans_decode_block(fuji_compressed_block* info, int cur_line) const;
+ void fuji_bayer_decode_block(fuji_compressed_block* info, int cur_line) const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.cpp
new file mode 100644
index 00000000..ff0e345c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.cpp
@@ -0,0 +1,108 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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/HasselbladDecompressor.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32, BitStream<>::f...
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array
+#include <cassert> // for assert
+
+namespace rawspeed {
+
+HasselbladDecompressor::HasselbladDecompressor(const ByteStream& bs,
+ const RawImage& img)
+ : AbstractLJpegDecompressor(bs, img) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ // FIXME: could be wrong. max "active pixels" - "100 MP"
+ if (mRaw->dim.x == 0 || mRaw->dim.y == 0 || mRaw->dim.x % 2 != 0 ||
+ mRaw->dim.x > 12000 || mRaw->dim.y > 8816) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+}
+
+// Returns len bits as a signed value.
+// Highest bit is a sign bit
+inline int HasselbladDecompressor::getBits(BitPumpMSB32* bs, int len) {
+ int diff = bs->getBits(len);
+ diff = len > 0 ? HuffmanTable::signExtended(diff, len) : diff;
+ if (diff == 65535)
+ return -32768;
+ return diff;
+}
+
+void HasselbladDecompressor::decodeScan() {
+ if (frame.w != static_cast<unsigned>(mRaw->dim.x) ||
+ frame.h != static_cast<unsigned>(mRaw->dim.y)) {
+ ThrowRDE("LJPEG frame does not match EXIF dimensions: (%u; %u) vs (%i; %i)",
+ frame.w, frame.h, mRaw->dim.x, mRaw->dim.y);
+ }
+
+ assert(frame.h > 0);
+ assert(frame.w > 0);
+ assert(frame.w % 2 == 0);
+
+ const auto ht = getHuffmanTables<1>();
+
+ BitPumpMSB32 bitStream(input);
+ // Pixels are packed two at a time, not like LJPEG:
+ // [p1_length_as_huffman][p2_length_as_huffman][p0_diff_with_length][p1_diff_with_length]|NEXT PIXELS
+ for (uint32 y = 0; y < frame.h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ int p1 = 0x8000 + pixelBaseOffset;
+ int p2 = 0x8000 + pixelBaseOffset;
+ for (uint32 x = 0; x < frame.w; x += 2) {
+ int len1 = ht[0]->decodeLength(bitStream);
+ int len2 = ht[0]->decodeLength(bitStream);
+ p1 += getBits(&bitStream, len1);
+ p2 += getBits(&bitStream, len2);
+ // NOTE: this is rather unusual and weird, but appears to be correct.
+ // clampBits(p, 16) results in completely garbled images.
+ dest[x] = ushort16(p1);
+ dest[x + 1] = ushort16(p2);
+ }
+ }
+ input.skipBytes(bitStream.getBufferPosition());
+}
+
+void HasselbladDecompressor::decode(int pixelBaseOffset_)
+{
+ pixelBaseOffset = pixelBaseOffset_;
+
+ if (pixelBaseOffset < -65536 || pixelBaseOffset > 65535)
+ ThrowRDE("Either the offset %i or the bounds are wrong.", pixelBaseOffset);
+
+ // We cannot use fully decoding huffman table,
+ // because values are packed two pixels at the time.
+ fullDecodeHT = false;
+
+ AbstractLJpegDecompressor::decode();
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.h
new file mode 100644
index 00000000..47ad9094
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HasselbladDecompressor.h
@@ -0,0 +1,46 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "decompressors/AbstractLJpegDecompressor.h" // for AbstractLJpegDe...
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+
+namespace rawspeed {
+
+class ByteStream;
+class RawImage;
+
+class HasselbladDecompressor final : public AbstractLJpegDecompressor
+{
+ int pixelBaseOffset = 0;
+
+ void decodeScan() override;
+
+public:
+ HasselbladDecompressor(const ByteStream& bs, const RawImage& img);
+
+ void decode(int pixelBaseOffset_);
+
+ static int getBits(BitPumpMSB32* bs, int len);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTable.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTable.h
new file mode 100644
index 00000000..6f54bcac
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTable.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
+
+// IWYU pragma: begin_exports
+
+#include "decompressors/HuffmanTableLUT.h" // for HuffmanTableLUT
+// #include "decompressors/HuffmanTableLookup.h" // for HuffmanTableLookup
+// #include "decompressors/HuffmanTableTree.h" // for HuffmanTableTree
+// #include "decompressors/HuffmanTableVector.h" // for HuffmanTableVector
+
+// IWYU pragma: end_exports
+
+namespace rawspeed {
+
+using HuffmanTable = HuffmanTableLUT;
+// using HuffmanTable = HuffmanTableLookup;
+// using HuffmanTable = HuffmanTableTree;
+// using HuffmanTable = HuffmanTableVector;
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLUT.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLUT.h
new file mode 100644
index 00000000..45c7c31c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLUT.h
@@ -0,0 +1,257 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+ 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
+ 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, ushort16, int32
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable
+#include "io/BitStream.h" // for BitStreamTraits
+#include <cassert> // for assert
+#include <cstddef> // for size_t
+#include <memory> // for allocator_traits<>::...
+#include <vector> // for vector
+
+/*
+* The following code is inspired by the IJG JPEG library.
+*
+* Copyright (C) 1991, 1992, Thomas G. Lane.
+* Part of the Independent JPEG Group's software.
+* See the file Copyright for more details.
+*
+* Copyright (c) 1993 Brian C. Smith, The Regents of the University
+* of California
+* All rights reserved.
+*
+* Copyright (c) 1994 Kongji Huang and Brian C. Smith.
+* Cornell University
+* All rights reserved.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation for any purpose, without fee, and without written agreement is
+* hereby granted, provided that the above copyright notice and the following
+* two paragraphs appear in all copies of this software.
+*
+* IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
+* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL
+* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+* ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO
+* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*/
+
+namespace rawspeed {
+
+class HuffmanTableLUT final : public AbstractHuffmanTable {
+ // private fields calculated from codesPerBits and codeValues
+ // they are index '1' based, so we can directly lookup the value
+ // for code length l without decrementing
+ std::vector<uint32> maxCodeOL; // index is length of code
+ std::vector<ushort16> codeOffsetOL; // index is length of code
+
+ // The code can be compiled with two different decode lookup table layouts.
+ // The idea is that different CPU architectures may perform better with
+ // one or the other, depending on the relative performance of their arithmetic
+ // core vs their memory access. For an Intel Core i7, the big table is better.
+#if 1
+ // lookup table containing 3 fields: payload:16|flag:8|len:8
+ // The payload may be the fully decoded diff or the length of the diff.
+ // The len field contains the number of bits, this lookup consumed.
+ // A lookup value of 0 means the code was too big to fit into the table.
+ // The optimal LookupDepth is also likely to depend on the CPU architecture.
+ static constexpr unsigned PayloadShift = 16;
+ static constexpr unsigned FlagMask = 0x100;
+ static constexpr unsigned LenMask = 0xff;
+ static constexpr unsigned LookupDepth = 11;
+ std::vector<int32> decodeLookup;
+#else
+ // lookup table containing 2 fields: payload:4|len:4
+ // the payload is the length of the diff, len is the length of the code
+ static constexpr unsigned LookupDepth = 15;
+ static constexpr unsigned PayloadShift = 4;
+ static constexpr unsigned FlagMask = 0;
+ static constexpr unsigned LenMask = 0x0f;
+ std::vector<uchar8> decodeLookup;
+#endif
+
+ bool fullDecode = true;
+ bool fixDNGBug16 = false;
+
+public:
+ void setup(bool fullDecode_, bool fixDNGBug16_) {
+ this->fullDecode = fullDecode_;
+ this->fixDNGBug16 = fixDNGBug16_;
+
+ assert(!nCodesPerLength.empty());
+ assert(maxCodesCount() > 0);
+
+ unsigned int maxCodeLength = nCodesPerLength.size() - 1U;
+ assert(codeValues.size() == maxCodesCount());
+
+ assert(maxCodePlusDiffLength() <= 32U);
+
+ // Figure C.1: make table of Huffman code length for each symbol
+ // Figure C.2: generate the codes themselves
+ const auto symbols = generateCodeSymbols();
+ assert(symbols.size() == maxCodesCount());
+
+ // Figure F.15: generate decoding tables
+ codeOffsetOL.resize(maxCodeLength + 1UL, 0xFFFF);
+ maxCodeOL.resize(maxCodeLength + 1UL, 0xFFFFFFFF);
+ int code_index = 0;
+ for (unsigned int l = 1U; l <= maxCodeLength; l++) {
+ if (nCodesPerLength[l]) {
+ codeOffsetOL[l] = symbols[code_index].code - code_index;
+ code_index += nCodesPerLength[l];
+ maxCodeOL[l] = symbols[code_index - 1].code;
+ }
+ }
+
+ // Generate lookup table for fast decoding lookup.
+ // See definition of decodeLookup above
+ decodeLookup.resize(1 << LookupDepth);
+ for (size_t i = 0; i < symbols.size(); i++) {
+ uchar8 code_l = symbols[i].code_len;
+ if (code_l > static_cast<int>(LookupDepth))
+ break;
+
+ ushort16 ll = symbols[i].code << (LookupDepth - code_l);
+ ushort16 ul = ll | ((1 << (LookupDepth - code_l)) - 1);
+ ushort16 diff_l = codeValues[i];
+ for (ushort16 c = ll; c <= ul; c++) {
+ if (!(c < decodeLookup.size()))
+ ThrowRDE("Corrupt Huffman");
+
+ if (!FlagMask || !fullDecode || diff_l + code_l > LookupDepth) {
+ // lookup bit depth is too small to fit both the encoded length
+ // and the final difference value.
+ // -> store only the length and do a normal sign extension later
+ decodeLookup[c] = diff_l << PayloadShift | code_l;
+ } else {
+ // diff_l + code_l <= lookupDepth
+ // The table bit depth is large enough to store both.
+ decodeLookup[c] = (code_l + diff_l) | FlagMask;
+
+ if (diff_l) {
+ uint32 diff = (c >> (LookupDepth - code_l - diff_l)) & ((1 << diff_l) - 1);
+ decodeLookup[c] |= static_cast<int32>(
+ static_cast<uint32>(signExtended(diff, diff_l))
+ << PayloadShift);
+ }
+ }
+ }
+ }
+ }
+
+ template<typename BIT_STREAM> inline int decodeLength(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(!fullDecode);
+ return decode<BIT_STREAM, false>(bs);
+ }
+
+ template<typename BIT_STREAM> inline int decodeNext(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(fullDecode);
+ return decode<BIT_STREAM, true>(bs);
+ }
+
+ // The bool template paraeter is to enable two versions:
+ // one returning only the length of the of diff bits (see Hasselblad),
+ // one to return the fully decoded diff.
+ // All ifs depending on this bool will be optimized out by the compiler
+ template<typename BIT_STREAM, bool FULL_DECODE> inline int decode(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(FULL_DECODE == fullDecode);
+
+ // 32 is the absolute maximum combined length of code + diff
+ // assertion maxCodePlusDiffLength() <= 32U is already checked in setup()
+ bs.fill(32);
+
+ // for processors supporting bmi2 instructions, using maxCodePlusDiffLength()
+ // might be benifitial
+
+ uint32 code = bs.peekBitsNoFill(LookupDepth);
+ assert(code < decodeLookup.size());
+ auto val = static_cast<unsigned>(decodeLookup[code]);
+ int len = val & LenMask;
+ assert(len >= 0);
+ assert(len <= 16);
+
+ // if the code is invalid (bitstream corrupted) len will be 0
+ bs.skipBitsNoFill(len);
+ if (FULL_DECODE && val & FlagMask) {
+ // if the flag bit is set, the payload is the already sign extended difference
+ return static_cast<int>(val) >> PayloadShift;
+ }
+
+ if (len) {
+ // if the flag bit is not set but len != 0, the payload is the number of bits to sign extend and return
+ const int l_diff = static_cast<int>(val) >> PayloadShift;
+ assert((FULL_DECODE && (len + l_diff <= 32)) || !FULL_DECODE);
+ if (FULL_DECODE && l_diff == 16) {
+ if (fixDNGBug16)
+ bs.skipBits(16);
+ return -32768;
+ }
+ return FULL_DECODE ? signExtended(bs.getBitsNoFill(l_diff), l_diff) : l_diff;
+ }
+
+ uint32 code_l = LookupDepth;
+ bs.skipBitsNoFill(code_l);
+ while (code_l < maxCodeOL.size() &&
+ (0xFFFFFFFF == maxCodeOL[code_l] || code > maxCodeOL[code_l])) {
+ uint32 temp = bs.getBitsNoFill(1);
+ code = (code << 1) | temp;
+ code_l++;
+ }
+
+ if (code_l >= maxCodeOL.size() ||
+ (0xFFFFFFFF == maxCodeOL[code_l] || code > maxCodeOL[code_l]))
+ ThrowRDE("bad Huffman code: %u (len: %u)", code, code_l);
+
+ if (code < codeOffsetOL[code_l])
+ ThrowRDE("likely corrupt Huffman code: %u (len: %u)", code, code_l);
+
+ int diff_l = codeValues[code - codeOffsetOL[code_l]];
+
+ if (!FULL_DECODE)
+ return diff_l;
+
+ if (diff_l == 16) {
+ if (fixDNGBug16)
+ bs.skipBits(16);
+ return -32768;
+ }
+
+ assert(FULL_DECODE);
+ assert((diff_l && (len + code_l + diff_l <= 32)) || !diff_l);
+ return diff_l ? signExtended(bs.getBitsNoFill(diff_l), diff_l) : 0;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLookup.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLookup.h
new file mode 100644
index 00000000..df8408cc
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableLookup.h
@@ -0,0 +1,171 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+ 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
+ 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, ushort16
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable
+#include "io/BitStream.h" // for BitStreamTraits
+#include <cassert> // for assert
+#include <memory> // for allocator_traits<>::...
+#include <vector> // for vector
+
+/*
+ * The following code is inspired by the IJG JPEG library.
+ *
+ * Copyright (C) 1991, 1992, Thomas G. Lane.
+ * Part of the Independent JPEG Group's software.
+ * See the file Copyright for more details.
+ *
+ * Copyright (c) 1993 Brian C. Smith, The Regents of the University
+ * of California
+ * All rights reserved.
+ *
+ * Copyright (c) 1994 Kongji Huang and Brian C. Smith.
+ * Cornell University
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL
+ * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+namespace rawspeed {
+
+class HuffmanTableLookup final : public AbstractHuffmanTable {
+ // private fields calculated from codesPerBits and codeValues
+ // they are index '1' based, so we can directly lookup the value
+ // for code length l without decrementing
+ std::vector<uint32> maxCodeOL; // index is length of code
+ std::vector<ushort16> codeOffsetOL; // index is length of code
+
+ bool fullDecode = true;
+ bool fixDNGBug16 = false;
+
+public:
+ void setup(bool fullDecode_, bool fixDNGBug16_) {
+ this->fullDecode = fullDecode_;
+ this->fixDNGBug16 = fixDNGBug16_;
+
+ assert(!nCodesPerLength.empty());
+ assert(maxCodesCount() > 0);
+
+ unsigned int maxCodeLength = nCodesPerLength.size() - 1U;
+ assert(codeValues.size() == maxCodesCount());
+
+ assert(maxCodePlusDiffLength() <= 32U);
+
+ // Figure C.1: make table of Huffman code length for each symbol
+ // Figure C.2: generate the codes themselves
+ const auto symbols = generateCodeSymbols();
+ assert(symbols.size() == maxCodesCount());
+
+ // Figure F.15: generate decoding tables
+ codeOffsetOL.resize(maxCodeLength + 1UL, 0xFFFF);
+ maxCodeOL.resize(maxCodeLength + 1UL, 0xFFFFFFFF);
+ int code_index = 0;
+ for (unsigned int l = 1U; l <= maxCodeLength; l++) {
+ if (nCodesPerLength[l]) {
+ codeOffsetOL[l] = symbols[code_index].code - code_index;
+ code_index += nCodesPerLength[l];
+ maxCodeOL[l] = symbols[code_index - 1].code;
+ }
+ }
+ }
+
+ template <typename BIT_STREAM> inline int decodeLength(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(!fullDecode);
+ return decode<BIT_STREAM, false>(bs);
+ }
+
+ template <typename BIT_STREAM> inline int decodeNext(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(fullDecode);
+ return decode<BIT_STREAM, true>(bs);
+ }
+
+ // The bool template paraeter is to enable two versions:
+ // one returning only the length of the of diff bits (see Hasselblad),
+ // one to return the fully decoded diff.
+ // All ifs depending on this bool will be optimized out by the compiler
+ template <typename BIT_STREAM, bool FULL_DECODE>
+ inline int decode(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(FULL_DECODE == fullDecode);
+
+ // 32 is the absolute maximum combined length of code + diff
+ // assertion maxCodePlusDiffLength() <= 32U is already checked in setup()
+ bs.fill(32);
+
+ // for processors supporting bmi2 instructions, using
+ // maxCodePlusDiffLength() might be benifitial
+
+ uint32 code = 0;
+ uint32 code_l = 0;
+ while (code_l < maxCodeOL.size() &&
+ (0xFFFFFFFF == maxCodeOL[code_l] || code > maxCodeOL[code_l])) {
+ uint32 temp = bs.getBitsNoFill(1);
+ code = (code << 1) | temp;
+ code_l++;
+ }
+
+ if (code_l >= maxCodeOL.size() ||
+ (0xFFFFFFFF == maxCodeOL[code_l] || code > maxCodeOL[code_l]))
+ ThrowRDE("bad Huffman code: %u (len: %u)", code, code_l);
+
+ if (code < codeOffsetOL[code_l])
+ ThrowRDE("likely corrupt Huffman code: %u (len: %u)", code, code_l);
+
+ int diff_l = codeValues[code - codeOffsetOL[code_l]];
+
+ if (!FULL_DECODE)
+ return diff_l;
+
+ if (diff_l == 16) {
+ if (fixDNGBug16)
+ bs.skipBits(16);
+ return -32768;
+ }
+
+ assert(FULL_DECODE);
+ assert((diff_l && (code_l + diff_l <= 32)) || !diff_l);
+ return diff_l ? signExtended(bs.getBitsNoFill(diff_l), diff_l) : 0;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableTree.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableTree.h
new file mode 100644
index 00000000..0dd0088e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableTree.h
@@ -0,0 +1,165 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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 "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable...
+#include "decompressors/BinaryHuffmanTree.h" // IWYU pragma: export
+#include "io/BitStream.h" // for BitStreamTraits
+#include <algorithm> // for for_each
+#include <cassert> // for assert
+#include <initializer_list> // for initializer_list
+#include <iterator> // for advance, next
+#include <memory> // for unique_ptr, make_unique
+#include <vector> // for vector, vector<>::co...
+
+namespace rawspeed {
+
+class HuffmanTableTree final : public AbstractHuffmanTable {
+ using ValueType = decltype(codeValues)::value_type;
+
+ BinaryHuffmanTree<ValueType> tree;
+
+ bool fullDecode = true;
+ bool fixDNGBug16 = false;
+
+protected:
+ template <typename BIT_STREAM>
+ inline ValueType getValue(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ CodeSymbol partial;
+
+ const auto* top = &(tree.root->getAsBranch());
+
+ // Read bits until either find the code or detect the uncorrect code
+ for (partial.code = 0, partial.code_len = 1;; ++partial.code_len) {
+ assert(partial.code_len <= 16);
+
+ // Read one more bit
+ const bool bit = bs.getBits(1);
+
+ partial.code <<= 1;
+ partial.code |= bit;
+
+ // What is the last bit, which we have just read?
+
+ // NOTE: The order *IS* important! Left to right, zero to one!
+ const auto& newNode = !bit ? top->zero : top->one;
+
+ if (!newNode) {
+ // Got nothing in this direction.
+ ThrowRDE("bad Huffman code: %u (len: %u)", partial.code,
+ partial.code_len);
+ }
+
+ if (static_cast<decltype(tree)::Node::Type>(*newNode) ==
+ decltype(tree)::Node::Type::Leaf) {
+ // Ok, great, hit a Leaf. This is it.
+ return newNode->getAsLeaf().value;
+ }
+
+ // Else, this is a branch, continue looking.
+ top = &(newNode->getAsBranch());
+ }
+
+ // We have either returned the found symbol, or thrown on uncorrect symbol.
+ __builtin_unreachable();
+ }
+
+public:
+ void setup(bool fullDecode_, bool fixDNGBug16_) {
+ this->fullDecode = fullDecode_;
+ this->fixDNGBug16 = fixDNGBug16_;
+
+ assert(!nCodesPerLength.empty());
+ assert(maxCodesCount() > 0);
+ assert(codeValues.size() == maxCodesCount());
+
+ auto currValue = codeValues.cbegin();
+ for (auto codeLen = 1UL; codeLen < nCodesPerLength.size(); codeLen++) {
+ const auto nCodesForCurrLen = nCodesPerLength[codeLen];
+
+ auto nodes = tree.getAllVacantNodesAtDepth(codeLen);
+ if (nodes.size() < nCodesForCurrLen) {
+ ThrowRDE("Got too many (%u) codes for len %lu, can only have %zu codes",
+ nCodesForCurrLen, codeLen, nodes.size());
+ }
+
+ // Make first nCodesForCurrLen nodes Leafs
+ std::for_each(nodes.cbegin(), std::next(nodes.cbegin(), nCodesForCurrLen),
+ [&currValue](auto* node) {
+ *node =
+ std::make_unique<decltype(tree)::Leaf>(*currValue);
+ std::advance(currValue, 1);
+ });
+ }
+
+ assert(codeValues.cend() == currValue);
+
+ // And get rid of all the branches that do not lead to Leafs.
+ // It is crucial to detect degenerate codes at the earliest.
+ tree.pruneLeaflessBranches();
+ }
+
+ template <typename BIT_STREAM> inline int decodeLength(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(!fullDecode);
+ return decode<BIT_STREAM, false>(bs);
+ }
+
+ template <typename BIT_STREAM> inline int decodeNext(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(fullDecode);
+ return decode<BIT_STREAM, true>(bs);
+ }
+
+ // The bool template paraeter is to enable two versions:
+ // one returning only the length of the of diff bits (see Hasselblad),
+ // one to return the fully decoded diff.
+ // All ifs depending on this bool will be optimized out by the compiler
+ template <typename BIT_STREAM, bool FULL_DECODE>
+ inline int decode(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(FULL_DECODE == fullDecode);
+
+ const auto codeValue = getValue(bs);
+
+ const int diff_l = codeValue;
+
+ if (!FULL_DECODE)
+ return diff_l;
+
+ if (diff_l == 16) {
+ if (fixDNGBug16)
+ bs.skipBits(16);
+ return -32768;
+ }
+
+ return diff_l ? signExtended(bs.getBits(diff_l), diff_l) : 0;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableVector.h
b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableVector.h
new file mode 100644
index 00000000..3a1c8824
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/HuffmanTableVector.h
@@ -0,0 +1,155 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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 "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable...
+#include "io/BitStream.h" // for BitStreamTraits
+#include <cassert> // for assert
+#include <utility> // for make_pair, pair
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class HuffmanTableVector final : public AbstractHuffmanTable {
+ std::vector<CodeSymbol> symbols;
+
+ bool fullDecode = true;
+ bool fixDNGBug16 = false;
+
+ // Given this code len, which code id is the minimal?
+ std::vector<unsigned int> extrCodeIdForLen; // index is length of code
+
+protected:
+ template <typename BIT_STREAM>
+ inline std::pair<CodeSymbol, unsigned> getSymbol(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+
+ CodeSymbol partial;
+ unsigned long codeId;
+
+ // Read bits until either find the code or detect the uncorrect code
+ for (partial.code = 0, partial.code_len = 1;; ++partial.code_len) {
+ assert(partial.code_len <= 16);
+
+ // Read one more bit
+ const bool bit = bs.getBits(1);
+
+ partial.code <<= 1;
+ partial.code |= bit;
+
+ // Given global ordering and the code length, we know the code id range.
+ for (codeId = extrCodeIdForLen[partial.code_len];
+ codeId < extrCodeIdForLen[1U + partial.code_len]; codeId++) {
+ const CodeSymbol& symbol = symbols[codeId];
+ if (symbol == partial) // yay, found?
+ return std::make_pair(symbol, codeId);
+ }
+
+ // Ok, but does any symbol have this same prefix?
+ bool haveCommonPrefix = false;
+ for (; codeId < symbols.size(); codeId++) {
+ const CodeSymbol& symbol = symbols[codeId];
+ haveCommonPrefix |= CodeSymbol::HaveCommonPrefix(symbol, partial);
+ if (haveCommonPrefix)
+ break;
+ }
+
+ // If no symbols have this prefix, then the code is invalid.
+ if (!haveCommonPrefix) {
+ ThrowRDE("bad Huffman code: %u (len: %u)", partial.code,
+ partial.code_len);
+ }
+ }
+
+ // We have either returned the found symbol, or thrown on uncorrect symbol.
+ __builtin_unreachable();
+ }
+
+public:
+ void setup(bool fullDecode_, bool fixDNGBug16_) {
+ this->fullDecode = fullDecode_;
+ this->fixDNGBug16 = fixDNGBug16_;
+
+ assert(!nCodesPerLength.empty());
+ assert(maxCodesCount() > 0);
+ assert(codeValues.size() == maxCodesCount());
+
+ // Figure C.1: make table of Huffman code length for each symbol
+ // Figure C.2: generate the codes themselves
+ symbols = generateCodeSymbols();
+ assert(symbols.size() == maxCodesCount());
+
+ extrCodeIdForLen.reserve(1U + nCodesPerLength.size());
+ extrCodeIdForLen.resize(2); // for len 0 and 1, the min code id is always 0
+ for (auto codeLen = 1UL; codeLen < nCodesPerLength.size(); codeLen++) {
+ auto minCodeId = extrCodeIdForLen.back();
+ minCodeId += nCodesPerLength[codeLen];
+ extrCodeIdForLen.emplace_back(minCodeId);
+ }
+ assert(extrCodeIdForLen.size() == 1U + nCodesPerLength.size());
+ }
+
+ template <typename BIT_STREAM> inline int decodeLength(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(!fullDecode);
+ return decode<BIT_STREAM, false>(bs);
+ }
+
+ template <typename BIT_STREAM> inline int decodeNext(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(fullDecode);
+ return decode<BIT_STREAM, true>(bs);
+ }
+
+ // The bool template paraeter is to enable two versions:
+ // one returning only the length of the of diff bits (see Hasselblad),
+ // one to return the fully decoded diff.
+ // All ifs depending on this bool will be optimized out by the compiler
+ template <typename BIT_STREAM, bool FULL_DECODE>
+ inline int decode(BIT_STREAM& bs) const {
+ static_assert(BitStreamTraits<BIT_STREAM>::canUseWithHuffmanTable,
+ "This BitStream specialization is not marked as usable here");
+ assert(FULL_DECODE == fullDecode);
+
+ const auto got = getSymbol(bs);
+ const unsigned codeId = got.second;
+
+ const int diff_l = codeValues[codeId];
+
+ if (!FULL_DECODE)
+ return diff_l;
+
+ if (diff_l == 16) {
+ if (fixDNGBug16)
+ bs.skipBits(16);
+ return -32768;
+ }
+
+ return diff_l ? signExtended(bs.getBits(diff_l), diff_l) : 0;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.cpp
new file mode 100644
index 00000000..13fabb6c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.cpp
@@ -0,0 +1,171 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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
+*/
+
+#include "rawspeedconfig.h"
+
+#ifdef HAVE_JPEG
+
+#include "decompressors/JpegDecompressor.h"
+
+#include "common/Common.h" // for uchar8, uint32, ushort16
+#include "common/Memory.h" // for alignedFree, alignedMalloc...
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/ByteStream.h" // for ByteStream
+#include <algorithm> // for min
+#include <cstdio> // for size_t
+#include <jpeglib.h> // for jpeg
+#include <memory> // for unique_ptr
+#include <vector> // for vector
+
+#ifndef HAVE_JPEG_MEM_SRC
+#include "io/IOException.h" // for ThrowIOE
+#endif
+
+using std::vector;
+using std::unique_ptr;
+using std::min;
+
+namespace rawspeed {
+
+#ifdef HAVE_JPEG_MEM_SRC
+
+// FIXME: some libjpeg versions discard const qual for the input data pointer
+// should this be a cmake check?
+#define JPEG_MEMSRC(A, B, C) \
+ jpeg_mem_src(A, const_cast<unsigned char*>(B), C) // NOLINT
+
+#else
+
+#define JPEG_MEMSRC(A, B, C) jpeg_mem_src_int(A, B, C)
+/* Read JPEG image from a memory segment */
+
+static void init_source(j_decompress_ptr cinfo) {}
+static boolean fill_input_buffer(j_decompress_ptr cinfo) {
+ auto* src = (struct jpeg_source_mgr*)cinfo->src;
+ return (boolean) !!src->bytes_in_buffer;
+}
+static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ auto* src = (struct jpeg_source_mgr*)cinfo->src;
+
+ if (num_bytes > (int)src->bytes_in_buffer)
+ ThrowIOE("read out of buffer");
+ if (num_bytes > 0) {
+ src->next_input_byte += (size_t)num_bytes;
+ src->bytes_in_buffer -= (size_t)num_bytes;
+ }
+}
+static void term_source(j_decompress_ptr cinfo) {}
+static void jpeg_mem_src_int(j_decompress_ptr cinfo,
+ const unsigned char* buffer, long nbytes) {
+ struct jpeg_source_mgr* src;
+
+ if (cinfo->src == nullptr) { /* first time for this JPEG object? */
+ cinfo->src = (struct jpeg_source_mgr*)(*cinfo->mem->alloc_small)(
+ (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr));
+ }
+
+ src = (struct jpeg_source_mgr*)cinfo->src;
+ src->init_source = init_source;
+ src->fill_input_buffer = fill_input_buffer;
+ src->skip_input_data = skip_input_data;
+ src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ src->term_source = term_source;
+ src->bytes_in_buffer = nbytes;
+ src->next_input_byte = (const JOCTET*)buffer;
+}
+
+#endif
+
+[[noreturn]] METHODDEF(void) my_error_throw(j_common_ptr cinfo) {
+ std::array<char, JMSG_LENGTH_MAX> buf;
+ buf.fill(0);
+ (*cinfo->err->format_message)(cinfo, buf.data());
+ ThrowRDE("JPEG decoder error: %s", buf.data());
+}
+
+struct JpegDecompressor::JpegDecompressStruct : jpeg_decompress_struct {
+ struct jpeg_error_mgr jerr;
+ JpegDecompressStruct() {
+ jpeg_create_decompress(this);
+
+ err = jpeg_std_error(&jerr);
+ jerr.error_exit = &my_error_throw;
+ }
+ ~JpegDecompressStruct() { jpeg_destroy_decompress(this); }
+};
+
+void JpegDecompressor::decode(uint32 offX,
+ uint32 offY) { /* Each slice is a JPEG image */
+ struct JpegDecompressStruct dinfo;
+
+ vector<JSAMPROW> buffer(1);
+
+ const auto size = input.getRemainSize();
+
+ JPEG_MEMSRC(&dinfo, input.getData(size), size);
+
+ if (JPEG_HEADER_OK != jpeg_read_header(&dinfo, static_cast<boolean>(true)))
+ ThrowRDE("Unable to read JPEG header");
+
+ jpeg_start_decompress(&dinfo);
+ if (dinfo.output_components != static_cast<int>(mRaw->getCpp()))
+ ThrowRDE("Component count doesn't match");
+ int row_stride = dinfo.output_width * dinfo.output_components;
+
+ unique_ptr<uchar8[], // NOLINT
+ decltype(&alignedFree)>
+ complete_buffer(
+ alignedMallocArray<uchar8, 16>(dinfo.output_height, row_stride),
+ &alignedFree);
+ while (dinfo.output_scanline < dinfo.output_height) {
+ buffer[0] = static_cast<JSAMPROW>(
+ &complete_buffer[static_cast<size_t>(dinfo.output_scanline) *
+ row_stride]);
+ if (0 == jpeg_read_scanlines(&dinfo, &buffer[0], 1))
+ ThrowRDE("JPEG Error while decompressing image.");
+ }
+ jpeg_finish_decompress(&dinfo);
+
+ // Now the image is decoded, and we copy the image data
+ int copy_w = min(mRaw->dim.x - offX, dinfo.output_width);
+ int copy_h = min(mRaw->dim.y - offY, dinfo.output_height);
+ for (int y = 0; y < copy_h; y++) {
+ uchar8* src = &complete_buffer[static_cast<size_t>(row_stride) * y];
+ auto* dst = reinterpret_cast<ushort16*>(mRaw->getData(offX, y + offY));
+ for (int x = 0; x < copy_w; x++) {
+ for (int c = 0; c < dinfo.output_components; c++) {
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+ }
+}
+
+} // namespace rawspeed
+
+#else
+
+#pragma message \
+ "JPEG is not present! Lossy JPEG compression will not be supported!"
+
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.h
new file mode 100644
index 00000000..114c6ed8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/JpegDecompressor.h
@@ -0,0 +1,57 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 "rawspeedconfig.h"
+
+#ifdef HAVE_JPEG
+
+#include "common/Common.h" // for uint32
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianne...
+#include <utility> // for move
+
+namespace rawspeed {
+
+class JpegDecompressor final : public AbstractDecompressor {
+ struct JpegDecompressStruct;
+ ByteStream input;
+ RawImage mRaw;
+
+public:
+ JpegDecompressor(ByteStream bs, const RawImage& img)
+ : input(std::move(bs)), mRaw(img) {
+ input.setByteOrder(Endianness::big);
+ }
+
+ void decode(uint32 offsetX, uint32 offsetY);
+};
+
+} // namespace rawspeed
+
+#else
+
+#pragma message \
+ "JPEG is not present! Lossy JPEG compression will not be supported!"
+
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.cpp
new file mode 100644
index 00000000..7d19a560
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.cpp
@@ -0,0 +1,143 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/KodakDecompressor.h"
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/ByteStream.h" // for ByteStream
+#include <algorithm> // for min
+#include <array> // for array
+#include <cassert> // for assert
+#include <utility> // for move
+
+namespace rawspeed {
+
+constexpr int KodakDecompressor::segment_size;
+
+KodakDecompressor::KodakDecompressor(const RawImage& img, ByteStream bs,
+ int bps_, bool uncorrectedRawValues_)
+ : mRaw(img), input(std::move(bs)), bps(bps_),
+ uncorrectedRawValues(uncorrectedRawValues_) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ if (mRaw->dim.x == 0 || mRaw->dim.y == 0 || mRaw->dim.x % 4 != 0 ||
+ mRaw->dim.x > 4516 || mRaw->dim.y > 3012)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+
+ if (bps != 10 && bps != 12)
+ ThrowRDE("Unexpected bits per sample: %i", bps);
+
+ // Lower estimate: this decompressor requires *at least* half a byte
+ // per output pixel
+ input.check(mRaw->dim.area() / 2ULL);
+}
+
+KodakDecompressor::segment
+KodakDecompressor::decodeSegment(const uint32 bsize) {
+ assert(bsize > 0);
+ assert(bsize % 4 == 0);
+ assert(bsize <= segment_size);
+
+ segment out;
+ static_assert(out.size() == segment_size, "Wrong segment size");
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+ // We are to produce only bsize pixels.
+ __sanitizer_annotate_contiguous_container(out.begin(), out.end(), out.end(),
+ out.begin() + bsize);
+#endif
+
+ std::array<uchar8, 2 * segment_size> blen;
+ uint64 bitbuf = 0;
+ uint32 bits = 0;
+
+ for (uint32 i = 0; i < bsize; i += 2) {
+ // One byte per two pixels
+ blen[i] = input.peekByte() & 15;
+ blen[i + 1] = input.getByte() >> 4;
+ }
+ if ((bsize & 7) == 4) {
+ bitbuf = (static_cast<uint64>(input.getByte())) << 8UL;
+ bitbuf += (static_cast<int>(input.getByte()));
+ bits = 16;
+ }
+ for (uint32 i = 0; i < bsize; i++) {
+ uint32 len = blen[i];
+ assert(len < 16);
+
+ if (bits < len) {
+ for (uint32 j = 0; j < 32; j += 8) {
+ bitbuf += static_cast<long long>(static_cast<int>(input.getByte()))
+ << (bits + (j ^ 8));
+ }
+ bits += 32;
+ }
+
+ uint32 diff = static_cast<uint32>(bitbuf) & (0xffff >> (16 - len));
+ bitbuf >>= len;
+ bits -= len;
+
+ out[i] = len != 0 ? HuffmanTable::signExtended(diff, len) : int(diff);
+ }
+
+ return out;
+}
+
+void KodakDecompressor::decompress() {
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+
+ uint32 random = 0;
+ for (auto y = 0; y < mRaw->dim.y; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+
+ for (auto x = 0; x < mRaw->dim.x; x += segment_size) {
+ const uint32 len = std::min(segment_size, mRaw->dim.x - x);
+
+ const segment buf = decodeSegment(len);
+
+ std::array<int, 2> pred;
+ pred.fill(0);
+
+ for (uint32 i = 0; i < len; i++) {
+ pred[i & 1] += buf[i];
+
+ int value = pred[i & 1];
+ if (unsigned(value) >= (1U << bps))
+ ThrowRDE("Value out of bounds %d (bps = %i)", value, bps);
+
+ if (uncorrectedRawValues)
+ dest[x + i] = value;
+ else
+ mRaw->setWithLookUp(value, reinterpret_cast<uchar8*>(&dest[x + i]),
+ &random);
+ }
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.h
new file mode 100644
index 00000000..14bc4f0b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/KodakDecompressor.h
@@ -0,0 +1,51 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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/Common.h" // for ushort16
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array
+
+namespace rawspeed {
+
+class KodakDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+ ByteStream input;
+ int bps;
+ bool uncorrectedRawValues;
+
+ static constexpr int segment_size = 256; // pixels
+ using segment = std::array<short16, segment_size>;
+
+ segment decodeSegment(uint32 bsize);
+
+public:
+ KodakDecompressor(const RawImage& img, ByteStream bs, int bps,
+ bool uncorrectedRawValues_);
+
+ void decompress();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.cpp
new file mode 100644
index 00000000..c6d51e99
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.cpp
@@ -0,0 +1,232 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/LJpegDecompressor.h"
+#include "common/Common.h" // for unroll_loop, uint32, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpJPEG.h" // for BitPumpJPEG, BitStream<>::...
+#include <algorithm> // for copy_n
+#include <cassert> // for assert
+
+using std::copy_n;
+
+namespace rawspeed {
+
+LJpegDecompressor::LJpegDecompressor(const ByteStream& bs, const RawImage& img)
+ : AbstractLJpegDecompressor(bs, img) {
+ if (mRaw->getDataType() != TYPE_USHORT16)
+ ThrowRDE("Unexpected data type (%u)", mRaw->getDataType());
+
+ if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == 2) ||
+ (mRaw->getCpp() == 3 && mRaw->getBpp() == 6)))
+ ThrowRDE("Unexpected component count (%u)", mRaw->getCpp());
+
+ if (mRaw->dim.x == 0 || mRaw->dim.y == 0)
+ ThrowRDE("Image has zero size");
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ // Yeah, sure, here it would be just dumb to leave this for production :)
+ if (mRaw->dim.x > 7424 || mRaw->dim.y > 5552) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+#endif
+}
+
+void LJpegDecompressor::decode(uint32 offsetX, uint32 offsetY, uint32 width,
+ uint32 height, bool fixDng16Bug_) {
+ if (offsetX >= static_cast<unsigned>(mRaw->dim.x))
+ ThrowRDE("X offset outside of image");
+ if (offsetY >= static_cast<unsigned>(mRaw->dim.y))
+ ThrowRDE("Y offset outside of image");
+
+ if (width > static_cast<unsigned>(mRaw->dim.x))
+ ThrowRDE("Tile wider than image");
+ if (height > static_cast<unsigned>(mRaw->dim.y))
+ ThrowRDE("Tile taller than image");
+
+ if (offsetX + width > static_cast<unsigned>(mRaw->dim.x))
+ ThrowRDE("Tile overflows image horizontally");
+ if (offsetY + height > static_cast<unsigned>(mRaw->dim.y))
+ ThrowRDE("Tile overflows image vertically");
+
+ offX = offsetX;
+ offY = offsetY;
+ w = width;
+ h = height;
+
+ fixDng16Bug = fixDng16Bug_;
+
+ AbstractLJpegDecompressor::decode();
+}
+
+void LJpegDecompressor::decodeScan()
+{
+ assert(frame.cps > 0);
+
+ if (predictorMode != 1)
+ ThrowRDE("Unsupported predictor mode: %u", predictorMode);
+
+ for (uint32 i = 0; i < frame.cps; i++)
+ if (frame.compInfo[i].superH != 1 || frame.compInfo[i].superV != 1)
+ ThrowRDE("Unsupported subsampling");
+
+ assert(static_cast<unsigned>(mRaw->dim.x) > offX);
+ if ((mRaw->getCpp() * (mRaw->dim.x - offX)) < frame.cps)
+ ThrowRDE("Got less pixels than the components per sample");
+
+ // How many output pixels are we expected to produce, as per DNG tiling?
+ const auto tileRequiredWidth = mRaw->getCpp() * w;
+
+ // How many full pixel blocks do we need to consume for that?
+ const auto blocksToConsume = roundUpDivision(tileRequiredWidth, frame.cps);
+ if (frame.w < blocksToConsume || frame.h < h) {
+ ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)",
+ frame.cps * frame.w, frame.h, tileRequiredWidth, h);
+ }
+
+ // How many full pixel blocks will we produce?
+ fullBlocks = tileRequiredWidth / frame.cps; // Truncating division!
+ // Do we need to also produce part of a block?
+ trailingPixels = tileRequiredWidth % frame.cps;
+
+ if (trailingPixels == 0) {
+ switch (frame.cps) {
+ case 1:
+ decodeN<1>();
+ break;
+ case 2:
+ decodeN<2>();
+ break;
+ case 3:
+ decodeN<3>();
+ break;
+ case 4:
+ decodeN<4>();
+ break;
+ default:
+ ThrowRDE("Unsupported number of components: %u", frame.cps);
+ }
+ } else /* trailingPixels != 0 */ {
+ // FIXME: using different function just for one tile likely causes
+ // i-cache misses and whatnot. Need to check how not splitting it into
+ // two different functions affects performance of the normal case.
+ switch (frame.cps) {
+ // Naturally can't happen for CPS=1.
+ case 2:
+ decodeN<2, /*WeirdWidth=*/true>();
+ break;
+ case 3:
+ decodeN<3, /*WeirdWidth=*/true>();
+ break;
+ case 4:
+ decodeN<4, /*WeirdWidth=*/true>();
+ break;
+ default:
+ ThrowRDE("Unsupported number of components: %u", frame.cps);
+ }
+ }
+}
+
+// N_COMP == number of components (2, 3 or 4)
+
+template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
+ assert(mRaw->getCpp() > 0);
+ assert(N_COMP > 0);
+ assert(N_COMP >= mRaw->getCpp());
+ assert((N_COMP / mRaw->getCpp()) > 0);
+
+ assert(mRaw->dim.x >= N_COMP);
+ assert((mRaw->getCpp() * (mRaw->dim.x - offX)) >= N_COMP);
+
+ auto ht = getHuffmanTables<N_COMP>();
+ auto pred = getInitialPredictors<N_COMP>();
+ auto predNext = pred.data();
+
+ BitPumpJPEG bitStream(input);
+
+ // A recoded DNG might be split up into tiles of self contained LJpeg blobs.
+ // The tiles at the bottom and the right may extend beyond the dimension of
+ // the raw image buffer. The excessive content has to be ignored.
+
+ assert(frame.h >= h);
+ assert(frame.cps * frame.w >= mRaw->getCpp() * w);
+
+ assert(offY + h <= static_cast<unsigned>(mRaw->dim.y));
+ assert(offX + w <= static_cast<unsigned>(mRaw->dim.x));
+
+ // For y, we can simply stop decoding when we reached the border.
+ for (unsigned y = 0; y < h; ++y) {
+ auto destY = offY + y;
+ auto dest =
+ reinterpret_cast<ushort16*>(mRaw->getDataUncropped(offX, destY));
+
+ copy_n(predNext, N_COMP, pred.data());
+ // the predictor for the next line is the start of this line
+ predNext = dest;
+
+ unsigned x = 0;
+
+ // FIXME: predictor may have value outside of the ushort16.
+ // https://github.com/darktable-org/rawspeed/issues/175
+
+ // For x, we first process all full pixel blocks within the image buffer ...
+ for (; x < fullBlocks; ++x) {
+ unroll_loop<N_COMP>([&](int i) {
+ pred[i] = ushort16(pred[i] + ht[i]->decodeNext(bitStream));
+ *dest++ = pred[i];
+ });
+ }
+
+ // Sometimes we also need to consume one more block, and produce part of it.
+ if /*constexpr*/ (WeirdWidth) {
+ // FIXME: evaluate i-cache implications due to this being compile-time.
+ static_assert(N_COMP > 1 || !WeirdWidth,
+ "can't want part of 1-pixel-wide block");
+ // Some rather esoteric DNG's have odd dimensions, e.g. width % 2 = 1.
+ // We may end up needing just part of last N_COMP pixels.
+ assert(trailingPixels > 0);
+ assert(trailingPixels < N_COMP);
+ unsigned c = 0;
+ for (; c < trailingPixels; ++c) {
+ pred[c] = ushort16(pred[c] + ht[c]->decodeNext(bitStream));
+ *dest++ = pred[c];
+ }
+ // Discard the rest of the block.
+ assert(c < N_COMP);
+ for (; c < N_COMP; ++c) {
+ ht[c]->decodeNext(bitStream);
+ }
+ ++x; // We did just process one more block.
+ }
+
+ // ... and discard the rest.
+ for (; x < frame.w; ++x) {
+ unroll_loop<N_COMP>([&](int i) {
+ ht[i]->decodeNext(bitStream);
+ });
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.h
new file mode 100644
index 00000000..d2783a85
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/LJpegDecompressor.h
@@ -0,0 +1,53 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "decompressors/AbstractLJpegDecompressor.h" // for AbstractLJpegDe...
+
+namespace rawspeed {
+
+class ByteStream;
+class RawImage;
+
+// Decompresses Lossless JPEGs, with 2-4 components
+
+class LJpegDecompressor final : public AbstractLJpegDecompressor
+{
+ void decodeScan() override;
+ template <int N_COMP, bool WeirdWidth = false> void decodeN();
+
+ uint32 offX = 0;
+ uint32 offY = 0;
+ uint32 w = 0;
+ uint32 h = 0;
+
+ uint32 fullBlocks = 0;
+ uint32 trailingPixels = 0;
+
+public:
+ LJpegDecompressor(const ByteStream& bs, const RawImage& img);
+
+ void decode(uint32 offsetX, uint32 offsetY, uint32 width, uint32 height,
+ bool fixDng16Bug_);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.cpp
new file mode 100644
index 00000000..f07a60f8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.cpp
@@ -0,0 +1,544 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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/NikonDecompressor.h"
+#include "common/Common.h" // for uint32, clampBits, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpMSB.h" // for BitPumpMSB, BitStream<>::f...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include <cassert> // for assert
+#include <cstdio> // for size_t
+#include <vector> // for vector
+
+namespace rawspeed {
+
+const std::array<std::array<std::array<uchar8, 16>, 2>, 6>
+ NikonDecompressor::nikon_tree = {{
+ {{/* 12-bit lossy */
+ {0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0},
+ {5, 4, 3, 6, 2, 7, 1, 0, 8, 9, 11, 10, 12}}},
+ {{/* 12-bit lossy after split */
+ {0, 1, 5, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0},
+ {0x39, 0x5a, 0x38, 0x27, 0x16, 5, 4, 3, 2, 1, 0, 11, 12, 12}}},
+ {{/* 12-bit lossless */
+ {0, 1, 4, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {5, 4, 6, 3, 7, 2, 8, 1, 9, 0, 10, 11, 12}}},
+ {{/* 14-bit lossy */
+ {0, 1, 4, 3, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0},
+ {5, 6, 4, 7, 8, 3, 9, 2, 1, 0, 10, 11, 12, 13, 14}}},
+ {{/* 14-bit lossy after split */
+ {0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0},
+ {8, 0x5c, 0x4b, 0x3a, 0x29, 7, 6, 5, 4, 3, 2, 1, 0, 13, 14}}},
+ {{/* 14-bit lossless */
+ {0, 1, 4, 2, 2, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0},
+ {7, 6, 8, 5, 9, 4, 10, 3, 11, 12, 2, 0, 1, 13, 14}}},
+ }};
+
+namespace {
+
+const std::array<uint32, 32> bitMask = {
+ {0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, 0x0fffffff, 0x07ffffff,
+ 0x03ffffff, 0x01ffffff, 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
+ 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, 0x0000ffff, 0x00007fff,
+ 0x00003fff, 0x00001fff, 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
+ 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, 0x0000000f, 0x00000007,
+ 0x00000003, 0x00000001}};
+
+class NikonLASDecompressor {
+ bool mUseBigtable = true;
+ bool mDNGCompatible = false;
+
+ struct HuffmanTable {
+ /*
+ * These two fields directly represent the contents of a JPEG DHT
+ * marker
+ */
+ std::array<uint32, 17> bits;
+ std::array<uint32, 256> huffval;
+
+ /*
+ * The remaining fields are computed from the above to allow more
+ * efficient coding and decoding. These fields should be considered
+ * private to the Huffman compression & decompression modules.
+ */
+
+ std::array<ushort16, 17> mincode;
+ std::array<int, 18> maxcode;
+ std::array<short, 17> valptr;
+ std::array<uint32, 256> numbits;
+ std::vector<int> bigTable;
+ bool initialized;
+ } dctbl1;
+
+ void createHuffmanTable() {
+ int p;
+ int i;
+ int l;
+ int lastp;
+ int si;
+ std::array<char, 257> huffsize;
+ std::array<ushort16, 257> huffcode;
+ ushort16 code;
+ int size;
+ int value;
+ int ll;
+ int ul;
+
+ /*
+ * Figure C.1: make table of Huffman code length for each symbol
+ * Note that this is in code-length order.
+ */
+ p = 0;
+ for (l = 1; l <= 16; l++) {
+ for (i = 1; i <= static_cast<int>(dctbl1.bits[l]); i++) {
+ huffsize[p++] = static_cast<char>(l);
+ if (p > 256)
+ ThrowRDE("LJpegDecompressor::createHuffmanTable: Code length too "
+ "long. Corrupt data.");
+ }
+ }
+ huffsize[p] = 0;
+ lastp = p;
+
+ /*
+ * Figure C.2: generate the codes themselves
+ * Note that this is in code-length order.
+ */
+ code = 0;
+ si = huffsize[0];
+ p = 0;
+ while (huffsize[p]) {
+ while ((static_cast<int>(huffsize[p])) == si) {
+ huffcode[p++] = code;
+ code++;
+ }
+ code <<= 1;
+ si++;
+ if (p > 256)
+ ThrowRDE("createHuffmanTable: Code length too long. Corrupt data.");
+ }
+
+ /*
+ * Figure F.15: generate decoding tables
+ */
+ dctbl1.mincode[0] = 0;
+ dctbl1.maxcode[0] = 0;
+ p = 0;
+ for (l = 1; l <= 16; l++) {
+ if (dctbl1.bits[l]) {
+ dctbl1.valptr[l] = p;
+ dctbl1.mincode[l] = huffcode[p];
+ p += dctbl1.bits[l];
+ dctbl1.maxcode[l] = huffcode[p - 1];
+ } else {
+ dctbl1.valptr[l] =
+ 0xff; // This check must be present to avoid crash on junk
+ dctbl1.maxcode[l] = -1;
+ }
+ if (p > 256)
+ ThrowRDE("createHuffmanTable: Code length too long. Corrupt data.");
+ }
+
+ /*
+ * We put in this value to ensure HuffDecode terminates.
+ */
+ dctbl1.maxcode[17] = 0xFFFFFL;
+
+ /*
+ * Build the numbits, value lookup tables.
+ * These table allow us to gather 8 bits from the bits stream,
+ * and immediately lookup the size and value of the huffman codes.
+ * If size is zero, it means that more than 8 bits are in the huffman
+ * code (this happens about 3-4% of the time).
+ */
+ dctbl1.numbits.fill(0);
+ for (p = 0; p < lastp; p++) {
+ size = huffsize[p];
+ if (size <= 8) {
+ value = dctbl1.huffval[p];
+ code = huffcode[p];
+ ll = code << (8 - size);
+ if (size < 8) {
+ ul = ll | bitMask[24 + size];
+ } else {
+ ul = ll;
+ }
+ if (ul > 256 || ll > ul)
+ ThrowRDE("createHuffmanTable: Code length too long. Corrupt data.");
+ for (i = ll; i <= ul; i++) {
+ dctbl1.numbits[i] = size | (value << 4);
+ }
+ }
+ }
+ if (mUseBigtable)
+ createBigTable();
+ dctbl1.initialized = true;
+ }
+
+ /************************************
+ * Bitable creation
+ *
+ * This is expanding the concept of fast lookups
+ *
+ * A complete table for 14 arbitrary bits will be
+ * created that enables fast lookup of number of bits used,
+ * and final delta result.
+ * Hit rate is about 90-99% for typical LJPEGS, usually about 98%
+ *
+ ************************************/
+
+ void createBigTable() {
+ const uint32 bits =
+ 14; // HuffDecode functions must be changed, if this is modified.
+ const uint32 size = 1 << bits;
+ int rv = 0;
+ int temp;
+ uint32 l;
+
+ dctbl1.bigTable.resize(size);
+ for (uint32 i = 0; i < size; i++) {
+ ushort16 input = i << 2; // Calculate input value
+ int code = input >> 8; // Get 8 bits
+ uint32 val = dctbl1.numbits[code];
+ l = val & 15;
+ if (l) {
+ rv = val >> 4;
+ } else {
+ l = 8;
+ while (code > dctbl1.maxcode[l]) {
+ temp = input >> (15 - l) & 1;
+ code = (code << 1) | temp;
+ l++;
+ }
+
+ /*
+ * With garbage input we may reach the sentinel value l = 17.
+ */
+
+ if (l > 16 || dctbl1.valptr[l] == 0xff) {
+ dctbl1.bigTable[i] = 0xff;
+ continue;
+ }
+ rv = dctbl1.huffval[dctbl1.valptr[l] + (code - dctbl1.mincode[l])];
+ }
+
+ if (rv == 16) {
+ if (mDNGCompatible)
+ dctbl1.bigTable[i] = (-(32768 << 8)) | (16 + l);
+ else
+ dctbl1.bigTable[i] = (-(32768 << 8)) | l;
+ continue;
+ }
+
+ if (rv + l > bits) {
+ dctbl1.bigTable[i] = 0xff;
+ continue;
+ }
+
+ if (rv) {
+ int x = input >> (16 - l - rv) & ((1 << rv) - 1);
+ if ((x & (1 << (rv - 1))) == 0)
+ x -= (1 << rv) - 1;
+ dctbl1.bigTable[i] =
+ static_cast<int>((static_cast<unsigned>(x) << 8) | (l + rv));
+ } else {
+ dctbl1.bigTable[i] = l;
+ }
+ }
+ }
+
+public:
+ uint32 setNCodesPerLength(const Buffer& data) {
+ uint32 acc = 0;
+ for (uint32 i = 0; i < 16; i++) {
+ dctbl1.bits[i + 1] = data[i];
+ acc += dctbl1.bits[i + 1];
+ }
+ dctbl1.bits[0] = 0;
+ return acc;
+ }
+
+ void setCodeValues(const Buffer& data) {
+ for (uint32 i = 0; i < data.getSize(); i++)
+ dctbl1.huffval[i] = data[i];
+ }
+
+ void setup(bool fullDecode_, bool fixDNGBug16_) { createHuffmanTable(); }
+
+ /*
+ *--------------------------------------------------------------
+ *
+ * HuffDecode --
+ *
+ * Taken from Figure F.16: extract next coded symbol from
+ * input stream. This should becode a macro.
+ *
+ * Results:
+ * Next coded symbol
+ *
+ * Side effects:
+ * Bitstream is parsed.
+ *
+ *--------------------------------------------------------------
+ */
+ int decodeNext(BitPumpMSB& bits) { // NOLINT: google-runtime-references
+ int rv;
+ int l;
+ int temp;
+ int code;
+ unsigned val;
+
+ bits.fill();
+ code = bits.peekBitsNoFill(14);
+ val = static_cast<unsigned>(dctbl1.bigTable[code]);
+ if ((val & 0xff) != 0xff) {
+ bits.skipBitsNoFill(val & 0xff);
+ return static_cast<int>(val) >> 8;
+ }
+
+ rv = 0;
+ code = bits.peekBitsNoFill(8);
+ val = dctbl1.numbits[code];
+ l = val & 15;
+ if (l) {
+ bits.skipBitsNoFill(l);
+ rv = static_cast<int>(val) >> 4;
+ } else {
+ bits.skipBits(8);
+ l = 8;
+ while (code > dctbl1.maxcode[l]) {
+ temp = bits.getBitsNoFill(1);
+ code = (code << 1) | temp;
+ l++;
+ }
+
+ if (l > 16) {
+ ThrowRDE("Corrupt JPEG data: bad Huffman code:%u\n", l);
+ } else {
+ rv = dctbl1.huffval[dctbl1.valptr[l] + (code - dctbl1.mincode[l])];
+ }
+ }
+
+ if (rv == 16)
+ return -32768;
+
+ /*
+ * Section F.2.2.1: decode the difference and
+ * Figure F.12: extend sign bit
+ */
+ uint32 len = rv & 15;
+ uint32 shl = rv >> 4;
+ int diff = ((bits.getBits(len - shl) << 1) + 1) << shl >> 1;
+ if ((diff & (1 << (len - 1))) == 0)
+ diff -= (1 << len) - !shl;
+ return diff;
+ }
+};
+
+} // namespace
+
+std::vector<ushort16> NikonDecompressor::createCurve(ByteStream* metadata,
+ uint32 bitsPS, uint32 v0,
+ uint32 v1, uint32* split) {
+ // Nikon Z7 12/14 bit compressed hack.
+ if (v0 == 68 && v1 == 64)
+ bitsPS -= 2;
+
+ // 'curve' will hold a peace wise linearly interpolated function.
+ // there are 'csize' segments, each is 'step' values long.
+ // the very last value is not part of the used table but necessary
+ // to linearly interpolate the last segment, therefore the '+1/-1'
+ // size adjustments of 'curve'.
+ std::vector<ushort16> curve((1 << bitsPS & 0x7fff) + 1);
+ assert(curve.size() > 1);
+
+ for (size_t i = 0; i < curve.size(); i++)
+ curve[i] = i;
+
+ uint32 step = 0;
+ uint32 csize = metadata->getU16();
+ if (csize > 1)
+ step = curve.size() / (csize - 1);
+
+ if (v0 == 68 && (v1 == 32 || v1 == 64) && step > 0) {
+ if ((csize - 1) * step != curve.size() - 1)
+ ThrowRDE("Bad curve segment count (%u)", csize);
+
+ for (size_t i = 0; i < csize; i++)
+ curve[i * step] = metadata->getU16();
+ for (size_t i = 0; i < curve.size() - 1; i++) {
+ const uint32 b_scale = i % step;
+
+ const uint32 a_pos = i - b_scale;
+ const uint32 b_pos = a_pos + step;
+ assert(a_pos < curve.size());
+ assert(b_pos > 0);
+ assert(b_pos < curve.size());
+ assert(a_pos < b_pos);
+
+ const uint32 a_scale = step - b_scale;
+ curve[i] = (a_scale * curve[a_pos] + b_scale * curve[b_pos]) / step;
+ }
+
+ metadata->setPosition(562);
+ *split = metadata->getU16();
+ } else if (v0 != 70) {
+ if (csize == 0 || csize > 0x4001)
+ ThrowRDE("Don't know how to compute curve! csize = %u", csize);
+
+ curve.resize(csize + 1UL);
+ assert(curve.size() > 1);
+
+ for (uint32 i = 0; i < csize; i++) {
+ curve[i] = metadata->getU16();
+ }
+ }
+
+ // and drop the last value
+ curve.resize(curve.size() - 1);
+ assert(!curve.empty());
+
+ return curve;
+}
+
+template <typename Huffman>
+Huffman NikonDecompressor::createHuffmanTable(uint32 huffSelect) {
+ Huffman ht;
+ uint32 count =
+ ht.setNCodesPerLength(Buffer(nikon_tree[huffSelect][0].data(), 16));
+ ht.setCodeValues(Buffer(nikon_tree[huffSelect][1].data(), count));
+ ht.setup(true, false);
+ return ht;
+}
+
+NikonDecompressor::NikonDecompressor(const RawImage& raw, ByteStream metadata,
+ uint32 bitsPS_)
+ : mRaw(raw), bitsPS(bitsPS_) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ if (mRaw->dim.x == 0 || mRaw->dim.y == 0 || mRaw->dim.x % 2 != 0 ||
+ mRaw->dim.x > 8288 || mRaw->dim.y > 5520)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+
+ switch (bitsPS) {
+ case 12:
+ case 14:
+ break;
+ default:
+ ThrowRDE("Invalid bpp found: %u", bitsPS);
+ }
+
+ uint32 v0 = metadata.getByte();
+ uint32 v1 = metadata.getByte();
+
+ writeLog(DEBUG_PRIO_EXTRA, "Nef version v0:%u, v1:%u", v0, v1);
+
+ if (v0 == 73 || v1 == 88)
+ metadata.skipBytes(2110);
+
+ if (v0 == 70)
+ huffSelect = 2;
+ if (bitsPS == 14)
+ huffSelect += 3;
+
+ pUp1[0] = metadata.getU16();
+ pUp1[1] = metadata.getU16();
+ pUp2[0] = metadata.getU16();
+ pUp2[1] = metadata.getU16();
+
+ curve = createCurve(&metadata, bitsPS, v0, v1, &split);
+
+ // If the 'split' happens outside of the image, it does not actually happen.
+ if (split >= static_cast<unsigned>(mRaw->dim.y))
+ split = 0;
+}
+
+template <typename Huffman>
+void NikonDecompressor::decompress(BitPumpMSB* bits, int start_y, int end_y) {
+ Huffman ht = createHuffmanTable<Huffman>(huffSelect);
+
+ uchar8* draw = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+
+ int pLeft1 = 0;
+ int pLeft2 = 0;
+
+ // allow gcc to devirtualize the calls below
+ auto* rawdata = reinterpret_cast<RawImageDataU16*>(mRaw.get());
+
+ const iPoint2D& size = mRaw->dim;
+ assert(size.x % 2 == 0);
+ assert(size.x >= 2);
+ for (uint32 y = start_y; y < static_cast<uint32>(end_y); y++) {
+ auto* dest =
+ reinterpret_cast<ushort16*>(&draw[y * pitch]); // Adjust destination
+ pUp1[y & 1] += ht.decodeNext(*bits);
+ pUp2[y & 1] += ht.decodeNext(*bits);
+ pLeft1 = pUp1[y & 1];
+ pLeft2 = pUp2[y & 1];
+
+ rawdata->setWithLookUp(clampBits(pLeft1, 15),
+ reinterpret_cast<uchar8*>(dest + 0), &random);
+ rawdata->setWithLookUp(clampBits(pLeft2, 15),
+ reinterpret_cast<uchar8*>(dest + 1), &random);
+
+ dest += 2;
+
+ for (uint32 x = 2; x < static_cast<uint32>(size.x); x += 2) {
+ pLeft1 += ht.decodeNext(*bits);
+ pLeft2 += ht.decodeNext(*bits);
+
+ rawdata->setWithLookUp(clampBits(pLeft1, 15),
+ reinterpret_cast<uchar8*>(dest + 0), &random);
+ rawdata->setWithLookUp(clampBits(pLeft2, 15),
+ reinterpret_cast<uchar8*>(dest + 1), &random);
+
+ dest += 2;
+ }
+ }
+}
+
+void NikonDecompressor::decompress(const ByteStream& data,
+ bool uncorrectedRawValues) {
+ RawImageCurveGuard curveHandler(&mRaw, curve, uncorrectedRawValues);
+
+ BitPumpMSB bits(data);
+
+ random = bits.peekBits(24);
+
+ assert(split == 0 || split < static_cast<unsigned>(mRaw->dim.y));
+
+ if (!split) {
+ decompress<HuffmanTable>(&bits, 0, mRaw->dim.y);
+ } else {
+ decompress<HuffmanTable>(&bits, 0, split);
+ huffSelect += 1;
+ decompress<NikonLASDecompressor>(&bits, split, mRaw->dim.y);
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.h
new file mode 100644
index 00000000..cfa1bb0e
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/NikonDecompressor.h
@@ -0,0 +1,64 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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, ushort16
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class ByteStream;
+
+class NikonDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+ uint32 bitsPS;
+
+ uint32 huffSelect = 0;
+ uint32 split = 0;
+
+ std::array<int, 2> pUp1;
+ std::array<int, 2> pUp2;
+
+ std::vector<ushort16> curve;
+
+ uint32 random;
+
+public:
+ NikonDecompressor(const RawImage& raw, ByteStream metadata, uint32 bitsPS);
+
+ void decompress(const ByteStream& data, bool uncorrectedRawValues);
+
+private:
+ static const std::array<std::array<std::array<uchar8, 16>, 2>, 6> nikon_tree;
+ static std::vector<ushort16> createCurve(ByteStream* metadata, uint32 bitsPS,
+ uint32 v0, uint32 v1, uint32* split);
+
+ template <typename Huffman>
+ void decompress(BitPumpMSB* bits, int start_y, int end_y);
+
+ template <typename Huffman>
+ static Huffman createHuffmanTable(uint32 huffSelect);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.cpp
new file mode 100644
index 00000000..b9bc3022
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.cpp
@@ -0,0 +1,174 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ 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
+ 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/OlympusDecompressor.h"
+#include "common/Common.h" // for uint32, ushort16, uchar8
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include "io/ByteStream.h" // for ByteStream
+#include <algorithm> // for min
+#include <array> // for array, array<>::value_type
+#include <cassert> // for assert
+#include <cmath> // for abs
+#include <cstdlib> // for abs
+#include <type_traits> // for enable_if_t, is_integral
+
+namespace {
+
+// Normally, we'd just use std::signbit(int) here. But, some (non-conforming?)
+// compilers do not provide that overload, so the code simply fails to compile.
+// One could cast the int to the double, but at least right now that results
+// in a horrible code. So let's just provide our own signbit(). It compiles to
+// the exact same code as the std::signbit(int).
+template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
+inline constexpr __attribute__((const)) bool SignBit(T x) {
+ return x < 0;
+}
+
+} // namespace
+
+namespace rawspeed {
+
+OlympusDecompressor::OlympusDecompressor(const RawImage& img) : mRaw(img) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const uint32 w = mRaw->dim.x;
+ const uint32 h = mRaw->dim.y;
+
+ if (w == 0 || h == 0 || w % 2 != 0 || w > 10400 || h > 7792)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", w, h);
+}
+
+/* This is probably the slowest decoder of them all.
+ * I cannot see any way to effectively speed up the prediction
+ * phase, which is by far the slowest part of this algorithm.
+ * Also there is no way to multithread this code, since prediction
+ * is based on the output of all previous pixel (bar the first four)
+ */
+
+inline __attribute__((always_inline)) int
+OlympusDecompressor::parseCarry(BitPumpMSB* bits,
+ std::array<int, 3>* carry) const {
+ bits->fill();
+ int i = 2 * ((*carry)[2] < 3);
+ int nbits;
+ for (nbits = 2 + i; static_cast<ushort16>((*carry)[0]) >> (nbits + i);
+ nbits++)
+ ;
+
+ int b = bits->peekBitsNoFill(15);
+ int sign = (b >> 14) * -1;
+ int low = (b >> 12) & 3;
+ int high = bittable[b & 4095];
+
+ // Skip bytes used above or read bits
+ if (high == 12) {
+ bits->skipBitsNoFill(15);
+ high = bits->getBitsNoFill(16 - nbits) >> 1;
+ } else
+ bits->skipBitsNoFill(high + 1 + 3);
+
+ (*carry)[0] = (high << nbits) | bits->getBitsNoFill(nbits);
+ int diff = ((*carry)[0] ^ sign) + (*carry)[1];
+ (*carry)[1] = (diff * 3 + (*carry)[1]) >> 5;
+ (*carry)[2] = (*carry)[0] > 16 ? 0 : (*carry)[2] + 1;
+
+ return (diff * 4) | low;
+}
+
+inline int OlympusDecompressor::getPred(int row, int x, ushort16* dest,
+ const ushort16* up_ptr) const {
+ auto getLeft = [dest]() { return dest[-2]; };
+ auto getUp = [up_ptr]() { return up_ptr[0]; };
+ auto getLeftUp = [up_ptr]() { return up_ptr[-2]; };
+
+ int pred;
+ if (row < 2 && x < 2)
+ pred = 0;
+ else if (row < 2)
+ pred = getLeft();
+ else if (x < 2)
+ pred = getUp();
+ else {
+ int left = getLeft();
+ int up = getUp();
+ int leftUp = getLeftUp();
+
+ int leftMinusNw = left - leftUp;
+ int upMinusNw = up - leftUp;
+
+ // Check if sign is different, and they are both not zero
+ if ((SignBit(leftMinusNw) ^ SignBit(upMinusNw)) &&
+ (leftMinusNw != 0 && upMinusNw != 0)) {
+ if (std::abs(leftMinusNw) > 32 || std::abs(upMinusNw) > 32)
+ pred = left + upMinusNw;
+ else
+ pred = (left + up) >> 1;
+ } else
+ pred = std::abs(leftMinusNw) > std::abs(upMinusNw) ? left : up;
+ }
+
+ return pred;
+}
+
+void OlympusDecompressor::decompressRow(BitPumpMSB* bits, int row) const {
+ assert(mRaw->dim.y > 0);
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.x % 2 == 0);
+
+ int pitch = mRaw->pitch;
+
+ std::array<std::array<int, 3>, 2> acarry{{}};
+
+ auto* dest = reinterpret_cast<ushort16*>(mRaw->getData(0, row));
+ const auto* up_ptr = row > 0 ? &dest[-pitch] : &dest[0];
+ for (int x = 0; x < mRaw->dim.x; x++) {
+ int c = x & 1;
+
+ std::array<int, 3>& carry = acarry[c];
+
+ int diff = parseCarry(bits, &carry);
+ int pred = getPred(row, x, dest, up_ptr);
+
+ *dest = pred + diff;
+ dest++;
+ up_ptr++;
+ }
+}
+
+void OlympusDecompressor::decompress(ByteStream input) const {
+ assert(mRaw->dim.y > 0);
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.x % 2 == 0);
+
+ input.skipBytes(7);
+ BitPumpMSB bits(input);
+
+ for (int y = 0; y < mRaw->dim.y; y++)
+ decompressRow(&bits, y);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.h
new file mode 100644
index 00000000..05cd92c4
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/OlympusDecompressor.h
@@ -0,0 +1,59 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/RawImage.h" // for RawImage
+#include "common/SimpleLUT.h" // for SimpleLUT
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include <array> // for array, array<>::value...
+
+namespace rawspeed {
+
+class ByteStream;
+
+class OlympusDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+
+ // A table to quickly look up "high" value
+ const SimpleLUT<char, 12> bittable{[](unsigned i, unsigned tableSize) {
+ int b = i;
+ int high;
+ for (high = 0; high < 12; high++)
+ if ((b >> (11 - high)) & 1)
+ break;
+ return std::min(12, high);
+ }};
+
+ inline __attribute__((always_inline)) int
+ parseCarry(BitPumpMSB* bits, std::array<int, 3>* carry) const;
+
+ inline int getPred(int row, int x, ushort16* dest,
+ const ushort16* up_ptr) const;
+
+ void decompressRow(BitPumpMSB* bits, int row) const;
+
+public:
+ explicit OlympusDecompressor(const RawImage& img);
+ void decompress(ByteStream input) const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.cpp
new file mode 100644
index 00000000..16e271a3
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.cpp
@@ -0,0 +1,268 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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
+ 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 "rawspeedconfig.h"
+#include "decompressors/PanasonicDecompressor.h"
+#include "common/Mutex.h" // for MutexLocker
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/Buffer.h" // for Buffer, Buffer::size_type
+#include <algorithm> // for generate_n, min
+#include <array> // for array
+#include <cassert> // for assert
+#include <cstddef> // for size_t
+#include <iterator> // for back_insert_iterator, back...
+#include <limits> // for numeric_limits
+#include <memory> // for allocator_traits<>::value_...
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+constexpr uint32 PanasonicDecompressor::BlockSize;
+
+PanasonicDecompressor::PanasonicDecompressor(const RawImage& img,
+ const ByteStream& input_,
+ bool zero_is_not_bad,
+ uint32 section_split_offset_)
+ : mRaw(img), zero_is_bad(!zero_is_not_bad),
+ section_split_offset(section_split_offset_) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ if (!mRaw->dim.hasPositiveArea() || mRaw->dim.x % PixelsPerPacket != 0) {
+ ThrowRDE("Unexpected image dimensions found: (%i; %i)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+
+ if (BlockSize < section_split_offset)
+ ThrowRDE("Bad section_split_offset: %u, less than BlockSize (%u)",
+ section_split_offset, BlockSize);
+
+ // Naive count of bytes that given pixel count requires.
+ assert(mRaw->dim.area() % PixelsPerPacket == 0);
+ const auto bytesTotal = (mRaw->dim.area() / PixelsPerPacket) * BytesPerPacket;
+ assert(bytesTotal > 0);
+
+ // If section_split_offset is zero, then that we need to read the normal
+ // amount of bytes. But if it is not, then we need to round up to multiple of
+ // BlockSize, because of splitting&rotation of each BlockSize's slice in half
+ // at section_split_offset bytes.
+ const auto bufSize =
+ section_split_offset == 0 ? bytesTotal : roundUp(bytesTotal, BlockSize);
+
+ if (bufSize > std::numeric_limits<ByteStream::size_type>::max())
+ ThrowRDE("Raw dimensions require input buffer larger than supported");
+
+ input = input_.peekStream(bufSize);
+
+ chopInputIntoBlocks();
+}
+
+void PanasonicDecompressor::chopInputIntoBlocks() {
+ auto pixelToCoordinate = [width = mRaw->dim.x](unsigned pixel) {
+ return iPoint2D(pixel % width, pixel / width);
+ };
+
+ // If section_split_offset == 0, last block may not be full.
+ const auto blocksTotal = roundUpDivision(input.getRemainSize(), BlockSize);
+ assert(blocksTotal > 0);
+ assert(blocksTotal * PixelsPerBlock >= mRaw->dim.area());
+ blocks.reserve(blocksTotal);
+
+ unsigned currPixel = 0;
+ std::generate_n(std::back_inserter(blocks), blocksTotal,
+ [input = &input, &currPixel, pixelToCoordinate]() -> Block {
+ assert(input->getRemainSize() != 0);
+ const auto blockSize =
+ std::min(input->getRemainSize(), BlockSize);
+ assert(blockSize > 0);
+ assert(blockSize % BytesPerPacket == 0);
+ const auto packets = blockSize / BytesPerPacket;
+ assert(packets > 0);
+ const auto pixels = packets * PixelsPerPacket;
+ assert(pixels > 0);
+
+ ByteStream bs = input->getStream(blockSize);
+ iPoint2D beginCoord = pixelToCoordinate(currPixel);
+ currPixel += pixels;
+ iPoint2D endCoord = pixelToCoordinate(currPixel);
+ return {std::move(bs), beginCoord, endCoord};
+ });
+ assert(blocks.size() == blocksTotal);
+ assert(currPixel >= mRaw->dim.area());
+ assert(input.getRemainSize() == 0);
+
+ // Clamp the end coordinate for the last block.
+ blocks.back().endCoord = mRaw->dim;
+ blocks.back().endCoord.y -= 1;
+}
+
+class PanasonicDecompressor::ProxyStream {
+ ByteStream block;
+ const uint32 section_split_offset;
+ std::vector<uchar8> buf;
+
+ int vbits = 0;
+
+ void parseBlock() {
+ assert(buf.empty());
+ assert(block.getRemainSize() <= BlockSize);
+ assert(section_split_offset <= BlockSize);
+
+ Buffer FirstSection = block.getBuffer(section_split_offset);
+ Buffer SecondSection = block.getBuffer(block.getRemainSize());
+
+ // get one more byte, so the return statement of getBits does not have
+ // to special case for accessing the last byte
+ buf.reserve(BlockSize + 1UL);
+
+ // First copy the second section. This makes it the first section.
+ buf.insert(buf.end(), SecondSection.begin(), SecondSection.end());
+ // Now append the original 1'st section right after the new 1'st section.
+ buf.insert(buf.end(), FirstSection.begin(), FirstSection.end());
+
+ assert(block.getRemainSize() == 0);
+
+ // get one more byte, so the return statement of getBits does not have
+ // to special case for accessing the last byte
+ buf.emplace_back(0);
+ }
+
+public:
+ ProxyStream(ByteStream block_, int section_split_offset_)
+ : block(std::move(block_)), section_split_offset(section_split_offset_) {
+ parseBlock();
+ }
+
+ uint32 getBits(int nbits) noexcept {
+ vbits = (vbits - nbits) & 0x1ffff;
+ int byte = vbits >> 3 ^ 0x3ff0;
+ return (buf[byte] | buf[byte + 1UL] << 8) >> (vbits & 7) & ~(-(1 << nbits));
+ }
+};
+
+void PanasonicDecompressor::processPixelPacket(
+ ProxyStream* bits, int y, ushort16* dest, int xbegin,
+ std::vector<uint32>* zero_pos) const noexcept {
+ int sh = 0;
+
+ std::array<int, 2> pred;
+ pred.fill(0);
+
+ std::array<int, 2> nonz;
+ nonz.fill(0);
+
+ int u = 0;
+
+ for (int p = 0; p < PixelsPerPacket; p++) {
+ const int c = p & 1;
+
+ if (u == 2) {
+ sh = 4 >> (3 - bits->getBits(2));
+ u = -1;
+ }
+
+ if (nonz[c]) {
+ int j = bits->getBits(8);
+ if (j) {
+ pred[c] -= 0x80 << sh;
+ if (pred[c] < 0 || sh == 4)
+ pred[c] &= ~(-(1 << sh));
+ pred[c] += j << sh;
+ }
+ } else {
+ nonz[c] = bits->getBits(8);
+ if (nonz[c] || p > 11)
+ pred[c] = nonz[c] << 4 | bits->getBits(4);
+ }
+
+ *dest = pred[c];
+
+ if (zero_is_bad && 0 == pred[c])
+ zero_pos->push_back((y << 16) | (xbegin + p));
+
+ u++;
+ dest++;
+ }
+}
+
+void PanasonicDecompressor::processBlock(const Block& block,
+ std::vector<uint32>* zero_pos) const
+ noexcept {
+ ProxyStream bits(block.bs, section_split_offset);
+
+ for (int y = block.beginCoord.y; y <= block.endCoord.y; y++) {
+ int x = 0;
+ // First row may not begin at the first column.
+ if (block.beginCoord.y == y)
+ x = block.beginCoord.x;
+
+ int endx = mRaw->dim.x;
+ // Last row may end before the last column.
+ if (block.endCoord.y == y)
+ endx = block.endCoord.x;
+
+ auto* dest = reinterpret_cast<ushort16*>(mRaw->getData(x, y));
+
+ assert(x % PixelsPerPacket == 0);
+ assert(endx % PixelsPerPacket == 0);
+
+ for (; x < endx;) {
+ processPixelPacket(&bits, y, dest, x, zero_pos);
+
+ x += PixelsPerPacket;
+ dest += PixelsPerPacket;
+ }
+ }
+}
+
+void PanasonicDecompressor::decompressThread() const noexcept {
+ std::vector<uint32> zero_pos;
+
+ assert(!blocks.empty());
+
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto block = blocks.cbegin(); block < blocks.cend(); ++block)
+ processBlock(*block, &zero_pos);
+
+ if (zero_is_bad && !zero_pos.empty()) {
+ MutexLocker guard(&mRaw->mBadPixelMutex);
+ mRaw->mBadPixelPositions.insert(mRaw->mBadPixelPositions.end(),
+ zero_pos.begin(), zero_pos.end());
+ }
+}
+
+void PanasonicDecompressor::decompress() const noexcept {
+ assert(!blocks.empty());
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) \
+ num_threads(rawspeed_get_number_of_processor_cores())
+#endif
+ decompressThread();
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.h
new file mode 100644
index 00000000..17855056
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressor.h
@@ -0,0 +1,91 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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 "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class PanasonicDecompressor final : public AbstractDecompressor {
+ static constexpr uint32 BlockSize = 0x4000;
+
+ static constexpr int PixelsPerPacket = 14;
+
+ static constexpr uint32 BytesPerPacket = 16;
+
+ static constexpr uint32 PacketsPerBlock = BlockSize / BytesPerPacket;
+
+ static constexpr uint32 PixelsPerBlock = PixelsPerPacket * PacketsPerBlock;
+
+ class ProxyStream;
+
+ RawImage mRaw;
+ ByteStream input;
+ bool zero_is_bad;
+
+ // The RW2 raw image buffer is split into sections of BufSize bytes.
+ // If section_split_offset is 0, then the last section is not neccesarily
+ // full. If section_split_offset is not 0, then each section has two parts:
+ // bytes: [0..section_split_offset-1][section_split_offset..BufSize-1]
+ // pixels: [a..b][0..a-1]
+ // I.e. these two parts need to be swapped around.
+ uint32 section_split_offset;
+
+ struct Block {
+ ByteStream bs;
+ iPoint2D beginCoord;
+ // The rectangle is an incorrect representation. All the rows
+ // between the first and last one span the entire width of the image.
+ iPoint2D endCoord;
+
+ Block() = default;
+ Block(ByteStream&& bs_, iPoint2D beginCoord_, iPoint2D endCoord_)
+ : bs(std::move(bs_)), beginCoord(beginCoord_), endCoord(endCoord_) {}
+ };
+
+ // If really wanted, this vector could be avoided,
+ // and each Block computed on-the-fly
+ std::vector<Block> blocks;
+
+ void chopInputIntoBlocks();
+
+ void processPixelPacket(ProxyStream* bits, int y, ushort16* dest, int xbegin,
+ std::vector<uint32>* zero_pos) const noexcept;
+
+ void processBlock(const Block& block, std::vector<uint32>* zero_pos) const
+ noexcept;
+
+ void decompressThread() const noexcept;
+
+public:
+ PanasonicDecompressor(const RawImage& img, const ByteStream& input_,
+ bool zero_is_not_bad, uint32 section_split_offset_);
+
+ void decompress() const noexcept;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.cpp
new file mode 100644
index 00000000..7686849f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.cpp
@@ -0,0 +1,252 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Alexey Danilchenko
+ Copyright (C) 2018 Stefan Hoffmeister
+ 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
+*/
+
+#include "rawspeedconfig.h"
+#include "decompressors/PanasonicDecompressorV5.h"
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpLSB.h" // for BitPumpLSB
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include <algorithm> // for generate_n
+#include <cassert> // for assert
+#include <iterator> // for back_insert_iterator, back...
+#include <memory> // for allocator_traits<>::value_...
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+struct PanasonicDecompressorV5::PacketDsc {
+ int bps;
+ int pixelsPerPacket;
+
+ constexpr PacketDsc();
+ explicit constexpr PacketDsc(int bps_)
+ : bps(bps_),
+ pixelsPerPacket(PanasonicDecompressorV5::bitsPerPacket / bps) {
+ // NOTE: the division is truncating. There may be some padding bits left.
+ }
+};
+
+constexpr PanasonicDecompressorV5::PacketDsc
+ PanasonicDecompressorV5::TwelveBitPacket =
+ PanasonicDecompressorV5::PacketDsc(/*bps=*/12);
+constexpr PanasonicDecompressorV5::PacketDsc
+ PanasonicDecompressorV5::FourteenBitPacket =
+ PanasonicDecompressorV5::PacketDsc(/*bps=*/14);
+
+PanasonicDecompressorV5::PanasonicDecompressorV5(const RawImage& img,
+ const ByteStream& input_,
+ uint32 bps_)
+ : mRaw(img), bps(bps_) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const PacketDsc* dsc = nullptr;
+ switch (bps) {
+ case 12:
+ dsc = &TwelveBitPacket;
+ break;
+ case 14:
+ dsc = &FourteenBitPacket;
+ break;
+ default:
+ ThrowRDE("Unsupported bps: %u", bps);
+ }
+
+ if (!mRaw->dim.hasPositiveArea() || mRaw->dim.x % dsc->pixelsPerPacket != 0) {
+ ThrowRDE("Unexpected image dimensions found: (%i; %i)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+
+ // How many pixel packets does the specified pixel count require?
+ assert(mRaw->dim.area() % dsc->pixelsPerPacket == 0);
+ const auto numPackets = mRaw->dim.area() / dsc->pixelsPerPacket;
+ assert(numPackets > 0);
+
+ // And how many blocks that would be? Last block may not be full, pad it.
+ numBlocks = roundUpDivision(numPackets, PacketsPerBlock);
+ assert(numBlocks > 0);
+
+ // How many full blocks does the input contain? This is truncating division.
+ const auto haveBlocks = input_.getRemainSize() / BlockSize;
+
+ // Does the input contain enough blocks?
+ if (haveBlocks < numBlocks)
+ ThrowRDE("Unsufficient count of input blocks for a given image");
+
+ // We only want those blocks we need, no extras.
+ input = input_.peekStream(numBlocks, BlockSize);
+
+ chopInputIntoBlocks(*dsc);
+}
+
+void PanasonicDecompressorV5::chopInputIntoBlocks(const PacketDsc& dsc) {
+ auto pixelToCoordinate = [width = mRaw->dim.x](unsigned pixel) {
+ return iPoint2D(pixel % width, pixel / width);
+ };
+
+ assert(numBlocks * BlockSize == input.getRemainSize());
+ blocks.reserve(numBlocks);
+
+ const auto pixelsPerBlock = dsc.pixelsPerPacket * PacketsPerBlock;
+ assert((numBlocks - 1U) * pixelsPerBlock < mRaw->dim.area());
+ assert(numBlocks * pixelsPerBlock >= mRaw->dim.area());
+
+ unsigned currPixel = 0;
+ std::generate_n(std::back_inserter(blocks), numBlocks,
+ [input = &input, &currPixel, pixelToCoordinate,
+ pixelsPerBlock]() -> Block {
+ ByteStream bs = input->getStream(BlockSize);
+ iPoint2D beginCoord = pixelToCoordinate(currPixel);
+ currPixel += pixelsPerBlock;
+ iPoint2D endCoord = pixelToCoordinate(currPixel);
+ return {std::move(bs), beginCoord, endCoord};
+ });
+ assert(blocks.size() == numBlocks);
+ assert(currPixel >= mRaw->dim.area());
+ assert(input.getRemainSize() == 0);
+
+ // Clamp the end coordinate for the last block.
+ blocks.back().endCoord = mRaw->dim;
+ blocks.back().endCoord.y -= 1;
+}
+
+class PanasonicDecompressorV5::ProxyStream {
+ ByteStream block;
+ std::vector<uchar8> buf;
+ ByteStream input;
+
+ void parseBlock() {
+ assert(buf.empty());
+ assert(block.getRemainSize() == BlockSize);
+
+ static_assert(BlockSize > sectionSplitOffset, "");
+
+ Buffer FirstSection = block.getBuffer(sectionSplitOffset);
+ Buffer SecondSection = block.getBuffer(block.getRemainSize());
+ assert(FirstSection.getSize() < SecondSection.getSize());
+
+ buf.reserve(BlockSize);
+
+ // First copy the second section. This makes it the first section.
+ buf.insert(buf.end(), SecondSection.begin(), SecondSection.end());
+ // Now append the original 1'st section right after the new 1'st section.
+ buf.insert(buf.end(), FirstSection.begin(), FirstSection.end());
+
+ assert(buf.size() == BlockSize);
+ assert(block.getRemainSize() == 0);
+
+ // And reset the clock.
+ input = ByteStream(DataBuffer(Buffer(buf.data(), buf.size())));
+ // input.setByteOrder(Endianness::big); // does not seem to matter?!
+ }
+
+public:
+ explicit ProxyStream(ByteStream block_) : block(std::move(block_)) {}
+
+ ByteStream& getStream() {
+ parseBlock();
+ return input;
+ }
+};
+
+template <const PanasonicDecompressorV5::PacketDsc& dsc>
+void PanasonicDecompressorV5::processPixelPacket(BitPumpLSB* bs,
+ ushort16* dest) const {
+ static_assert(dsc.pixelsPerPacket > 0, "dsc should be compile-time const");
+ static_assert(dsc.bps > 0 && dsc.bps <= 16, "");
+
+ assert(bs->getFillLevel() == 0);
+
+ const ushort16* const endDest = dest + dsc.pixelsPerPacket;
+ for (; dest != endDest;) {
+ bs->fill();
+ for (; bs->getFillLevel() >= dsc.bps; dest++) {
+ assert(dest != endDest);
+
+ *dest = bs->getBitsNoFill(dsc.bps);
+ }
+ }
+ bs->skipBitsNoFill(bs->getFillLevel()); // get rid of padding.
+}
+
+template <const PanasonicDecompressorV5::PacketDsc& dsc>
+void PanasonicDecompressorV5::processBlock(const Block& block) const {
+ static_assert(dsc.pixelsPerPacket > 0, "dsc should be compile-time const");
+ static_assert(BlockSize % bytesPerPacket == 0, "");
+
+ ProxyStream proxy(block.bs);
+ BitPumpLSB bs(proxy.getStream());
+
+ for (int y = block.beginCoord.y; y <= block.endCoord.y; y++) {
+ int x = 0;
+ // First row may not begin at the first column.
+ if (block.beginCoord.y == y)
+ x = block.beginCoord.x;
+
+ int endx = mRaw->dim.x;
+ // Last row may end before the last column.
+ if (block.endCoord.y == y)
+ endx = block.endCoord.x;
+
+ auto* dest = reinterpret_cast<ushort16*>(mRaw->getData(x, y));
+
+ assert(x % dsc.pixelsPerPacket == 0);
+ assert(endx % dsc.pixelsPerPacket == 0);
+
+ for (; x < endx;) {
+ processPixelPacket<dsc>(&bs, dest);
+
+ x += dsc.pixelsPerPacket;
+ dest += dsc.pixelsPerPacket;
+ }
+ }
+}
+
+template <const PanasonicDecompressorV5::PacketDsc& dsc>
+void PanasonicDecompressorV5::decompressInternal() const noexcept {
+#ifdef HAVE_OPENMP
+ // NOLINTNEXTLINE(openmp-exception-escape): we have checked size already.
+#pragma omp parallel for num_threads(rawspeed_get_number_of_processor_cores()) \
+ schedule(static) default(none)
+#endif
+ for (auto block = blocks.cbegin(); block < blocks.cend(); ++block)
+ processBlock<dsc>(*block);
+}
+
+void PanasonicDecompressorV5::decompress() const noexcept {
+ switch (bps) {
+ case 12:
+ decompressInternal<TwelveBitPacket>();
+ break;
+ case 14:
+ decompressInternal<FourteenBitPacket>();
+ break;
+ default:
+ __builtin_unreachable();
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.h
b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.h
new file mode 100644
index 00000000..64a64927
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PanasonicDecompressorV5.h
@@ -0,0 +1,107 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Roman Lebedev
+ Copyright (C) 2018 Stefan Hoffmeister
+
+ 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 "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/BitPumpLSB.h" // for BitPumpLSB
+#include "io/ByteStream.h" // for ByteStream
+#include <cstddef> // for size_t
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class PanasonicDecompressorV5 final : public AbstractDecompressor {
+ // The RW2 raw image buffer consists of individual blocks,
+ // each one BlockSize bytes in size.
+ static constexpr uint32 BlockSize = 0x4000;
+
+ // These blocks themselves comprise of two sections,
+ // split and swapped at section_split_offset:
+ // bytes: [0..sectionSplitOffset-1][sectionSplitOffset..BlockSize-1]
+ // pixels: [a..b][0..a-1]
+ // When reading, these two sections need to be swapped to enable linear
+ // processing..
+ static constexpr uint32 sectionSplitOffset = 0x1FF8;
+
+ // The blocks themselves consist of packets with fixed size of bytesPerPacket,
+ // and each packet decodes to pixelsPerPacket pixels, which depends on bps.
+ static constexpr uint32 bytesPerPacket = 16;
+ static constexpr uint32 bitsPerPacket = 8 * bytesPerPacket;
+ static_assert(BlockSize % bytesPerPacket == 0, "");
+ static constexpr uint32 PacketsPerBlock = BlockSize / bytesPerPacket;
+
+ // Contains the decoding recepie for the packet,
+ struct PacketDsc;
+
+ // There are two variants. Which one is to be used depends on image's bps.
+ static const PacketDsc TwelveBitPacket;
+ static const PacketDsc FourteenBitPacket;
+
+ // Takes care of unsplitting&swapping back the block at sectionSplitOffset.
+ class ProxyStream;
+
+ RawImage mRaw;
+
+ // The full input buffer, containing all the blocks.
+ ByteStream input;
+
+ const uint32 bps;
+
+ size_t numBlocks;
+
+ struct Block {
+ ByteStream bs;
+ iPoint2D beginCoord;
+ // The rectangle is an incorrect representation. All the rows
+ // between the first and last one span the entire width of the image.
+ iPoint2D endCoord;
+
+ Block() = default;
+ Block(ByteStream&& bs_, iPoint2D beginCoord_, iPoint2D endCoord_)
+ : bs(std::move(bs_)), beginCoord(beginCoord_), endCoord(endCoord_) {}
+ };
+
+ // If really wanted, this vector could be avoided,
+ // and each Block computed on-the-fly
+ std::vector<Block> blocks;
+
+ void chopInputIntoBlocks(const PacketDsc& dsc);
+
+ template <const PacketDsc& dsc>
+ void processPixelPacket(BitPumpLSB* bs, ushort16* dest) const;
+
+ template <const PacketDsc& dsc> void processBlock(const Block& block) const;
+
+ template <const PacketDsc& dsc> void decompressInternal() const noexcept;
+
+public:
+ PanasonicDecompressorV5(const RawImage& img, const ByteStream& input_,
+ uint32 bps_);
+
+ void decompress() const noexcept;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.cpp
new file mode 100644
index 00000000..ef4a3824
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.cpp
@@ -0,0 +1,174 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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/PentaxDecompressor.h"
+#include "common/Common.h" // for uint32, uchar8, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpMSB.h" // for BitPumpMSB, BitStream<>::f...
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include <cassert> // for assert
+#include <vector> // for vector
+
+namespace rawspeed {
+
+// 16 entries of codes per bit length
+// 13 entries of code values
+const std::array<std::array<std::array<uchar8, 16>, 2>, 1>
+ PentaxDecompressor::pentax_tree = {{
+ {{{0, 2, 3, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0},
+ {3, 4, 2, 5, 1, 6, 0, 7, 8, 9, 10, 11, 12}}},
+ }};
+
+PentaxDecompressor::PentaxDecompressor(const RawImage& img,
+ ByteStream* metaData)
+ : mRaw(img), ht(SetupHuffmanTable(metaData)) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ if (!mRaw->dim.x || !mRaw->dim.y || mRaw->dim.x % 2 != 0 ||
+ mRaw->dim.x > 8384 || mRaw->dim.y > 6208) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+}
+
+HuffmanTable PentaxDecompressor::SetupHuffmanTable_Legacy() {
+ HuffmanTable ht;
+
+ /* Initialize with legacy data */
+ auto nCodes = ht.setNCodesPerLength(Buffer(pentax_tree[0][0].data(), 16));
+ assert(nCodes == 13); // see pentax_tree definition
+ ht.setCodeValues(Buffer(pentax_tree[0][1].data(), nCodes));
+
+ return ht;
+}
+
+HuffmanTable PentaxDecompressor::SetupHuffmanTable_Modern(ByteStream stream) {
+ HuffmanTable ht;
+
+ const uint32 depth = stream.getU16() + 12;
+ if (depth > 15)
+ ThrowRDE("Depth of huffman table is too great (%u).", depth);
+
+ stream.skipBytes(12);
+
+ std::array<uint32, 16> v0;
+ std::array<uint32, 16> v1;
+ for (uint32 i = 0; i < depth; i++)
+ v0[i] = stream.getU16();
+ for (uint32 i = 0; i < depth; i++) {
+ v1[i] = stream.getByte();
+
+ if (v1[i] == 0 || v1[i] > 12)
+ ThrowRDE("Data corrupt: v1[%i]=%i, expected [1..12]", depth, v1[i]);
+ }
+
+ std::vector<uchar8> nCodesPerLength;
+ nCodesPerLength.resize(17);
+
+ std::array<uint32, 16> v2;
+ /* Calculate codes and store bitcounts */
+ for (uint32 c = 0; c < depth; c++) {
+ v2[c] = v0[c] >> (12 - v1[c]);
+ nCodesPerLength.at(v1[c])++;
+ }
+
+ assert(nCodesPerLength.size() == 17);
+ assert(nCodesPerLength[0] == 0);
+ auto nCodes = ht.setNCodesPerLength(Buffer(&nCodesPerLength[1], 16));
+ assert(nCodes == depth);
+
+ std::vector<uchar8> codeValues;
+ codeValues.reserve(nCodes);
+
+ /* Find smallest */
+ for (uint32 i = 0; i < depth; i++) {
+ uint32 sm_val = 0xfffffff;
+ uint32 sm_num = 0xff;
+ for (uint32 j = 0; j < depth; j++) {
+ if (v2[j] <= sm_val) {
+ sm_num = j;
+ sm_val = v2[j];
+ }
+ }
+ codeValues.push_back(sm_num);
+ v2[sm_num] = 0xffffffff;
+ }
+
+ assert(codeValues.size() == nCodes);
+ ht.setCodeValues(Buffer(codeValues.data(), nCodes));
+
+ return ht;
+}
+
+HuffmanTable PentaxDecompressor::SetupHuffmanTable(ByteStream* metaData) {
+ HuffmanTable ht;
+
+ if (metaData)
+ ht = SetupHuffmanTable_Modern(*metaData);
+ else
+ ht = SetupHuffmanTable_Legacy();
+
+ ht.setup(true, false);
+
+ return ht;
+}
+
+void PentaxDecompressor::decompress(const ByteStream& data) const {
+ BitPumpMSB bs(data);
+ uchar8* draw = mRaw->getData();
+
+ assert(mRaw->dim.y > 0);
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.x % 2 == 0);
+
+ std::array<int, 2> pUp1 = {{}};
+ std::array<int, 2> pUp2 = {{}};
+
+ for (int y = 0; y < mRaw->dim.y && mRaw->dim.x >= 2; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&draw[y * mRaw->pitch]);
+
+ pUp1[y & 1] += ht.decodeNext(bs);
+ pUp2[y & 1] += ht.decodeNext(bs);
+
+ int pLeft1 = dest[0] = pUp1[y & 1];
+ int pLeft2 = dest[1] = pUp2[y & 1];
+
+ for (int x = 2; x < mRaw->dim.x; x += 2) {
+ pLeft1 += ht.decodeNext(bs);
+ pLeft2 += ht.decodeNext(bs);
+
+ dest[x] = pLeft1;
+ dest[x + 1] = pLeft2;
+
+ if (pLeft1 < 0 || pLeft1 > 65535)
+ ThrowRDE("decoded value out of bounds at %d:%d", x, y);
+ if (pLeft2 < 0 || pLeft2 > 65535)
+ ThrowRDE("decoded value out of bounds at %d:%d", x, y);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.h
new file mode 100644
index 00000000..4cc91597
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PentaxDecompressor.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 uchar8
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+
+namespace rawspeed {
+
+class ByteStream;
+
+class PentaxDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+ const HuffmanTable ht;
+
+public:
+ PentaxDecompressor(const RawImage& img, ByteStream* metaData);
+
+ void decompress(const ByteStream& data) const;
+
+private:
+ static HuffmanTable SetupHuffmanTable_Legacy();
+ static HuffmanTable SetupHuffmanTable_Modern(ByteStream stream);
+ static HuffmanTable SetupHuffmanTable(ByteStream* metaData);
+
+ static const std::array<std::array<std::array<uchar8, 16>, 2>, 1> pentax_tree;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.cpp
new file mode 100644
index 00000000..3debadac
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.cpp
@@ -0,0 +1,175 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ 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
+ 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 "rawspeedconfig.h"
+#include "decompressors/PhaseOneDecompressor.h"
+#include "common/Common.h" // for int32, uint32, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+#include <algorithm> // for for_each
+#include <array> // for array
+#include <cassert> // for assert
+#include <cstddef> // for size_t
+#include <utility> // for move
+#include <vector> // for vector, vector<>::size_type
+
+namespace rawspeed {
+
+PhaseOneDecompressor::PhaseOneDecompressor(const RawImage& img,
+ std::vector<PhaseOneStrip>&& strips_)
+ : mRaw(img), strips(std::move(strips_)) {
+ if (mRaw->getDataType() != TYPE_USHORT16)
+ ThrowRDE("Unexpected data type");
+
+ if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == 2)))
+ ThrowRDE("Unexpected cpp: %u", mRaw->getCpp());
+
+ if (!mRaw->dim.hasPositiveArea() || mRaw->dim.x % 2 != 0 ||
+ mRaw->dim.x > 11976 || mRaw->dim.y > 8852) {
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
+ mRaw->dim.y);
+ }
+
+ validateStrips();
+}
+
+void PhaseOneDecompressor::validateStrips() const {
+ // The 'strips' vector should contain exactly one element per row of image.
+
+ // If the lenght is different, then the 'strips' vector is clearly incorrect.
+ if (strips.size() != static_cast<decltype(strips)::size_type>(mRaw->dim.y)) {
+ ThrowRDE("Height (%u) vs strip count %zu mismatch", mRaw->dim.y,
+ strips.size());
+ }
+
+ struct RowBin {
+ using value_type = unsigned char;
+ bool isEmpty() const { return data == 0; }
+ void fill() { data = 1; }
+ value_type data = 0;
+ };
+
+ // Now, the strips in 'strips' vector aren't in order.
+ // The 'decltype(strips)::value_type::n' is the row number of a strip.
+ // We need to make sure that we have every row (0..mRaw->dim.y-1), once.
+
+ // There are many ways to do that. Here, we take the histogram of all the
+ // row numbers, and if any bin ends up not being '1' (one strip per row),
+ // then the input is bad.
+ std::vector<RowBin> histogram;
+ histogram.resize(strips.size());
+ int numBinsFilled = 0;
+ std::for_each(strips.begin(), strips.end(),
+ [y = mRaw->dim.y, &histogram,
+ &numBinsFilled](const PhaseOneStrip& strip) {
+ if (strip.n < 0 || strip.n >= y)
+ ThrowRDE("Strip specifies out-of-bounds row %u", strip.n);
+ RowBin& rowBin = histogram[strip.n];
+ if (!rowBin.isEmpty())
+ ThrowRDE("Duplicate row %u", strip.n);
+ rowBin.fill();
+ numBinsFilled++;
+ });
+ assert(histogram.size() == strips.size());
+ assert(numBinsFilled == mRaw->dim.y &&
+ "We should only get here if all the rows/bins got filled.");
+}
+
+void PhaseOneDecompressor::decompressStrip(const PhaseOneStrip& strip) const {
+ uint32 width = mRaw->dim.x;
+ assert(width % 2 == 0);
+
+ static constexpr std::array<const int, 10> length = {8, 7, 6, 9, 11,
+ 10, 5, 12, 14, 13};
+
+ BitPumpMSB32 pump(strip.bs);
+
+ std::array<int32, 2> pred;
+ pred.fill(0);
+ std::array<int, 2> len;
+ auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, strip.n));
+ for (uint32 col = 0; col < width; col++) {
+ if (col >= (width & ~7U)) // last 'width % 8' pixels.
+ len[0] = len[1] = 14;
+ else if ((col & 7) == 0) {
+ for (int& i : len) {
+ int j = 0;
+
+ for (; j < 5; j++) {
+ if (pump.getBits(1) != 0) {
+ if (col == 0)
+ ThrowRDE("Can not initialize lengths. Data is corrupt.");
+
+ // else, we have previously initialized lengths, so we are fine
+ break;
+ }
+ }
+
+ assert((col == 0 && j > 0) || col != 0);
+ if (j > 0)
+ i = length[2 * (j - 1) + pump.getBits(1)];
+ }
+ }
+
+ int i = len[col & 1];
+ if (i == 14)
+ img[col] = pred[col & 1] = pump.getBits(16);
+ else {
+ pred[col & 1] +=
+ static_cast<signed>(pump.getBits(i)) + 1 - (1 << (i - 1));
+ // FIXME: is the truncation the right solution here?
+ img[col] = ushort16(pred[col & 1]);
+ }
+ }
+}
+
+void PhaseOneDecompressor::decompressThread() const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (auto strip = strips.cbegin(); strip < strips.cend(); ++strip) {
+ try {
+ decompressStrip(*strip);
+ } catch (RawspeedException& err) {
+ // Propagate the exception out of OpenMP magic.
+ mRaw->setError(err.what());
+ }
+ }
+}
+
+void PhaseOneDecompressor::decompress() const {
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) \
+ num_threads(rawspeed_get_number_of_processor_cores())
+#endif
+ decompressThread();
+
+ std::string firstErr;
+ if (mRaw->isTooManyErrors(1, &firstErr)) {
+ ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
+ firstErr.c_str());
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.h
new file mode 100644
index 00000000..b1f1b7ef
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/PhaseOneDecompressor.h
@@ -0,0 +1,60 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+struct PhaseOneStrip {
+ const int n;
+ const ByteStream bs;
+
+ PhaseOneStrip(int block, ByteStream bs_) : n(block), bs(std::move(bs_)) {}
+};
+
+class PhaseOneDecompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+
+ std::vector<PhaseOneStrip> strips;
+
+ void decompressStrip(const PhaseOneStrip& strip) const;
+
+ void decompressThread() const noexcept;
+
+ void validateStrips() const;
+
+public:
+ PhaseOneDecompressor(const RawImage& img,
+ std::vector<PhaseOneStrip>&& strips_);
+
+ void decompress() const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.cpp
new file mode 100644
index 00000000..b6765958
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.cpp
@@ -0,0 +1,220 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2010 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/SamsungV0Decompressor.h"
+#include "common/Common.h" // for uint32, ushort16, int32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+#include "io/ByteStream.h" // for ByteStream
+#include <algorithm> // for max
+#include <cassert> // for assert
+#include <iterator> // for advance, begin, end, next
+#include <vector> // for vector
+
+namespace rawspeed {
+
+SamsungV0Decompressor::SamsungV0Decompressor(const RawImage& image,
+ const ByteStream& bso,
+ const ByteStream& bsr)
+ : AbstractSamsungDecompressor(image) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const uint32 width = mRaw->dim.x;
+ const uint32 height = mRaw->dim.y;
+
+ if (width == 0 || height == 0 || width < 16 || width > 5546 || height > 3714)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ computeStripes(bso.peekStream(height, 4), bsr);
+}
+
+// FIXME: this is very close to IiqDecoder::computeSripes()
+void SamsungV0Decompressor::computeStripes(ByteStream bso, ByteStream bsr) {
+ const uint32 height = mRaw->dim.y;
+
+ std::vector<uint32> offsets;
+ offsets.reserve(1 + height);
+ for (uint32 y = 0; y < height; y++)
+ offsets.emplace_back(bso.getU32());
+ offsets.emplace_back(bsr.getSize());
+
+ stripes.reserve(height);
+
+ auto offset_iterator = std::begin(offsets);
+ bsr.skipBytes(*offset_iterator);
+
+ auto next_offset_iterator = std::next(offset_iterator);
+ while (next_offset_iterator < std::end(offsets)) {
+ if (*offset_iterator >= *next_offset_iterator)
+ ThrowRDE("Line offsets are out of sequence or slice is empty.");
+
+ const auto size = *next_offset_iterator - *offset_iterator;
+ assert(size > 0);
+
+ stripes.emplace_back(bsr.getStream(size));
+
+ std::advance(offset_iterator, 1);
+ std::advance(next_offset_iterator, 1);
+ }
+
+ assert(stripes.size() == height);
+}
+
+void SamsungV0Decompressor::decompress() const {
+ for (int y = 0; y < mRaw->dim.y; y++)
+ decompressStrip(y, stripes[y]);
+
+ // Swap red and blue pixels to get the final CFA pattern
+ for (int y = 0; y < mRaw->dim.y - 1; y += 2) {
+ auto* topline = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ auto* bottomline = reinterpret_cast<ushort16*>(mRaw->getData(0, y + 1));
+
+ for (int x = 0; x < mRaw->dim.x - 1; x += 2) {
+ ushort16 temp = topline[1];
+ topline[1] = bottomline[0];
+ bottomline[0] = temp;
+
+ topline += 2;
+ bottomline += 2;
+ }
+ }
+}
+
+int32 SamsungV0Decompressor::calcAdj(BitPumpMSB32* bits, int b) {
+ int32 adj = 0;
+ if (b)
+ adj = (static_cast<int32>(bits->getBits(b)) << (32 - b) >> (32 - b));
+ return adj;
+}
+
+void SamsungV0Decompressor::decompressStrip(uint32 y,
+ const ByteStream& bs) const {
+ const uint32 width = mRaw->dim.x;
+ assert(width > 0);
+
+ BitPumpMSB32 bits(bs);
+
+ std::array<int, 4> len;
+ for (int& i : len)
+ i = y < 2 ? 7 : 4;
+
+ auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ const auto* const past_last =
+ reinterpret_cast<ushort16*>(mRaw->getData(width - 1, y) + mRaw->getBpp());
+ ushort16* img_up = reinterpret_cast<ushort16*>(
+ mRaw->getData(0, std::max(0, static_cast<int>(y) - 1)));
+ ushort16* img_up2 = reinterpret_cast<ushort16*>(
+ mRaw->getData(0, std::max(0, static_cast<int>(y) - 2)));
+
+ // Image is arranged in groups of 16 pixels horizontally
+ for (uint32 x = 0; x < width; x += 16) {
+ bits.fill();
+ bool dir = !!bits.getBitsNoFill(1);
+
+ std::array<int, 4> op;
+ for (int& i : op)
+ i = bits.getBitsNoFill(2);
+
+ for (int i = 0; i < 4; i++) {
+ assert(op[i] >= 0 && op[i] <= 3);
+
+ switch (op[i]) {
+ case 3:
+ len[i] = bits.getBits(4);
+ break;
+ case 2:
+ len[i]--;
+ break;
+ case 1:
+ len[i]++;
+ break;
+ default:
+ // FIXME: it can be zero too.
+ break;
+ }
+
+ if (len[i] < 0)
+ ThrowRDE("Bit length less than 0.");
+ if (len[i] > 16)
+ ThrowRDE("Bit Length more than 16.");
+ }
+
+ if (dir) {
+ // Upward prediction
+
+ if (y < 2)
+ ThrowRDE("Upward prediction for the first two rows. Raw corrupt");
+
+ if (x + 16 >= width)
+ ThrowRDE("Upward prediction for the last block of pixels. Raw corrupt");
+
+ // First we decode even pixels
+ for (int c = 0; c < 16; c += 2) {
+ int b = len[c >> 3];
+ int32 adj = calcAdj(&bits, b);
+
+ img[c] = adj + img_up[c];
+ }
+
+ // Now we decode odd pixels
+ // Why on earth upward prediction only looks up 1 line above
+ // is beyond me, it will hurt compression a deal.
+ for (int c = 1; c < 16; c += 2) {
+ int b = len[2 | (c >> 3)];
+ int32 adj = calcAdj(&bits, b);
+
+ img[c] = adj + img_up2[c];
+ }
+ } else {
+ // Left to right prediction
+ // First we decode even pixels
+ int pred_left = x != 0 ? img[-2] : 128;
+ for (int c = 0; c < 16; c += 2) {
+ int b = len[c >> 3];
+ int32 adj = calcAdj(&bits, b);
+
+ if (img + c < past_last)
+ img[c] = adj + pred_left;
+ }
+
+ // Now we decode odd pixels
+ pred_left = x != 0 ? img[-1] : 128;
+ for (int c = 1; c < 16; c += 2) {
+ int b = len[2 | (c >> 3)];
+ int32 adj = calcAdj(&bits, b);
+
+ if (img + c < past_last)
+ img[c] = adj + pred_left;
+ }
+ }
+
+ img += 16;
+ img_up += 16;
+ img_up2 += 16;
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.h
new file mode 100644
index 00000000..958123c1
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV0Decompressor.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/Common.h" // for int32, uint32
+#include "decompressors/AbstractSamsungDecompressor.h" // for AbstractSamsu...
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+#include "io/ByteStream.h" // for ByteStream
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class RawImage;
+
+// Decoder for compressed srw files (NX300 and later)
+class SamsungV0Decompressor final : public AbstractSamsungDecompressor {
+ std::vector<ByteStream> stripes;
+
+ void computeStripes(ByteStream bso, ByteStream bsr);
+
+ void decompressStrip(uint32 y, const ByteStream& bs) const;
+
+ static int32 calcAdj(BitPumpMSB32* bits, int b);
+
+public:
+ SamsungV0Decompressor(const RawImage& image, const ByteStream& bso,
+ const ByteStream& bsr);
+
+ void decompress() const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.cpp
new file mode 100644
index 00000000..a1aa1790
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.cpp
@@ -0,0 +1,137 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2010 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/SamsungV1Decompressor.h"
+#include "common/Common.h" // for uint32, ushort16, int32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include <memory> // for allocator_traits<>::value_...
+#include <vector> // for vector
+
+namespace rawspeed {
+
+struct SamsungV1Decompressor::encTableItem {
+ uchar8 encLen;
+ uchar8 diffLen;
+};
+
+SamsungV1Decompressor::SamsungV1Decompressor(const RawImage& image,
+ const ByteStream* bs_, int bit)
+ : AbstractSamsungDecompressor(image), bs(bs_), bits(bit) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ switch (bit) {
+ case 12:
+ break;
+ default:
+ ThrowRDE("Unexpected bit per pixel (%u)", bit);
+ }
+
+ const uint32 width = mRaw->dim.x;
+ const uint32 height = mRaw->dim.y;
+
+ if (width == 0 || height == 0 || width > 5664 || height > 3714)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+}
+
+int32 SamsungV1Decompressor::samsungDiff(BitPumpMSB* pump,
+ const std::vector<encTableItem>& tbl) {
+ // We read 10 bits to index into our table
+ uint32 c = pump->peekBits(10);
+ // Skip the bits that were used to encode this case
+ pump->getBits(tbl[c].encLen);
+ // Read the number of bits the table tells me
+ int32 len = tbl[c].diffLen;
+ int32 diff = pump->getBits(len);
+
+ // If the first bit is 0 we need to turn this into a negative number
+ diff = len != 0 ? HuffmanTable::signExtended(diff, len) : diff;
+
+ return diff;
+}
+
+void SamsungV1Decompressor::decompress() {
+ const uint32 width = mRaw->dim.x;
+ const uint32 height = mRaw->dim.y;
+
+ // This format has a variable length encoding of how many bits are needed
+ // to encode the difference between pixels, we use a table to process it
+ // that has two values, the first the number of bits that were used to
+ // encode, the second the number of bits that come after with the difference
+ // The table has 14 entries because the difference can have between 0 (no
+ // difference) and 13 bits (differences between 12 bits numbers can need 13)
+ static const std::array<std::array<uchar8, 2>, 14> tab = {{{3, 4},
+ {3, 7},
+ {2, 6},
+ {2, 5},
+ {4, 3},
+ {6, 0},
+ {7, 9},
+ {8, 10},
+ {9, 11},
+ {10, 12},
+ {10, 13},
+ {5, 1},
+ {4, 8},
+ {4, 2}}};
+ std::vector<encTableItem> tbl(1024);
+ std::array<std::array<ushort16, 2>, 2> vpred = {{}};
+ std::array<ushort16, 2> hpred;
+
+ // We generate a 1024 entry table (to be addressed by reading 10 bits) by
+ // consecutively filling in 2^(10-N) positions where N is the variable number
+ // of bits of the encoding. So for example 4 is encoded with 3 bits so the
+ // first 2^(10-3)=128 positions are set with 3,4 so that any time we read 000
+ // we know the next 4 bits are the difference. We read 10 bits because that is
+ // the maximum number of bits used in the variable encoding (for the 12 and
+ // 13 cases)
+ uint32 n = 0;
+ for (auto i : tab) {
+ for (int32 c = 0; c < (1024 >> i[0]); c++) {
+ tbl[n].encLen = i[0];
+ tbl[n].diffLen = i[1];
+ n++;
+ }
+ }
+
+ BitPumpMSB pump(*bs);
+ for (uint32 y = 0; y < height; y++) {
+ auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ for (uint32 x = 0; x < width; x++) {
+ int32 diff = samsungDiff(&pump, tbl);
+ if (x < 2)
+ hpred[x] = vpred[y & 1][x] += diff;
+ else
+ hpred[x & 1] += diff;
+ img[x] = hpred[x & 1];
+ if (img[x] >> bits)
+ ThrowRDE("decoded value out of bounds at %d:%d", x, y);
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.h
new file mode 100644
index 00000000..bc6f95a9
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV1Decompressor.h
@@ -0,0 +1,48 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/Common.h" // for int32
+#include "decompressors/AbstractSamsungDecompressor.h" // for AbstractSamsu...
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class ByteStream;
+class RawImage;
+
+// Decoder for compressed srw files (NX3000 and later)
+class SamsungV1Decompressor final : public AbstractSamsungDecompressor {
+ struct encTableItem;
+ static int32 samsungDiff(BitPumpMSB* pump,
+ const std::vector<encTableItem>& tbl);
+
+ const ByteStream* bs;
+ int bits;
+
+public:
+ SamsungV1Decompressor(const RawImage& image, const ByteStream* bs_, int bit);
+
+ void decompress();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.cpp
new file mode 100644
index 00000000..b3f71c92
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.cpp
@@ -0,0 +1,344 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2010 Klaus Post
+ Copyright (C) 2014-2015 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/SamsungV2Decompressor.h"
+#include "common/Common.h" // for uint32, ushort16, int32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+#include "io/ByteStream.h" // for ByteStream
+#include <algorithm> // for max
+#include <cassert> // for assert
+#include <type_traits> // for underlying_type, underlyin...
+
+namespace rawspeed {
+
+// Seriously Samsung just use lossless jpeg already, it compresses better too :)
+
+// Thanks to Michael Reichmann (Luminous Landscape) for putting Pedro Côrte-Real
+// in contact and Loring von Palleske (Samsung) for pointing to the open-source
+// code of Samsung's DNG converter at http://opensource.samsung.com/
+
+enum struct SamsungV2Decompressor::OptFlags : uint32 {
+ NONE = 0U, // no flags
+ SKIP = 1U << 0U, // Skip checking if we need differences from previous line
+ MV = 1U << 1U, // Simplify motion vector definition
+ QP = 1U << 2U, // Don't scale the diff values
+
+ // all possible flags
+ ALL = SKIP | MV | QP,
+};
+
+constexpr SamsungV2Decompressor::OptFlags
+operator|(SamsungV2Decompressor::OptFlags lhs,
+ SamsungV2Decompressor::OptFlags rhs) {
+ return static_cast<SamsungV2Decompressor::OptFlags>(
+ static_cast<std::underlying_type<SamsungV2Decompressor::OptFlags>::type>(
+ lhs) |
+ static_cast<std::underlying_type<SamsungV2Decompressor::OptFlags>::type>(
+ rhs));
+}
+
+constexpr bool operator&(SamsungV2Decompressor::OptFlags lhs,
+ SamsungV2Decompressor::OptFlags rhs) {
+ return SamsungV2Decompressor::OptFlags::NONE !=
+ static_cast<SamsungV2Decompressor::OptFlags>(
+ static_cast<
+ std::underlying_type<SamsungV2Decompressor::OptFlags>::type>(
+ lhs) &
+ static_cast<
+ std::underlying_type<SamsungV2Decompressor::OptFlags>::type>(
+ rhs));
+}
+
+SamsungV2Decompressor::SamsungV2Decompressor(const RawImage& image,
+ const ByteStream& bs, int bit)
+ : AbstractSamsungDecompressor(image), bits(bit) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ switch (bit) {
+ case 12:
+ case 14:
+ break;
+ default:
+ ThrowRDE("Unexpected bit per pixel (%u)", bit);
+ }
+
+ static constexpr const auto headerSize = 16;
+ bs.check(headerSize);
+
+ BitPumpMSB32 startpump(bs);
+
+ // Process the initial metadata bits, we only really use initVal, width and
+ // height (the last two match the TIFF values anyway)
+ startpump.getBits(16); // NLCVersion
+ startpump.getBits(4); // ImgFormat
+ bitDepth = startpump.getBits(4) + 1;
+ startpump.getBits(4); // NumBlkInRCUnit
+ startpump.getBits(4); // CompressionRatio
+ width = startpump.getBits(16);
+ height = startpump.getBits(16);
+ startpump.getBits(16); // TileWidth
+ startpump.getBits(4); // reserved
+
+ // The format includes an optimization code that sets 3 flags to change the
+ // decoding parameters
+ const uint32 optflags = startpump.getBits(4);
+ if (optflags > static_cast<uint32>(OptFlags::ALL))
+ ThrowRDE("Invalid opt flags %x", optflags);
+ _flags = static_cast<OptFlags>(optflags);
+
+ startpump.getBits(8); // OverlapWidth
+ startpump.getBits(8); // reserved
+ startpump.getBits(8); // Inc
+ startpump.getBits(2); // reserved
+ initVal = startpump.getBits(14);
+
+ assert(startpump.getPosition() == headerSize);
+
+ if (width == 0 || height == 0 || width % 16 != 0 || width > 6496 ||
+ height > 4336)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
+
+ if (width != static_cast<uint32>(mRaw->dim.x) ||
+ height != static_cast<uint32>(mRaw->dim.y))
+ ThrowRDE("EXIF image dimensions do not match dimensions from raw header");
+
+ data = startpump.getStream(startpump.getRemainSize());
+}
+
+void SamsungV2Decompressor::decompress() {
+ switch (_flags) {
+ case OptFlags::NONE:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::NONE>(row);
+ break;
+ case OptFlags::ALL:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::ALL>(row);
+ break;
+
+ case OptFlags::SKIP:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::SKIP>(row);
+ break;
+ case OptFlags::MV:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::MV>(row);
+ break;
+ case OptFlags::QP:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::QP>(row);
+ break;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+ case OptFlags::SKIP | OptFlags::MV:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::SKIP | OptFlags::MV>(row);
+ break;
+ case OptFlags::SKIP | OptFlags::QP:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::SKIP | OptFlags::QP>(row);
+ break;
+
+ case OptFlags::MV | OptFlags::QP:
+ for (uint32 row = 0; row < height; row++)
+ decompressRow<OptFlags::MV | OptFlags::QP>(row);
+ break;
+#pragma GCC diagnostic pop
+
+ default:
+ __builtin_unreachable();
+ }
+}
+
+// The format is relatively straightforward. Each line gets encoded as a set
+// of differences from pixels from another line. Pixels are grouped in blocks
+// of 16 (8 green, 8 red or blue). Each block is encoded in three sections.
+// First 1 or 4 bits to specify which reference pixels to use, then a section
+// that specifies for each pixel the number of bits in the difference, then
+// the actual difference bits
+
+template <SamsungV2Decompressor::OptFlags optflags>
+void SamsungV2Decompressor::decompressRow(uint32 row) {
+ // Align pump to 16byte boundary
+ const auto line_offset = data.getPosition();
+ if ((line_offset & 0xf) != 0)
+ data.skipBytes(16 - (line_offset & 0xf));
+
+ BitPumpMSB32 pump(data);
+
+ auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, row));
+ ushort16* img_up = reinterpret_cast<ushort16*>(
+ mRaw->getData(0, std::max(0, static_cast<int>(row) - 1)));
+ ushort16* img_up2 = reinterpret_cast<ushort16*>(
+ mRaw->getData(0, std::max(0, static_cast<int>(row) - 2)));
+
+ // Initialize the motion and diff modes at the start of the line
+ uint32 motion = 7;
+ // By default we are not scaling values at all
+ int32 scale = 0;
+
+ std::array<std::array<int, 2>, 3> diffBitsMode = {{}};
+ for (auto& i : diffBitsMode)
+ i[0] = i[1] = (row == 0 || row == 1) ? 7 : 4;
+
+ assert(width >= 16);
+ assert(width % 16 == 0);
+ for (uint32 col = 0; col < width; col += 16) {
+ if (!(optflags & OptFlags::QP) && !(col & 63)) {
+ static constexpr std::array<int32, 3> scalevals = {{0, -2, 2}};
+ uint32 i = pump.getBits(2);
+ scale = i < 3 ? scale + scalevals[i] : pump.getBits(12);
+ }
+
+ // First we figure out which reference pixels mode we're in
+ if (optflags & OptFlags::MV)
+ motion = pump.getBits(1) ? 3 : 7;
+ else if (!pump.getBits(1))
+ motion = pump.getBits(3);
+
+ if ((row == 0 || row == 1) && (motion != 7))
+ ThrowRDE("At start of image and motion isn't 7. File corrupted?");
+
+ if (motion == 7) {
+ // The base case, just set all pixels to the previous ones on the same
+ // line If we're at the left edge we just start at the initial value
+ for (uint32 i = 0; i < 16; i++)
+ img[i] = (col == 0) ? initVal : *(img + i - 2);
+ } else {
+ // The complex case, we now need to actually lookup one or two lines
+ // above
+ if (row < 2)
+ ThrowRDE(
+ "Got a previous line lookup on first two lines. File corrupted?");
+
+ static constexpr std::array<int32, 7> motionOffset = {-4, -2, -2, 0,
+ 0, 2, 4};
+ static constexpr std::array<int32, 7> motionDoAverage = {0, 0, 1, 0,
+ 1, 0, 0};
+
+ int32 slideOffset = motionOffset[motion];
+ int32 doAverage = motionDoAverage[motion];
+
+ for (uint32 i = 0; i < 16; i++) {
+ ushort16* line;
+ ushort16* refpixel;
+
+ if ((row + i) & 0x1) {
+ // Red or blue pixels use same color two lines up
+ line = img_up2;
+ refpixel = line + i + slideOffset;
+ } else {
+ // Green pixel N uses Green pixel N from row above
+ // (top left or top right)
+ line = img_up;
+ refpixel = line + i + slideOffset + (((i % 2) != 0) ? -1 : 1);
+ }
+
+ if (col == 0 && line > refpixel)
+ ThrowRDE("Bad motion %u at the beginning of the row", motion);
+ if (col + 16 == width && ((refpixel >= line + 16) ||
+ (doAverage && (refpixel + 2 >= line + 16))))
+ ThrowRDE("Bad motion %u at the end of the row", motion);
+
+ // In some cases we use as reference interpolation of this pixel and
+ // the next
+ if (doAverage)
+ img[i] = (*refpixel + *(refpixel + 2) + 1) >> 1;
+ else
+ img[i] = *refpixel;
+ }
+ }
+
+ // Figure out how many difference bits we have to read for each pixel
+ std::array<uint32, 4> diffBits = {};
+ if (optflags & OptFlags::SKIP || !pump.getBits(1)) {
+ std::array<uint32, 4> flags;
+ for (unsigned int& flag : flags)
+ flag = pump.getBits(2);
+
+ for (uint32 i = 0; i < 4; i++) {
+ // The color is 0-Green 1-Blue 2-Red
+ uint32 colornum = (row % 2 != 0) ? i >> 1 : ((i >> 1) + 2) % 3;
+
+ assert(flags[i] <= 3);
+ switch (flags[i]) {
+ case 0:
+ diffBits[i] = diffBitsMode[colornum][0];
+ break;
+ case 1:
+ diffBits[i] = diffBitsMode[colornum][0] + 1;
+ break;
+ case 2:
+ if (diffBitsMode[colornum][0] == 0)
+ ThrowRDE("Difference bits underflow. File corrupted?");
+ diffBits[i] = diffBitsMode[colornum][0] - 1;
+ break;
+ case 3:
+ diffBits[i] = pump.getBits(4);
+ break;
+ default:
+ __builtin_unreachable();
+ }
+
+ diffBitsMode[colornum][0] = diffBitsMode[colornum][1];
+ diffBitsMode[colornum][1] = diffBits[i];
+
+ if (diffBits[i] > bitDepth + 1)
+ ThrowRDE("Too many difference bits. File corrupted?");
+ }
+ }
+
+ // Actually read the differences and write them to the pixels
+ for (uint32 i = 0; i < 16; i++) {
+ uint32 len = diffBits[i >> 2];
+ int32 diff = pump.getBits(len);
+
+ // If the first bit is 1 we need to turn this into a negative number
+ if (len != 0 && diff >> (len - 1))
+ diff -= (1 << len);
+
+ ushort16* value = nullptr;
+ // Apply the diff to pixels 0 2 4 6 8 10 12 14 1 3 5 7 9 11 13 15
+ if (row % 2)
+ value = &img[((i & 0x7) << 1) + 1 - (i >> 3)];
+ else
+ value = &img[((i & 0x7) << 1) + (i >> 3)];
+
+ diff = diff * (scale * 2 + 1) + scale;
+ *value = clampBits(static_cast<int>(*value) + diff, bits);
+ }
+
+ img += 16;
+ img_up += 16;
+ img_up2 += 16;
+ }
+
+ data.skipBytes(pump.getBufferPosition());
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.h
new file mode 100644
index 00000000..21bd7399
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SamsungV2Decompressor.h
@@ -0,0 +1,55 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/Common.h" // for uint32
+#include "decompressors/AbstractSamsungDecompressor.h" // for AbstractSamsu...
+#include "io/ByteStream.h" // for ByteStream
+
+namespace rawspeed {
+
+class RawImage;
+
+// Decoder for third generation compressed SRW files (NX1)
+class SamsungV2Decompressor final : public AbstractSamsungDecompressor {
+public:
+ enum struct OptFlags : uint32;
+
+protected:
+ int bits;
+
+ uint32 bitDepth;
+ uint32 width;
+ uint32 height;
+ OptFlags _flags;
+ uint32 initVal;
+
+ ByteStream data;
+
+ template <OptFlags optflags> void decompressRow(uint32 row);
+
+public:
+ SamsungV2Decompressor(const RawImage& image, const ByteStream& bs, int bit);
+
+ void decompress();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.cpp
new file mode 100644
index 00000000..1cbd2360
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.cpp
@@ -0,0 +1,88 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "decompressors/SonyArw1Decompressor.h"
+#include "common/Common.h" // for uint32, uchar8, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "decompressors/HuffmanTable.h" // for HuffmanTable
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include <cassert> // for assert
+
+namespace rawspeed {
+
+SonyArw1Decompressor::SonyArw1Decompressor(const RawImage& img) : mRaw(img) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const uint32 w = mRaw->dim.x;
+ const uint32 h = mRaw->dim.y;
+
+ if (w == 0 || h == 0 || h % 2 != 0 || w > 4600 || h > 3072)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", w, h);
+}
+
+void SonyArw1Decompressor::decompress(const ByteStream& input) const {
+ const uint32 w = mRaw->dim.x;
+ const uint32 h = mRaw->dim.y;
+
+ assert(w > 0);
+ assert(h > 0);
+ assert(h % 2 == 0);
+
+ BitPumpMSB bits(input);
+ uchar8* data = mRaw->getData();
+ auto* dest = reinterpret_cast<ushort16*>(&data[0]);
+ uint32 pitch = mRaw->pitch / sizeof(ushort16);
+ int sum = 0;
+ for (int64 x = w - 1; x >= 0; x--) {
+ for (uint32 y = 0; y < h + 1; y += 2) {
+ bits.fill();
+
+ if (y == h)
+ y = 1;
+
+ uint32 len = 4 - bits.getBitsNoFill(2);
+
+ if (len == 3 && bits.getBitsNoFill(1))
+ len = 0;
+
+ if (len == 4)
+ while (len < 17 && !bits.getBitsNoFill(1))
+ len++;
+
+ int diff = bits.getBits(len);
+ diff = len != 0 ? HuffmanTable::signExtended(diff, len) : diff;
+ sum += diff;
+
+ if (sum < 0 || (sum >> 12) > 0)
+ ThrowRDE("Error decompressing");
+
+ if (y < h)
+ dest[x + y * pitch] = sum;
+ }
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.h
new file mode 100644
index 00000000..1be0ccb8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw1Decompressor.h
@@ -0,0 +1,38 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+
+namespace rawspeed {
+
+class ByteStream;
+
+class SonyArw1Decompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+
+public:
+ explicit SonyArw1Decompressor(const RawImage& img);
+ void decompress(const ByteStream& input) const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.cpp
new file mode 100644
index 00000000..5cc20e69
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.cpp
@@ -0,0 +1,144 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017-2019 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
+*/
+
+#include "rawspeedconfig.h"
+#include "decompressors/SonyArw2Decompressor.h"
+#include "common/Common.h" // for uint32
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpLSB.h" // for BitPumpLSB
+#include <cassert> // for assert
+
+namespace rawspeed {
+
+SonyArw2Decompressor::SonyArw2Decompressor(const RawImage& img,
+ const ByteStream& input_)
+ : mRaw(img) {
+ if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
+ mRaw->getBpp() != 2)
+ ThrowRDE("Unexpected component count / data type");
+
+ const uint32 w = mRaw->dim.x;
+ const uint32 h = mRaw->dim.y;
+
+ if (w == 0 || h == 0 || w % 32 != 0 || w > 8000 || h > 5320)
+ ThrowRDE("Unexpected image dimensions found: (%u; %u)", w, h);
+
+ // 1 byte per pixel
+ input = input_.peekStream(mRaw->dim.x * mRaw->dim.y);
+}
+
+void SonyArw2Decompressor::decompressRow(int row) const {
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+ int32 w = mRaw->dim.x;
+
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.x % 32 == 0);
+
+ auto* dest = reinterpret_cast<ushort16*>(&data[row * pitch]);
+
+ ByteStream rowBs = input;
+ rowBs.skipBytes(row * mRaw->dim.x);
+ rowBs = rowBs.peekStream(mRaw->dim.x);
+
+ BitPumpLSB bits(rowBs);
+
+ uint32 random = bits.peekBits(24);
+
+ // Each loop iteration processes 16 pixels, consuming 128 bits of input.
+ for (int32 x = 0; x < w;) {
+ // 30 bits.
+ int _max = bits.getBits(11);
+ int _min = bits.getBits(11);
+ int _imax = bits.getBits(4);
+ int _imin = bits.getBits(4);
+
+ // 128-30 = 98 bits remaining, still need to decode 16 pixels...
+ // Each full pixel consumes 7 bits, thus we can only have 14 full pixels.
+ // So we lack 2 pixels. That is where _imin and _imax come into play,
+ // values of those pixels were already specified in _min and _max.
+ // But what that means is, _imin and _imax must not be equal!
+ if (_imax == _imin)
+ ThrowRDE("ARW2 invariant failed, same pixel is both min and max");
+
+ int sh = 0;
+ while ((sh < 4) && ((0x80 << sh) <= (_max - _min)))
+ sh++;
+
+ for (int i = 0; i < 16; i++) {
+ int p;
+ if (i == _imax)
+ p = _max;
+ else {
+ if (i == _imin)
+ p = _min;
+ else {
+ p = (bits.getBits(7) << sh) + _min;
+ if (p > 0x7ff)
+ p = 0x7ff;
+ }
+ }
+ mRaw->setWithLookUp(p << 1, reinterpret_cast<uchar8*>(&dest[x + i * 2]),
+ &random);
+ }
+ x += ((x & 1) != 0) ? 31 : 1; // Skip to next 32 pixels
+ }
+}
+
+void SonyArw2Decompressor::decompressThread() const noexcept {
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.x % 32 == 0);
+ assert(mRaw->dim.y > 0);
+
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (int y = 0; y < mRaw->dim.y; y++) {
+ try {
+ decompressRow(y);
+ } catch (RawspeedException& err) {
+ // Propagate the exception out of OpenMP magic.
+ mRaw->setError(err.what());
+#ifdef HAVE_OPENMP
+#pragma omp cancel for
+#endif
+ }
+ }
+}
+
+void SonyArw2Decompressor::decompress() const {
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) \
+ num_threads(rawspeed_get_number_of_processor_cores())
+#endif
+ decompressThread();
+
+ std::string firstErr;
+ if (mRaw->isTooManyErrors(1, &firstErr)) {
+ ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
+ firstErr.c_str());
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.h
new file mode 100644
index 00000000..31279578
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/SonyArw2Decompressor.h
@@ -0,0 +1,43 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017-2019 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/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/ByteStream.h" // for ByteStream
+
+namespace rawspeed {
+
+class RawImage;
+
+class SonyArw2Decompressor final : public AbstractDecompressor {
+ void decompressRow(int row) const;
+ void decompressThread() const noexcept;
+
+ RawImage mRaw;
+ ByteStream input;
+
+public:
+ SonyArw2Decompressor(const RawImage& img, const ByteStream& input);
+ void decompress() const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.cpp
new file mode 100644
index 00000000..be9d14e9
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.cpp
@@ -0,0 +1,388 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017-2019 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
+*/
+
+#include "decompressors/UncompressedDecompressor.h"
+#include "common/Common.h" // for uint32, uchar8, ushort16
+#include "common/Point.h" // for iPoint2D
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/BitPumpLSB.h" // for BitPumpLSB
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include "io/BitPumpMSB16.h" // for BitPumpMSB16
+#include "io/BitPumpMSB32.h" // for BitPumpMSB32
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for getHostEndianness, Endiann...
+#include "io/IOException.h" // for ThrowIOE
+#include <algorithm> // for min
+#include <cassert> // for assert
+
+using std::min;
+
+namespace rawspeed {
+
+void UncompressedDecompressor::sanityCheck(const uint32* h, int bytesPerLine) {
+ assert(h != nullptr);
+ assert(*h > 0);
+ assert(bytesPerLine > 0);
+ assert(input.getSize() > 0);
+
+ // How many multiples of bpl are there in the input buffer?
+ // The remainder is ignored/discarded.
+ const auto fullRows = input.getRemainSize() / bytesPerLine;
+
+ // If more than the output height, we are all good.
+ if (fullRows >= *h)
+ return; // all good!
+
+ if (fullRows == 0)
+ ThrowIOE("Not enough data to decode a single line. Image file truncated.");
+
+ ThrowIOE("Image truncated, only %u of %u lines found", fullRows, *h);
+
+ // FIXME: need to come up with some common variable to allow proceeding here
+ // *h = min_h;
+}
+
+void UncompressedDecompressor::sanityCheck(uint32 w, const uint32* h, int bpp) {
+ assert(w > 0);
+ assert(bpp > 0);
+
+ // bytes per line
+ const auto bpl = bpp * w;
+ assert(bpl > 0);
+
+ sanityCheck(h, bpl);
+}
+
+int UncompressedDecompressor::bytesPerLine(int w, bool skips) {
+ assert(w > 0);
+
+ if ((12 * w) % 8 != 0)
+ ThrowIOE("Bad image width");
+
+ // Calculate expected bytes per line.
+ auto perline = (12 * w) / 8;
+
+ if (!skips)
+ return perline;
+
+ // Add skips every 10 pixels
+ perline += ((w + 2) / 10);
+
+ return perline;
+}
+
+void UncompressedDecompressor::readUncompressedRaw(const iPoint2D& size,
+ const iPoint2D& offset,
+ int inputPitchBytes,
+ int bitPerPixel,
+ BitOrder order) {
+ assert(inputPitchBytes > 0);
+ assert(bitPerPixel > 0);
+
+ uchar8* data = mRaw->getData();
+ uint32 outPitch = mRaw->pitch;
+ uint32 w = size.x;
+ uint32 h = size.y;
+ uint32 cpp = mRaw->getCpp();
+ uint64 ox = offset.x;
+ uint64 oy = offset.y;
+
+ if (bitPerPixel > 16 && mRaw->getDataType() == TYPE_USHORT16)
+ ThrowRDE("Unsupported bit depth");
+
+ const int outPixelBits = w * cpp * bitPerPixel;
+ assert(outPixelBits > 0);
+
+ if (outPixelBits % 8 != 0) {
+ ThrowRDE("Bad combination of cpp (%u), bps (%u) and width (%u), the "
+ "pitch is %u bits, which is not a multiple of 8 (1 byte)",
+ cpp, bitPerPixel, w, outPixelBits);
+ }
+
+ const int outPixelBytes = outPixelBits / 8;
+
+ // The input pitch might be larger than needed (i.e. have some padding),
+ // but it can *not* be smaller than needed.
+ if (inputPitchBytes < outPixelBytes)
+ ThrowRDE("Specified pitch is smaller than minimally-required pitch");
+
+ // Check the specified pitch, not the minimally-required pitch.
+ sanityCheck(&h, inputPitchBytes);
+
+ assert(inputPitchBytes >= outPixelBytes);
+ uint32 skipBytes = inputPitchBytes - outPixelBytes; // Skip per line
+
+ if (oy > static_cast<uint64>(mRaw->dim.y))
+ ThrowRDE("Invalid y offset");
+ if (ox + size.x > static_cast<uint64>(mRaw->dim.x))
+ ThrowRDE("Invalid x offset");
+
+ uint64 y = oy;
+ h = min(h + oy, static_cast<uint64>(mRaw->dim.y));
+
+ if (mRaw->getDataType() == TYPE_FLOAT32) {
+ if (bitPerPixel != 32)
+ ThrowRDE("Only 32 bit float point supported");
+ copyPixels(&data[offset.x * sizeof(float) * cpp + y * outPitch], outPitch,
+ input.getData(inputPitchBytes * (h - y)), inputPitchBytes,
+ w * mRaw->getBpp(), h - y);
+ return;
+ }
+
+ if (BitOrder_MSB == order) {
+ BitPumpMSB bits(input);
+ w *= cpp;
+ for (; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) * cpp + y * outPitch]);
+ for (uint32 x = 0; x < w; x++) {
+ uint32 b = bits.getBits(bitPerPixel);
+ dest[x] = b;
+ }
+ bits.skipBytes(skipBytes);
+ }
+ } else if (BitOrder_MSB16 == order) {
+ BitPumpMSB16 bits(input);
+ w *= cpp;
+ for (; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) * cpp + y * outPitch]);
+ for (uint32 x = 0; x < w; x++) {
+ uint32 b = bits.getBits(bitPerPixel);
+ dest[x] = b;
+ }
+ bits.skipBytes(skipBytes);
+ }
+ } else if (BitOrder_MSB32 == order) {
+ BitPumpMSB32 bits(input);
+ w *= cpp;
+ for (; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) * cpp + y * outPitch]);
+ for (uint32 x = 0; x < w; x++) {
+ uint32 b = bits.getBits(bitPerPixel);
+ dest[x] = b;
+ }
+ bits.skipBytes(skipBytes);
+ }
+ } else {
+ if (bitPerPixel == 16 && getHostEndianness() == Endianness::little) {
+ copyPixels(&data[offset.x * sizeof(ushort16) * cpp + y * outPitch],
+ outPitch, input.getData(inputPitchBytes * (h - y)),
+ inputPitchBytes, w * mRaw->getBpp(), h - y);
+ return;
+ }
+ if (bitPerPixel == 12 && static_cast<int>(w) == inputPitchBytes * 8 / 12 &&
+ getHostEndianness() == Endianness::little) {
+ decode12BitRaw<Endianness::little>(w, h);
+ return;
+ }
+ BitPumpLSB bits(input);
+ w *= cpp;
+ for (; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(
+ &data[offset.x * sizeof(ushort16) + y * outPitch]);
+ for (uint32 x = 0; x < w; x++) {
+ uint32 b = bits.getBits(bitPerPixel);
+ dest[x] = b;
+ }
+ bits.skipBytes(skipBytes);
+ }
+ }
+}
+
+template <bool uncorrectedRawValues>
+void UncompressedDecompressor::decode8BitRaw(uint32 w, uint32 h) {
+ sanityCheck(w, &h, 1);
+
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+ const uchar8* in = input.getData(w * h);
+ uint32 random = 0;
+ for (uint32 y = 0; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+ for (uint32 x = 0; x < w; x++) {
+ if (uncorrectedRawValues)
+ dest[x] = *in;
+ else
+ mRaw->setWithLookUp(*in, reinterpret_cast<uchar8*>(&dest[x]), &random);
+ in++;
+ }
+ }
+}
+
+template void UncompressedDecompressor::decode8BitRaw<false>(uint32 w, uint32 h);
+template void UncompressedDecompressor::decode8BitRaw<true>(uint32 w, uint32 h);
+
+template <Endianness e, bool interlaced, bool skips>
+void UncompressedDecompressor::decode12BitRaw(uint32 w, uint32 h) {
+ static constexpr const auto bits = 12;
+
+ static_assert(e == Endianness::little || e == Endianness::big,
+ "unknown endiannes");
+
+ static constexpr const auto shift = 16 - bits;
+ static constexpr const auto pack = 8 - shift;
+ static constexpr const auto mask = (1 << pack) - 1;
+
+ static_assert(bits == 12 && pack == 4, "wrong pack");
+
+ static_assert(bits == 12 && mask == 0x0f, "wrong mask");
+
+ uint32 perline = bytesPerLine(w, skips);
+
+ sanityCheck(&h, perline);
+
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+
+ // FIXME: maybe check size of interlaced data?
+ const uchar8* in = input.peekData(perline * h);
+ uint32 half = (h + 1) >> 1;
+ for (uint32 row = 0; row < h; row++) {
+ uint32 y = !interlaced ? row : row % half * 2 + row / half;
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+
+ if (interlaced && y == 1) {
+ // The second field starts at a 2048 byte aligment
+ const uint32 offset = ((half * w * 3 / 2 >> 11) + 1) << 11;
+ input.skipBytes(offset);
+ in = input.peekData(perline * (h - row));
+ }
+
+ for (uint32 x = 0; x < w; x += 2, in += 3) {
+ uint32 g1 = in[0];
+ uint32 g2 = in[1];
+
+ auto process = [dest](uint32 i, bool invert, uint32 p1, uint32 p2) {
+ if (!(invert ^ (e == Endianness::little)))
+ dest[i] = (p1 << pack) | (p2 >> pack);
+ else
+ dest[i] = ((p2 & mask) << 8) | p1;
+ };
+
+ process(x, false, g1, g2);
+
+ g1 = in[2];
+
+ process(x + 1, true, g1, g2);
+
+ if (skips && ((x % 10) == 8))
+ in++;
+ }
+ }
+ input.skipBytes(input.getRemainSize());
+}
+
+template void
+UncompressedDecompressor::decode12BitRaw<Endianness::little, false, false>(
+ uint32 w, uint32 h);
+template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, false, false>(
+ uint32 w, uint32 h);
+template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, true, false>(
+ uint32 w, uint32 h);
+template void
+UncompressedDecompressor::decode12BitRaw<Endianness::little, false, true>(
+ uint32 w, uint32 h);
+template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, false, true>(
+ uint32 w, uint32 h);
+
+template <Endianness e>
+void UncompressedDecompressor::decode12BitRawUnpackedLeftAligned(uint32 w,
+ uint32 h) {
+ static_assert(e == Endianness::big, "unknown endiannes");
+
+ sanityCheck(w, &h, 2);
+
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+ const uchar8* in = input.getData(w * h * 2);
+
+ for (uint32 y = 0; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+ for (uint32 x = 0; x < w; x += 1, in += 2) {
+ uint32 g1 = in[0];
+ uint32 g2 = in[1];
+
+ if (e == Endianness::big)
+ dest[x] = (((g1 << 8) | (g2 & 0xf0)) >> 4);
+ }
+ }
+}
+
+template void
+UncompressedDecompressor::decode12BitRawUnpackedLeftAligned<Endianness::big>(
+ uint32 w, uint32 h);
+
+template <int bits, Endianness e>
+void UncompressedDecompressor::decodeRawUnpacked(uint32 w, uint32 h) {
+ static_assert(bits == 12 || bits == 14 || bits == 16, "unhandled bitdepth");
+ static_assert(e == Endianness::little || e == Endianness::big,
+ "unknown endiannes");
+
+ static constexpr const auto shift = 16 - bits;
+ static constexpr const auto mask = (1 << (8 - shift)) - 1;
+
+ static_assert((bits == 12 && mask == 0x0f) || bits != 12, "wrong mask");
+ static_assert((bits == 14 && mask == 0x3f) || bits != 14, "wrong mask");
+ static_assert((bits == 16 && mask == 0xff) || bits != 16, "wrong mask");
+
+ sanityCheck(w, &h, 2);
+
+ uchar8* data = mRaw->getData();
+ uint32 pitch = mRaw->pitch;
+ const uchar8* in = input.getData(w * h * 2);
+
+ for (uint32 y = 0; y < h; y++) {
+ auto* dest = reinterpret_cast<ushort16*>(&data[y * pitch]);
+ for (uint32 x = 0; x < w; x += 1, in += 2) {
+ uint32 g1 = in[0];
+ uint32 g2 = in[1];
+
+ if (e == Endianness::little)
+ dest[x] = ((g2 << 8) | g1) >> shift;
+ else
+ dest[x] = ((g1 & mask) << 8) | g2;
+ }
+ }
+}
+
+template void
+UncompressedDecompressor::decodeRawUnpacked<12, Endianness::little>(uint32 w,
+ uint32 h);
+template void
+UncompressedDecompressor::decodeRawUnpacked<12, Endianness::big>(uint32 w,
+ uint32 h);
+template void
+UncompressedDecompressor::decodeRawUnpacked<14, Endianness::big>(uint32 w,
+ uint32 h);
+template void
+UncompressedDecompressor::decodeRawUnpacked<16, Endianness::little>(uint32 w,
+ uint32 h);
+template void
+UncompressedDecompressor::decodeRawUnpacked<16, Endianness::big>(uint32 w,
+ uint32 h);
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.h
new file mode 100644
index 00000000..98e7e035
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/UncompressedDecompressor.h
@@ -0,0 +1,135 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2016-2019 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/Common.h" // for uint32, BitOrder
+#include "common/RawImage.h" // for RawImage
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/Buffer.h" // for Buffer, Buffer::size_type
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness
+#include <utility> // for move
+
+namespace rawspeed {
+
+class iPoint2D;
+
+class UncompressedDecompressor final : public AbstractDecompressor {
+ ByteStream input;
+ RawImage mRaw;
+
+ // check buffer size, throw, or compute minimal height that can be decoded
+ void sanityCheck(const uint32* h, int bytesPerLine);
+
+ // check buffer size, throw, or compute minimal height that can be decoded
+ void sanityCheck(uint32 w, const uint32* h, int bpp);
+
+ // for special packed formats
+ int bytesPerLine(int w, bool skips);
+
+public:
+ UncompressedDecompressor(ByteStream input_, const RawImage& img)
+ : input(std::move(input_)), mRaw(img) {}
+
+ UncompressedDecompressor(const Buffer& data, Buffer::size_type offset,
+ Buffer::size_type size, const RawImage& img)
+ : UncompressedDecompressor(ByteStream(data, offset, size), img) {}
+
+ UncompressedDecompressor(const Buffer& data, Buffer::size_type offset,
+ const RawImage& img)
+ : UncompressedDecompressor(ByteStream(data, offset), img) {}
+
+ UncompressedDecompressor(const Buffer& data, const RawImage& img)
+ : UncompressedDecompressor(data, 0, img) {}
+
+ /* Helper function for decoders, that will unpack uncompressed image data */
+ /* input: Input image, positioned at first pixel */
+ /* size: Size of the image to decode in pixels */
+ /* offset: offset to write the data into the final image */
+ /* inputPitch: Number of bytes between each line in the input image */
+ /* bitPerPixel: Number of bits to read for each input pixel. */
+ /* order: Order of the bits - see Common.h for possibilities. */
+ void readUncompressedRaw(const iPoint2D& size, const iPoint2D& offset,
+ int inputPitchBytes, int bitPerPixel,
+ BitOrder order);
+
+ /* Faster versions for unpacking 8 bit data */
+ template <bool uncorrectedRawValues> void decode8BitRaw(uint32 w, uint32 h);
+
+ /* Faster version for unpacking 12 bit data */
+ /* interlaced - is data with interlaced lines ? */
+ /* skips - is there control byte every 10 pixels ? */
+ template <Endianness e, bool interlaced = false, bool skips = false>
+ void decode12BitRaw(uint32 w, uint32 h);
+
+ /* Faster version for reading unpacked 12 bit data that is left aligned
+ * (needs >> 4 shift) */
+ template <Endianness e>
+ void decode12BitRawUnpackedLeftAligned(uint32 w, uint32 h);
+
+ /* Faster version for reading unpacked data */
+ template <int bits, Endianness e> void decodeRawUnpacked(uint32 w, uint32 h);
+};
+
+extern template void UncompressedDecompressor::decode8BitRaw<false>(uint32 w,
+ uint32 h);
+extern template void UncompressedDecompressor::decode8BitRaw<true>(uint32 w,
+ uint32 h);
+
+extern template void
+UncompressedDecompressor::decode12BitRaw<Endianness::little, false, false>(
+ uint32 w, uint32 h);
+extern template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, false, false>(
+ uint32 w, uint32 h);
+extern template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, true, false>(
+ uint32 w, uint32 h);
+extern template void
+UncompressedDecompressor::decode12BitRaw<Endianness::little, false, true>(
+ uint32 w, uint32 h);
+extern template void
+UncompressedDecompressor::decode12BitRaw<Endianness::big, false, true>(
+ uint32 w, uint32 h);
+
+extern template void
+UncompressedDecompressor::decode12BitRawUnpackedLeftAligned<Endianness::big>(
+ uint32 w, uint32 h);
+
+extern template void
+UncompressedDecompressor::decodeRawUnpacked<12, Endianness::little>(uint32 w,
+ uint32 h);
+extern template void
+UncompressedDecompressor::decodeRawUnpacked<12, Endianness::big>(uint32 w,
+ uint32 h);
+extern template void
+UncompressedDecompressor::decodeRawUnpacked<14, Endianness::big>(uint32 w,
+ uint32 h);
+extern template void
+UncompressedDecompressor::decodeRawUnpacked<16, Endianness::little>(uint32 w,
+ uint32 h);
+extern template void
+UncompressedDecompressor::decodeRawUnpacked<16, Endianness::big>(uint32 w,
+ uint32 h);
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.cpp
b/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.cpp
new file mode 100644
index 00000000..3a07fa2a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.cpp
@@ -0,0 +1,837 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Stefan Löffler
+ Copyright (C) 2018-2019 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
+*/
+
+/*
+ This is a decompressor for VC-5 raw compression algo, as used in GoPro raws.
+ This implementation is similar to that one of the official reference
+ implementation of the https://github.com/gopro/gpr project, and is producing
+ bitwise-identical output as compared with the Adobe DNG Converter
+ implementation.
+ */
+
+#include "rawspeedconfig.h"
+#include "decompressors/VC5Decompressor.h"
+#include "common/Array2DRef.h" // for Array2DRef
+#include "common/Optional.h" // for Optional
+#include "common/Point.h" // for iPoint2D
+#include "common/RawspeedException.h" // for RawspeedException
+#include "common/SimpleLUT.h" // for SimpleLUT, SimpleLUT<>::va...
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include "io/Endianness.h" // for Endianness, Endianness::big
+#include <cassert> // for assert
+#include <cmath> // for pow
+#include <initializer_list> // for initializer_list
+#include <limits> // for numeric_limits
+#include <string> // for string
+#include <utility> // for move
+
+namespace {
+
+// Definitions needed by table17.inc
+// Taken from
+// https://github.com/gopro/gpr/blob/a513701afce7b03173213a2f67dfd9dd28fa1868/source/lib/vc5_decoder/vlc.h
+struct RLV {
+ uint_fast8_t size; //!< Size of code word in bits
+ uint32_t bits; //!< Code word bits right justified
+ uint16_t count; //!< Run length
+ uint16_t value; //!< Run value (unsigned)
+};
+#define RLVTABLE(n) \
+ struct { \
+ const uint32_t length; \
+ const RLV entries[n]; \
+ } constexpr
+#include "gopro/vc5/table17.inc"
+
+constexpr int16_t decompand(int16_t val) {
+ double c = val;
+ // Invert companding curve
+ c += (c * c * c * 768) / (255. * 255. * 255.);
+ if (c > std::numeric_limits<int16_t>::max())
+ return std::numeric_limits<int16_t>::max();
+ if (c < std::numeric_limits<int16_t>::min())
+ return std::numeric_limits<int16_t>::min();
+ return c;
+}
+
+#ifndef NDEBUG
+int ignore = []() {
+ for (const RLV& entry : table17.entries) {
+ assert(((-decompand(entry.value)) == decompand(-int16_t(entry.value))) &&
+ "negation of decompanded value is the same as decompanding of "
+ "negated value");
+ }
+ return 0;
+}();
+#endif
+
+const std::array<RLV, table17.length> decompandedTable17 = []() {
+ std::array<RLV, table17.length> d;
+ for (auto i = 0U; i < table17.length; i++) {
+ d[i] = table17.entries[i];
+ d[i].value = decompand(table17.entries[i].value);
+ }
+ return d;
+}();
+
+} // namespace
+
+#define PRECISION_MIN 8
+#define PRECISION_MAX 16
+
+#define MARKER_BAND_END 1
+
+namespace rawspeed {
+
+void VC5Decompressor::Wavelet::setBandValid(const int band) {
+ mDecodedBandMask |= (1 << band);
+}
+
+bool VC5Decompressor::Wavelet::isBandValid(const int band) const {
+ return mDecodedBandMask & (1 << band);
+}
+
+bool VC5Decompressor::Wavelet::allBandsValid() const {
+ return mDecodedBandMask == static_cast<uint32>((1 << numBands) - 1);
+}
+
+Array2DRef<const int16_t>
+VC5Decompressor::Wavelet::bandAsArray2DRef(const unsigned int iBand) const {
+ return {bands[iBand]->data.data(), width, height};
+}
+
+namespace {
+auto convolute = [](int x, int y, std::array<int, 4> muls,
+ const Array2DRef<const int16_t> high, auto lowGetter,
+ int DescaleShift = 0) {
+ auto highCombined = muls[0] * high(x, y);
+ auto lowsCombined = [muls, lowGetter]() {
+ int lows = 0;
+ for (int i = 0; i < 3; i++)
+ lows += muls[1 + i] * lowGetter(i);
+ return lows;
+ }();
+ // Round up 'lows' up
+ lowsCombined += 4;
+ // And finally 'average' them.
+ auto lowsRounded = lowsCombined >> 3;
+ auto total = highCombined + lowsRounded;
+ // Descale it.
+ total <<= DescaleShift;
+ // And average it.
+ total >>= 1;
+ return total;
+};
+
+struct ConvolutionParams {
+ struct First {
+ static constexpr std::array<int, 4> mul_even = {+1, +11, -4, +1};
+ static constexpr std::array<int, 4> mul_odd = {-1, +5, +4, -1};
+ static constexpr int coord_shift = 0;
+ };
+ static constexpr First First{};
+
+ struct Middle {
+ static constexpr std::array<int, 4> mul_even = {+1, +1, +8, -1};
+ static constexpr std::array<int, 4> mul_odd = {-1, -1, +8, +1};
+ static constexpr int coord_shift = -1;
+ };
+ static constexpr Middle Middle{};
+
+ struct Last {
+ static constexpr std::array<int, 4> mul_even = {+1, -1, +4, +5};
+ static constexpr std::array<int, 4> mul_odd = {-1, +1, -4, +11};
+ static constexpr int coord_shift = -2;
+ };
+ static constexpr Last Last{};
+};
+
+constexpr std::array<int, 4> ConvolutionParams::First::mul_even;
+constexpr std::array<int, 4> ConvolutionParams::First::mul_odd;
+
+constexpr std::array<int, 4> ConvolutionParams::Middle::mul_even;
+constexpr std::array<int, 4> ConvolutionParams::Middle::mul_odd;
+
+constexpr std::array<int, 4> ConvolutionParams::Last::mul_even;
+constexpr std::array<int, 4> ConvolutionParams::Last::mul_odd;
+
+} // namespace
+
+void VC5Decompressor::Wavelet::reconstructPass(
+ const Array2DRef<int16_t> dst, const Array2DRef<const int16_t> high,
+ const Array2DRef<const int16_t> low) const noexcept {
+ auto process = [low, high, dst](auto segment, int x, int y) {
+ auto lowGetter = [&x, &y, low](int delta) {
+ return low(x, y + decltype(segment)::coord_shift + delta);
+ };
+ auto convolution = [&x, &y, high, lowGetter](std::array<int, 4> muls) {
+ return convolute(x, y, muls, high, lowGetter, /*DescaleShift*/ 0);
+ };
+
+ int even = convolution(decltype(segment)::mul_even);
+ int odd = convolution(decltype(segment)::mul_odd);
+
+ dst(x, 2 * y) = static_cast<int16_t>(even);
+ dst(x, 2 * y + 1) = static_cast<int16_t>(odd);
+ };
+
+ // Vertical reconstruction
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (int y = 0; y < height; ++y) {
+ if (y == 0) {
+ // 1st row
+ for (int x = 0; x < width; ++x)
+ process(ConvolutionParams::First, x, y);
+ } else if (y + 1 < height) {
+ // middle rows
+ for (int x = 0; x < width; ++x)
+ process(ConvolutionParams::Middle, x, y);
+ } else {
+ // last row
+ for (int x = 0; x < width; ++x)
+ process(ConvolutionParams::Last, x, y);
+ }
+ }
+}
+
+void VC5Decompressor::Wavelet::combineLowHighPass(
+ const Array2DRef<int16_t> dst, const Array2DRef<const int16_t> low,
+ const Array2DRef<const int16_t> high, int descaleShift,
+ bool clampUint = false) const noexcept {
+ auto process = [low, high, descaleShift, clampUint, dst](auto segment, int x,
+ int y) {
+ auto lowGetter = [&x, &y, low](int delta) {
+ return low(x + decltype(segment)::coord_shift + delta, y);
+ };
+ auto convolution = [&x, &y, high, lowGetter,
+ descaleShift](std::array<int, 4> muls) {
+ return convolute(x, y, muls, high, lowGetter, descaleShift);
+ };
+
+ int even = convolution(decltype(segment)::mul_even);
+ int odd = convolution(decltype(segment)::mul_odd);
+
+ if (clampUint) {
+ even = clampBits(even, 14);
+ odd = clampBits(odd, 14);
+ }
+ dst(2 * x, y) = static_cast<int16_t>(even);
+ dst(2 * x + 1, y) = static_cast<int16_t>(odd);
+ };
+
+ // Horizontal reconstruction
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static)
+#endif
+ for (int y = 0; y < dst.height; ++y) {
+ // First col
+ int x = 0;
+ process(ConvolutionParams::First, x, y);
+ // middle cols
+ for (x = 1; x + 1 < width; ++x) {
+ process(ConvolutionParams::Middle, x, y);
+ }
+ // last col
+ process(ConvolutionParams::Last, x, y);
+ }
+}
+
+void VC5Decompressor::Wavelet::ReconstructableBand::processLow(
+ const Wavelet& wavelet) noexcept {
+ Array2DRef<int16_t> lowpass;
+#ifdef HAVE_OPENMP
+#pragma omp single copyprivate(lowpass)
+#endif
+ lowpass = Array2DRef<int16_t>::create(&lowpass_storage, wavelet.width,
+ 2 * wavelet.height);
+
+ const Array2DRef<const int16_t> highlow = wavelet.bandAsArray2DRef(2);
+ const Array2DRef<const int16_t> lowlow = wavelet.bandAsArray2DRef(0);
+
+ // Reconstruct the "immediates", the actual low pass ...
+ wavelet.reconstructPass(lowpass, highlow, lowlow);
+}
+
+void VC5Decompressor::Wavelet::ReconstructableBand::processHigh(
+ const Wavelet& wavelet) noexcept {
+ Array2DRef<int16_t> highpass;
+#ifdef HAVE_OPENMP
+#pragma omp single copyprivate(highpass)
+#endif
+ highpass = Array2DRef<int16_t>::create(&highpass_storage, wavelet.width,
+ 2 * wavelet.height);
+
+ const Array2DRef<const int16_t> highhigh = wavelet.bandAsArray2DRef(3);
+ const Array2DRef<const int16_t> lowhigh = wavelet.bandAsArray2DRef(1);
+
+ wavelet.reconstructPass(highpass, highhigh, lowhigh);
+}
+
+void VC5Decompressor::Wavelet::ReconstructableBand::combine(
+ const Wavelet& wavelet) noexcept {
+ int16_t descaleShift = (wavelet.prescale == 2 ? 2 : 0);
+
+ Array2DRef<int16_t> dest;
+#ifdef HAVE_OPENMP
+#pragma omp single copyprivate(dest)
+#endif
+ dest =
+ Array2DRef<int16_t>::create(&data, 2 * wavelet.width, 2 * wavelet.height);
+
+ const Array2DRef<int16_t> lowpass(lowpass_storage.data(), wavelet.width,
+ 2 * wavelet.height);
+ const Array2DRef<int16_t> highpass(highpass_storage.data(), wavelet.width,
+ 2 * wavelet.height);
+
+ // And finally, combine the low pass, and high pass.
+ wavelet.combineLowHighPass(dest, lowpass, highpass, descaleShift, clampUint);
+}
+
+void VC5Decompressor::Wavelet::ReconstructableBand::decode(
+ const Wavelet& wavelet) noexcept {
+ assert(wavelet.allBandsValid());
+ assert(data.empty());
+ processLow(wavelet);
+ processHigh(wavelet);
+ combine(wavelet);
+}
+
+VC5Decompressor::VC5Decompressor(ByteStream bs, const RawImage& img)
+ : mRaw(img), mBs(std::move(bs)) {
+ if (!mRaw->dim.hasPositiveArea())
+ ThrowRDE("Bad image dimensions.");
+
+ if (mRaw->dim.x % mVC5.patternWidth != 0)
+ ThrowRDE("Width %u is not a multiple of %u", mRaw->dim.x,
+ mVC5.patternWidth);
+
+ if (mRaw->dim.y % mVC5.patternHeight != 0)
+ ThrowRDE("Height %u is not a multiple of %u", mRaw->dim.y,
+ mVC5.patternHeight);
+
+ // Initialize wavelet sizes.
+ for (Channel& channel : channels) {
+ channel.width = mRaw->dim.x / mVC5.patternWidth;
+ channel.height = mRaw->dim.y / mVC5.patternHeight;
+
+ uint16_t waveletWidth = channel.width;
+ uint16_t waveletHeight = channel.height;
+ for (Wavelet& wavelet : channel.wavelets) {
+ // Pad dimensions as necessary and divide them by two for the next wavelet
+ for (auto* dimension : {&waveletWidth, &waveletHeight})
+ *dimension = roundUpDivision(*dimension, 2);
+ wavelet.width = waveletWidth;
+ wavelet.height = waveletHeight;
+ }
+ }
+
+ if (img->whitePoint <= 0 || img->whitePoint > int(((1U << 16U) - 1U)))
+ ThrowRDE("Bad white level %i", img->whitePoint);
+
+ outputBits = 0;
+ for (int wp = img->whitePoint; wp != 0; wp >>= 1)
+ ++outputBits;
+ assert(outputBits <= 16);
+
+ parseVC5();
+}
+
+void VC5Decompressor::initVC5LogTable() {
+ mVC5LogTable = decltype(mVC5LogTable)(
+ [outputBits = outputBits](unsigned i, unsigned tableSize) {
+ // The vanilla "inverse log" curve for decoding.
+ auto normalizedCurve = [](auto normalizedI) {
+ return (std::pow(113.0, normalizedI) - 1) / 112.0;
+ };
+
+ auto normalizeI = [tableSize](auto x) { return x / (tableSize - 1.0); };
+ auto denormalizeY = [maxVal = std::numeric_limits<ushort16>::max()](
+ auto y) { return maxVal * y; };
+ // Adjust for output whitelevel bitdepth.
+ auto rescaleY = [outputBits](auto y) {
+ auto scale = 16 - outputBits;
+ return y >> scale;
+ };
+
+ const auto naiveY = denormalizeY(normalizedCurve(normalizeI(i)));
+ const auto intY = static_cast<unsigned int>(naiveY);
+ const auto rescaledY = rescaleY(intY);
+ return rescaledY;
+ });
+}
+
+void VC5Decompressor::parseVC5() {
+ mBs.setByteOrder(Endianness::big);
+
+ assert(mRaw->dim.x > 0);
+ assert(mRaw->dim.y > 0);
+
+ // All VC-5 data must start with "VC-%" (0x56432d35)
+ if (mBs.getU32() != 0x56432d35)
+ ThrowRDE("not a valid VC-5 datablock");
+
+ bool done = false;
+ while (!done) {
+ auto tag = static_cast<VC5Tag>(mBs.getU16());
+ ushort16 val = mBs.getU16();
+
+ bool optional = matches(tag, VC5Tag::Optional);
+ if (optional)
+ tag = -tag;
+
+ switch (tag) {
+ case VC5Tag::ChannelCount:
+ if (val != numChannels)
+ ThrowRDE("Bad channel count %u, expected %u", val, numChannels);
+ break;
+ case VC5Tag::ImageWidth:
+ if (val != mRaw->dim.x)
+ ThrowRDE("Image width mismatch: %u vs %u", val, mRaw->dim.x);
+ break;
+ case VC5Tag::ImageHeight:
+ if (val != mRaw->dim.y)
+ ThrowRDE("Image height mismatch: %u vs %u", val, mRaw->dim.y);
+ break;
+ case VC5Tag::LowpassPrecision:
+ if (val < PRECISION_MIN || val > PRECISION_MAX)
+ ThrowRDE("Invalid precision %i", val);
+ mVC5.lowpassPrecision = val;
+ break;
+ case VC5Tag::ChannelNumber:
+ if (val >= numChannels)
+ ThrowRDE("Bad channel number (%u)", val);
+ mVC5.iChannel = val;
+ break;
+ case VC5Tag::ImageFormat:
+ if (val != mVC5.imgFormat)
+ ThrowRDE("Image format %i is not 4(RAW)", val);
+ break;
+ case VC5Tag::SubbandCount:
+ if (val != numSubbands)
+ ThrowRDE("Unexpected subband count %u, expected %u", val, numSubbands);
+ break;
+ case VC5Tag::MaxBitsPerComponent:
+ if (val != VC5_LOG_TABLE_BITWIDTH) {
+ ThrowRDE("Bad bits per componend %u, not %u", val,
+ VC5_LOG_TABLE_BITWIDTH);
+ }
+ break;
+ case VC5Tag::PatternWidth:
+ if (val != mVC5.patternWidth)
+ ThrowRDE("Bad pattern width %u, not %u", val, mVC5.patternWidth);
+ break;
+ case VC5Tag::PatternHeight:
+ if (val != mVC5.patternHeight)
+ ThrowRDE("Bad pattern height %u, not %u", val, mVC5.patternHeight);
+ break;
+ case VC5Tag::SubbandNumber:
+ if (val >= numSubbands)
+ ThrowRDE("Bad subband number %u", val);
+ mVC5.iSubband = val;
+ break;
+ case VC5Tag::Quantization:
+ mVC5.quantization = static_cast<short16>(val);
+ break;
+ case VC5Tag::ComponentsPerSample:
+ if (val != mVC5.cps)
+ ThrowRDE("Bad compnent per sample count %u, not %u", val, mVC5.cps);
+ break;
+ case VC5Tag::PrescaleShift:
+ // FIXME: something is wrong. We get this before VC5Tag::ChannelNumber.
+ // Defaulting to 'mVC5.iChannel=0' seems to work *for existing samples*.
+ for (int iWavelet = 0; iWavelet < numWaveletLevels; ++iWavelet) {
+ auto& channel = channels[mVC5.iChannel];
+ auto& wavelet = channel.wavelets[iWavelet];
+ wavelet.prescale = (val >> (14 - 2 * iWavelet)) & 0x03;
+ }
+ break;
+ default: { // A chunk.
+ unsigned int chunkSize = 0;
+ if (matches(tag, VC5Tag::LARGE_CHUNK)) {
+ chunkSize = static_cast<unsigned int>(
+ ((static_cast<std::underlying_type<VC5Tag>::type>(tag) & 0xff)
+ << 16) |
+ (val & 0xffff));
+ } else if (matches(tag, VC5Tag::SMALL_CHUNK)) {
+ chunkSize = (val & 0xffff);
+ }
+
+ if (is(tag, VC5Tag::LargeCodeblock)) {
+ parseLargeCodeblock(mBs.getStream(chunkSize, 4));
+ break;
+ }
+
+ // And finally, we got here if we didn't handle this tag/maybe-chunk.
+
+ // Magic, all the other 'large' chunks are actually optional,
+ // and don't specify any chunk bytes-to-be-skipped.
+ if (matches(tag, VC5Tag::LARGE_CHUNK)) {
+ optional = true;
+ chunkSize = 0;
+ }
+
+ if (!optional) {
+ ThrowRDE("Unknown (unhandled) non-optional Tag 0x%04hx",
+ static_cast<std::underlying_type<VC5Tag>::type>(tag));
+ }
+
+ if (chunkSize)
+ mBs.skipBytes(chunkSize, 4);
+
+ break;
+ }
+ }
+
+ done = true;
+ for (int iChannel = 0; iChannel < numChannels && done; ++iChannel) {
+ Wavelet& wavelet = channels[iChannel].wavelets[0];
+ if (!wavelet.allBandsValid())
+ done = false;
+ }
+ }
+}
+
+VC5Decompressor::Wavelet::LowPassBand::LowPassBand(const Wavelet& wavelet,
+ ByteStream bs_,
+ ushort16 lowpassPrecision_)
+ : AbstractDecodeableBand(std::move(bs_)),
+ lowpassPrecision(lowpassPrecision_) {
+ // Low-pass band is a uncompressed version of the image, hugely downscaled.
+ // It consists of width * height pixels, `lowpassPrecision` each.
+ // We can easily check that we have sufficient amount of bits to decode it.
+ const auto waveletArea = iPoint2D(wavelet.width, wavelet.height).area();
+ const auto bitsTotal = waveletArea * lowpassPrecision;
+ const auto bytesTotal = roundUpDivision(bitsTotal, 8);
+ bs = bs.getStream(bytesTotal); // And clamp the size while we are at it.
+}
+
+void VC5Decompressor::Wavelet::LowPassBand::decode(const Wavelet& wavelet) {
+ const auto dst =
+ Array2DRef<int16_t>::create(&data, wavelet.width, wavelet.height);
+
+ BitPumpMSB bits(bs);
+ for (auto row = 0; row < dst.height; ++row) {
+ for (auto col = 0; col < dst.width; ++col)
+ dst(col, row) = static_cast<int16_t>(bits.getBits(lowpassPrecision));
+ }
+}
+
+void VC5Decompressor::Wavelet::HighPassBand::decode(const Wavelet& wavelet) {
+ auto dequantize = [quant = quant](int16_t val) -> int16_t {
+ return val * quant;
+ };
+
+ Array2DRef<int16_t>::create(&data, wavelet.width, wavelet.height);
+
+ BitPumpMSB bits(bs);
+ // decode highpass band
+ int pixelValue = 0;
+ unsigned int count = 0;
+ int nPixels = wavelet.width * wavelet.height;
+ for (int iPixel = 0; iPixel < nPixels;) {
+ getRLV(&bits, &pixelValue, &count);
+ for (; count > 0; --count) {
+ if (iPixel >= nPixels)
+ ThrowRDE("Buffer overflow");
+ data[iPixel] = dequantize(pixelValue);
+ ++iPixel;
+ }
+ }
+ getRLV(&bits, &pixelValue, &count);
+ static_assert(decompand(MARKER_BAND_END) == MARKER_BAND_END, "passthrought");
+ if (pixelValue != MARKER_BAND_END || count != 0)
+ ThrowRDE("EndOfBand marker not found");
+}
+
+void VC5Decompressor::parseLargeCodeblock(const ByteStream& bs) {
+ static const auto subband_wavelet_index = []() {
+ std::array<int, numSubbands> wavelets;
+ int wavelet = 0;
+ for (auto i = wavelets.size() - 1; i > 0;) {
+ for (auto t = 0; t < numWaveletLevels; t++) {
+ wavelets[i] = wavelet;
+ i--;
+ }
+ if (i > 0)
+ wavelet++;
+ }
+ wavelets.front() = wavelet;
+ return wavelets;
+ }();
+ static const auto subband_band_index = []() {
+ std::array<int, numSubbands> bands;
+ bands.front() = 0;
+ for (auto i = 1U; i < bands.size();) {
+ for (int t = 1; t <= numWaveletLevels;) {
+ bands[i] = t;
+ t++;
+ i++;
+ }
+ }
+ return bands;
+ }();
+
+ if (!mVC5.iSubband.hasValue())
+ ThrowRDE("Did not see VC5Tag::SubbandNumber yet");
+
+ const int idx = subband_wavelet_index[mVC5.iSubband.getValue()];
+ const int band = subband_band_index[mVC5.iSubband.getValue()];
+
+ auto& wavelets = channels[mVC5.iChannel].wavelets;
+
+ Wavelet& wavelet = wavelets[idx];
+ if (wavelet.isBandValid(band)) {
+ ThrowRDE("Band %u for wavelet %u on channel %u was already seen", band, idx,
+ mVC5.iChannel);
+ }
+
+ std::unique_ptr<Wavelet::AbstractBand>& dstBand = wavelet.bands[band];
+ if (mVC5.iSubband.getValue() == 0) {
+ assert(band == 0);
+ // low-pass band, only one, for the smallest wavelet, per channel per image
+ if (!mVC5.lowpassPrecision.hasValue())
+ ThrowRDE("Did not see VC5Tag::LowpassPrecision yet");
+ dstBand = std::make_unique<Wavelet::LowPassBand>(
+ wavelet, bs, mVC5.lowpassPrecision.getValue());
+ mVC5.lowpassPrecision.reset();
+ } else {
+ if (!mVC5.quantization.hasValue())
+ ThrowRDE("Did not see VC5Tag::Quantization yet");
+ dstBand = std::make_unique<Wavelet::HighPassBand>(
+ bs, mVC5.quantization.getValue());
+ mVC5.quantization.reset();
+ }
+ wavelet.setBandValid(band);
+
+ // If this wavelet is fully specified, mark the low-pass band of the
+ // next lower wavelet as specified.
+ if (idx > 0 && wavelet.allBandsValid()) {
+ Wavelet& nextWavelet = wavelets[idx - 1];
+ assert(!nextWavelet.isBandValid(0));
+ nextWavelet.bands[0] = std::make_unique<Wavelet::ReconstructableBand>();
+ nextWavelet.setBandValid(0);
+ }
+
+ mVC5.iSubband.reset();
+}
+
+void VC5Decompressor::prepareBandDecodingPlan() {
+ assert(allDecodeableBands.empty());
+ allDecodeableBands.reserve(numSubbandsTotal);
+ // All the high-pass bands for all wavelets,
+ // in this specific order of decreasing worksize.
+ for (int waveletLevel = 0; waveletLevel < numWaveletLevels; waveletLevel++) {
+ for (auto channelId = 0; channelId < numChannels; channelId++) {
+ for (int bandId = 1; bandId <= numHighPassBands; bandId++) {
+ auto& channel = channels[channelId];
+ auto& wavelet = channel.wavelets[waveletLevel];
+ auto* band = wavelet.bands[bandId].get();
+ auto* decodeableHighPassBand =
+ dynamic_cast<Wavelet::HighPassBand*>(band);
+ allDecodeableBands.emplace_back(decodeableHighPassBand, wavelet);
+ }
+ }
+ }
+ // The low-pass bands at the end. I'm guessing they should be fast to
+ // decode.
+ for (Channel& channel : channels) {
+ // Low-pass band of the smallest wavelet.
+ Wavelet& smallestWavelet = channel.wavelets.back();
+ auto* decodeableLowPassBand =
+ dynamic_cast<Wavelet::LowPassBand*>(smallestWavelet.bands[0].get());
+ allDecodeableBands.emplace_back(decodeableLowPassBand, smallestWavelet);
+ }
+ assert(allDecodeableBands.size() == numSubbandsTotal);
+}
+
+void VC5Decompressor::prepareBandReconstruction() {
+ assert(reconstructionSteps.empty());
+ reconstructionSteps.reserve(numLowPassBandsTotal);
+ // For every channel, recursively reconstruct the low-pass bands.
+ for (auto& channel : channels) {
+ // Reconstruct the intermediate lowpass bands.
+ for (int waveletLevel = numWaveletLevels - 1; waveletLevel > 0;
+ waveletLevel--) {
+ Wavelet* wavelet = &(channel.wavelets[waveletLevel]);
+ Wavelet& nextWavelet = channel.wavelets[waveletLevel - 1];
+
+ auto* band = dynamic_cast<Wavelet::ReconstructableBand*>(
+ nextWavelet.bands[0].get());
+ reconstructionSteps.emplace_back(wavelet, band);
+ }
+ // Finally, reconstruct the final lowpass band.
+ Wavelet* wavelet = &(channel.wavelets.front());
+ reconstructionSteps.emplace_back(wavelet, &(channel.band));
+ }
+ assert(reconstructionSteps.size() == numLowPassBandsTotal);
+}
+
+void VC5Decompressor::prepareDecodingPlan() {
+ prepareBandDecodingPlan();
+ prepareBandReconstruction();
+}
+
+void VC5Decompressor::decodeThread(bool* exceptionThrown) const noexcept {
+ // Decode all the existing bands. May fail.
+ decodeBands(exceptionThrown);
+
+ // Proceed only if decoding did not fail.
+ if (*exceptionThrown)
+ return;
+
+ // And now, reconstruct the low-pass bands.
+ reconstructLowpassBands();
+
+ // And finally!
+ combineFinalLowpassBands();
+}
+
+void VC5Decompressor::decode(unsigned int offsetX, unsigned int offsetY,
+ unsigned int width, unsigned int height) {
+ if (offsetX || offsetY || mRaw->dim != iPoint2D(width, height))
+ ThrowRDE("VC5Decompressor expects to fill the whole image, not some tile.");
+
+ initVC5LogTable();
+
+ prepareDecodingPlan();
+
+ bool exceptionThrown = false;
+#ifdef HAVE_OPENMP
+#pragma omp parallel default(none) shared(exceptionThrown) \
+ num_threads(rawspeed_get_number_of_processor_cores())
+#endif
+ decodeThread(&exceptionThrown);
+
+ std::string firstErr;
+ if (mRaw->isTooManyErrors(1, &firstErr)) {
+ assert(exceptionThrown);
+ ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
+ firstErr.c_str());
+ } else {
+ assert(!exceptionThrown);
+ }
+}
+
+void VC5Decompressor::decodeBands(bool* exceptionThrown) const noexcept {
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(dynamic, 1)
+#endif
+ for (auto decodeableBand = allDecodeableBands.begin();
+ decodeableBand < allDecodeableBands.end(); ++decodeableBand) {
+ try {
+ decodeableBand->band->decode(decodeableBand->wavelet);
+ } catch (RawspeedException& err) {
+ // Propagate the exception out of OpenMP magic.
+ mRaw->setError(err.what());
+#ifdef HAVE_OPENMP
+#pragma omp atomic write
+#endif
+ *exceptionThrown = true;
+#ifdef HAVE_OPENMP
+#pragma omp cancel for
+#endif
+ }
+ }
+}
+
+void VC5Decompressor::reconstructLowpassBands() const noexcept {
+ for (const ReconstructionStep& step : reconstructionSteps) {
+ step.band.decode(step.wavelet);
+
+#ifdef HAVE_OPENMP
+#pragma omp single nowait
+#endif
+ step.wavelet.clear(); // we no longer need it.
+ }
+}
+
+void VC5Decompressor::combineFinalLowpassBands() const noexcept {
+ const Array2DRef<uint16_t> out(reinterpret_cast<uint16_t*>(mRaw->getData()),
+ mRaw->dim.x, mRaw->dim.y,
+ mRaw->pitch / sizeof(uint16_t));
+
+ const int width = out.width / 2;
+ const int height = out.height / 2;
+
+ const Array2DRef<const int16_t> lowbands0 = Array2DRef<const int16_t>(
+ channels[0].band.data.data(), channels[0].width, channels[0].height);
+ const Array2DRef<const int16_t> lowbands1 = Array2DRef<const int16_t>(
+ channels[1].band.data.data(), channels[1].width, channels[1].height);
+ const Array2DRef<const int16_t> lowbands2 = Array2DRef<const int16_t>(
+ channels[2].band.data.data(), channels[2].width, channels[2].height);
+ const Array2DRef<const int16_t> lowbands3 = Array2DRef<const int16_t>(
+ channels[3].band.data.data(), channels[3].width, channels[3].height);
+
+ // Convert to RGGB output
+#ifdef HAVE_OPENMP
+#pragma omp for schedule(static) collapse(2)
+#endif
+ for (int row = 0; row < height; ++row) {
+ for (int col = 0; col < width; ++col) {
+ const int mid = 2048;
+
+ int gs = lowbands0(col, row);
+ int rg = lowbands1(col, row) - mid;
+ int bg = lowbands2(col, row) - mid;
+ int gd = lowbands3(col, row) - mid;
+
+ int r = gs + 2 * rg;
+ int b = gs + 2 * bg;
+ int g1 = gs + gd;
+ int g2 = gs - gd;
+
+ out(2 * col + 0, 2 * row + 0) = static_cast<uint16_t>(mVC5LogTable[r]);
+ out(2 * col + 1, 2 * row + 0) = static_cast<uint16_t>(mVC5LogTable[g1]);
+ out(2 * col + 0, 2 * row + 1) = static_cast<uint16_t>(mVC5LogTable[g2]);
+ out(2 * col + 1, 2 * row + 1) = static_cast<uint16_t>(mVC5LogTable[b]);
+ }
+ }
+}
+
+inline void VC5Decompressor::getRLV(BitPumpMSB* bits, int* value,
+ unsigned int* count) {
+ unsigned int iTab;
+
+ static constexpr auto maxBits = 1 + table17.entries[table17.length - 1].size;
+
+ // Ensure the maximum number of bits are cached to make peekBits() as fast as
+ // possible.
+ bits->fill(maxBits);
+ for (iTab = 0; iTab < table17.length; ++iTab) {
+ if (decompandedTable17[iTab].bits ==
+ bits->peekBitsNoFill(decompandedTable17[iTab].size))
+ break;
+ }
+ if (iTab >= table17.length)
+ ThrowRDE("Code not found in codebook");
+
+ bits->skipBitsNoFill(decompandedTable17[iTab].size);
+ *value = decompandedTable17[iTab].value;
+ *count = decompandedTable17[iTab].count;
+ if (*value != 0) {
+ if (bits->getBitsNoFill(1))
+ *value = -(*value);
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.h
b/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.h
new file mode 100644
index 00000000..e8f25244
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/VC5Decompressor.h
@@ -0,0 +1,241 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2018 Stefan Löffler
+ 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/Array2DRef.h" // for Array2DRef
+#include "common/Common.h" // for ushort16, short16
+#include "common/DefaultInitAllocatorAdaptor.h" // for DefaultInitAllocatorA...
+#include "common/Optional.h" // for Optional
+#include "common/RawImage.h" // for RawImage
+#include "common/SimpleLUT.h" // for SimpleLUT, SimpleLUT...
+#include "decompressors/AbstractDecompressor.h" // for AbstractDecompressor
+#include "io/BitPumpMSB.h" // for BitPumpMSB
+#include "io/ByteStream.h" // for ByteStream
+#include <array> // for array
+#include <cstdint> // for int16_t, uint16_t
+#include <memory> // for unique_ptr
+#include <type_traits> // for underlying_type, und...
+#include <utility> // for move
+#include <vector> // for vector
+
+namespace rawspeed {
+
+const int MAX_NUM_PRESCALE = 8;
+
+// Decompresses VC-5 as used by GoPro
+
+enum class VC5Tag : int16_t {
+ NoTag = 0x0, // synthetic, not an actual tag
+
+ ChannelCount = 0x000c,
+ ImageWidth = 0x0014,
+ ImageHeight = 0x0015,
+ LowpassPrecision = 0x0023,
+ SubbandCount = 0x000E,
+ SubbandNumber = 0x0030,
+ Quantization = 0x0035,
+ ChannelNumber = 0x003e,
+ ImageFormat = 0x0054,
+ MaxBitsPerComponent = 0x0066,
+ PatternWidth = 0x006a,
+ PatternHeight = 0x006b,
+ ComponentsPerSample = 0x006c,
+ PrescaleShift = 0x006d,
+
+ LARGE_CHUNK = 0x2000,
+ SMALL_CHUNK = 0x4000,
+ UniqueImageIdentifier = 0x4004,
+ LargeCodeblock = 0x6000,
+
+ Optional = int16_t(0x8000U), // only signbit set
+};
+inline VC5Tag operator&(VC5Tag LHS, VC5Tag RHS) {
+ using value_type = std::underlying_type<VC5Tag>::type;
+ return static_cast<VC5Tag>(static_cast<value_type>(LHS) &
+ static_cast<value_type>(RHS));
+}
+inline bool matches(VC5Tag LHS, VC5Tag RHS) {
+ // Are there any common bit set?
+ return (LHS & RHS) != VC5Tag::NoTag;
+}
+inline bool is(VC5Tag LHS, VC5Tag RHS) {
+ // Does LHS have all the RHS bits set?
+ return (LHS & RHS) == RHS;
+}
+inline VC5Tag operator-(VC5Tag tag) {
+ using value_type = std::underlying_type<VC5Tag>::type;
+ // Negate
+ return static_cast<VC5Tag>(-static_cast<value_type>(tag));
+}
+
+class VC5Decompressor final : public AbstractDecompressor {
+ RawImage mRaw;
+ ByteStream mBs;
+
+ static constexpr auto VC5_LOG_TABLE_BITWIDTH = 12;
+ int outputBits;
+ SimpleLUT<unsigned, VC5_LOG_TABLE_BITWIDTH> mVC5LogTable;
+
+ void initVC5LogTable();
+
+ static constexpr int numWaveletLevels = 3;
+ static constexpr int numHighPassBands = 3;
+ static constexpr int numLowPassBands = 1;
+ static constexpr int numSubbands =
+ numLowPassBands + numHighPassBands * numWaveletLevels;
+
+ struct {
+ ushort16 iChannel = 0; // 0'th channel is the default
+ Optional<ushort16> iSubband;
+ Optional<ushort16> lowpassPrecision;
+ Optional<short16> quantization;
+
+ const ushort16 imgFormat = 4;
+ const ushort16 patternWidth = 2;
+ const ushort16 patternHeight = 2;
+ const ushort16 cps = 1;
+ } mVC5;
+
+ class Wavelet {
+ public:
+ int width, height;
+ int16_t prescale;
+
+ struct AbstractBand {
+ std::vector<int16_t, DefaultInitAllocatorAdaptor<int16_t>> data;
+ virtual ~AbstractBand() = default;
+ virtual void decode(const Wavelet& wavelet) = 0;
+ };
+ struct ReconstructableBand final : AbstractBand {
+ bool clampUint;
+ std::vector<int16_t, DefaultInitAllocatorAdaptor<int16_t>>
+ lowpass_storage;
+ std::vector<int16_t, DefaultInitAllocatorAdaptor<int16_t>>
+ highpass_storage;
+ explicit ReconstructableBand(bool clampUint_ = false)
+ : clampUint(clampUint_) {}
+ void processLow(const Wavelet& wavelet) noexcept;
+ void processHigh(const Wavelet& wavelet) noexcept;
+ void combine(const Wavelet& wavelet) noexcept;
+ void decode(const Wavelet& wavelet) noexcept final;
+ };
+ struct AbstractDecodeableBand : AbstractBand {
+ ByteStream bs;
+ explicit AbstractDecodeableBand(ByteStream bs_) : bs(std::move(bs_)) {}
+ };
+ struct LowPassBand final : AbstractDecodeableBand {
+ ushort16 lowpassPrecision;
+ LowPassBand(const Wavelet& wavelet, ByteStream bs_,
+ ushort16 lowpassPrecision_);
+ void decode(const Wavelet& wavelet) final;
+ };
+ struct HighPassBand final : AbstractDecodeableBand {
+ int16_t quant;
+ HighPassBand(ByteStream bs_, int16_t quant_)
+ : AbstractDecodeableBand(std::move(bs_)), quant(quant_) {}
+ void decode(const Wavelet& wavelet) final;
+ };
+
+ static constexpr uint16_t numBands = 4;
+ std::array<std::unique_ptr<AbstractBand>, numBands> bands;
+
+ void clear() {
+ for (auto& band : bands)
+ band.reset();
+ }
+
+ void setBandValid(int band);
+ bool isBandValid(int band) const;
+ uint32_t getValidBandMask() const { return mDecodedBandMask; }
+ bool allBandsValid() const;
+
+ void reconstructPass(Array2DRef<int16_t> dst,
+ Array2DRef<const int16_t> high,
+ Array2DRef<const int16_t> low) const noexcept;
+
+ void combineLowHighPass(Array2DRef<int16_t> dst,
+ Array2DRef<const int16_t> low,
+ Array2DRef<const int16_t> high, int descaleShift,
+ bool clampUint /*= false*/) const noexcept;
+
+ Array2DRef<const int16_t> bandAsArray2DRef(unsigned int iBand) const;
+
+ protected:
+ uint32 mDecodedBandMask = 0;
+ };
+
+ struct Channel {
+ std::array<Wavelet, numWaveletLevels> wavelets;
+
+ Wavelet::ReconstructableBand band{/*clampUint*/ true};
+ // the final lowband.
+ int width, height;
+ };
+
+ static constexpr int numChannels = 4;
+ static constexpr int numSubbandsTotal = numSubbands * numChannels;
+ static constexpr int numLowPassBandsTotal = numWaveletLevels * numChannels;
+ std::array<Channel, numChannels> channels;
+
+ struct DecodeableBand {
+ Wavelet::AbstractDecodeableBand* band;
+ const Wavelet& wavelet;
+ DecodeableBand(Wavelet::AbstractDecodeableBand* band_,
+ const Wavelet& wavelet_)
+ : band(band_), wavelet(wavelet_) {}
+ };
+ std::vector<DecodeableBand> allDecodeableBands;
+
+ struct ReconstructionStep {
+ Wavelet& wavelet;
+ Wavelet::ReconstructableBand& band;
+ ReconstructionStep(Wavelet* wavelet_, Wavelet::ReconstructableBand* band_)
+ : wavelet(*wavelet_), band(*band_) {}
+ };
+ std::vector<ReconstructionStep> reconstructionSteps;
+
+ static inline void getRLV(BitPumpMSB* bits, int* value, unsigned int* count);
+
+ void parseLargeCodeblock(const ByteStream& bs);
+
+ void prepareBandDecodingPlan();
+ void prepareBandReconstruction();
+ void prepareDecodingPlan();
+
+ void decodeBands(bool* exceptionThrown) const noexcept;
+
+ void reconstructLowpassBands() const noexcept;
+
+ void combineFinalLowpassBands() const noexcept;
+
+ void decodeThread(bool* exceptionThrown) const noexcept;
+
+ void parseVC5();
+
+public:
+ VC5Decompressor(ByteStream bs, const RawImage& img);
+
+ void decode(unsigned int offsetX, unsigned int offsetY, unsigned int width,
+ unsigned int height);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/decompressors/meson.build
b/subprojects/rawspeed/src/librawspeed/decompressors/meson.build
new file mode 100644
index 00000000..bfc8b265
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/meson.build
@@ -0,0 +1,66 @@
+dependencies = [openmp_dep, zlib_dep]
+
+sources = files(
+ 'AbstractDecompressor.h',
+ 'AbstractDngDecompressor.cpp',
+ 'AbstractDngDecompressor.h',
+ 'AbstractHuffmanTable.h',
+ 'AbstractLJpegDecompressor.cpp',
+ 'AbstractLJpegDecompressor.h',
+ 'AbstractSamsungDecompressor.h',
+ 'BinaryHuffmanTree.h',
+ 'Cr2Decompressor.cpp',
+ 'Cr2Decompressor.h',
+ 'CrwDecompressor.cpp',
+ 'CrwDecompressor.h',
+ 'DeflateDecompressor.cpp',
+ 'DeflateDecompressor.h',
+ 'FujiDecompressor.cpp',
+ 'FujiDecompressor.h',
+ 'HasselbladDecompressor.cpp',
+ 'HasselbladDecompressor.h',
+ 'HuffmanTable.h',
+ 'HuffmanTableLUT.h',
+ 'HuffmanTableLookup.h',
+ 'HuffmanTableTree.h',
+ 'HuffmanTableVector.h',
+ 'JpegDecompressor.cpp',
+ 'JpegDecompressor.h',
+ 'KodakDecompressor.cpp',
+ 'KodakDecompressor.h',
+ 'LJpegDecompressor.cpp',
+ 'LJpegDecompressor.h',
+ 'NikonDecompressor.cpp',
+ 'NikonDecompressor.h',
+ 'OlympusDecompressor.cpp',
+ 'OlympusDecompressor.h',
+ 'PanasonicDecompressor.cpp',
+ 'PanasonicDecompressor.h',
+ 'PanasonicDecompressorV5.cpp',
+ 'PanasonicDecompressorV5.h',
+ 'PentaxDecompressor.cpp',
+ 'PentaxDecompressor.h',
+ 'PhaseOneDecompressor.cpp',
+ 'PhaseOneDecompressor.h',
+ 'SamsungV0Decompressor.cpp',
+ 'SamsungV0Decompressor.h',
+ 'SamsungV1Decompressor.cpp',
+ 'SamsungV1Decompressor.h',
+ 'SamsungV2Decompressor.cpp',
+ 'SamsungV2Decompressor.h',
+ 'SonyArw1Decompressor.cpp',
+ 'SonyArw1Decompressor.h',
+ 'SonyArw2Decompressor.cpp',
+ 'SonyArw2Decompressor.h',
+ 'UncompressedDecompressor.cpp',
+ 'UncompressedDecompressor.h',
+ 'VC5Decompressor.cpp',
+ 'VC5Decompressor.h',
+)
+
+librawspeed_decompressors = static_library(
+ 'rawspeed-decompressors',
+ sources,
+ dependencies: dependencies,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.cpp
b/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.cpp
new file mode 100644
index 00000000..59a2ba27
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.cpp
@@ -0,0 +1,522 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015-2017 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
+*/
+
+#include "interpolators/Cr2sRawInterpolator.h"
+#include "common/Common.h" // for ushort16, clampBits
+#include "common/Point.h" // for iPoint2D
+#include "common/RawImage.h" // for RawImage, RawImageData
+#include "decoders/RawDecoderException.h" // for RawDecoderException (ptr o...
+#include <array> // for array
+#include <cassert> // for assert
+#include <type_traits> // for is_pod
+
+using std::is_pod;
+using std::array;
+
+namespace rawspeed {
+
+struct Cr2sRawInterpolator::YCbCr final {
+ int Y;
+ int Cb;
+ int Cr;
+
+ inline static void LoadY(YCbCr* dst, const YCbCr& src) {
+ assert(dst);
+
+ dst->Y = src.Y;
+ }
+
+ inline static void LoadY(YCbCr* p, const ushort16* data) {
+ assert(p);
+ assert(data);
+
+ p->Y = data[0];
+ }
+
+ inline static void LoadCbCr(YCbCr* p, const ushort16* data) {
+ assert(p);
+ assert(data);
+
+ p->Cb = data[1];
+ p->Cr = data[2];
+ }
+
+ inline static void Load(YCbCr* p, const ushort16* data) {
+ assert(p);
+ assert(data);
+
+ LoadY(p, data);
+ LoadCbCr(p, data);
+ }
+
+ YCbCr() = default;
+
+ explicit YCbCr(ushort16* data) {
+ static_assert(is_pod<YCbCr>::value, "not a POD");
+
+ assert(data);
+
+ Load(this, data);
+ }
+
+ inline void signExtend() {
+ Cb -= 16384;
+ Cr -= 16384;
+ }
+
+ inline void applyHue(int hue_) {
+ Cb += hue_;
+ Cr += hue_;
+ }
+
+ inline void process(int hue_) {
+ signExtend();
+ applyHue(hue_);
+ }
+
+ inline void interpolate(const YCbCr& p0, const YCbCr& p2) {
+ // Y is already good, need to interpolate Cb and Cr
+ // FIXME: dcraw does +1 before >> 1
+ Cb = (p0.Cb + p2.Cb) >> 1;
+ Cr = (p0.Cr + p2.Cr) >> 1;
+ }
+
+ inline void interpolate(const YCbCr& p0, const YCbCr& p1, const YCbCr& p2,
+ const YCbCr& p3) {
+ // Y is already good, need to interpolate Cb and Cr
+ // FIXME: dcraw does +1 before >> 1
+ Cb = (p0.Cb + p1.Cb + p2.Cb + p3.Cb) >> 2;
+ Cr = (p0.Cr + p1.Cr + p2.Cr + p3.Cr) >> 2;
+ }
+};
+
+// NOTE: Thread safe.
+template <int version>
+inline void Cr2sRawInterpolator::interpolate_422_row(ushort16* data, int w) {
+ assert(data);
+ assert(w >= 2);
+ assert(w % 2 == 0);
+
+ // the format is:
+ // p0 p1 p2 p3
+ // [ Y1 Cb Cr ] [ Y2 ... ... ] [ Y1 Cb Cr ] [ Y2 ... ... ] ...
+ // i.e. even pixels are full, odd pixels need interpolation:
+ // p0 p1 p2 p3
+ // [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // for last (odd) pixel of the line, just keep Cb/Cr from previous pixel
+ // see http://lclevy.free.fr/cr2/#sraw
+
+ int x;
+ for (x = 0; x < w - 2; x += 2) {
+ assert(x + 4 <= w);
+ assert(x % 2 == 0);
+
+ // load, process and output first pixel, which is full
+ YCbCr p0(data);
+ p0.process(hue);
+ YUV_TO_RGB<version>(p0, data);
+ data += 3;
+
+ // load Y from second pixel, Cb/Cr need to be interpolated
+ YCbCr p;
+ YCbCr::LoadY(&p, data);
+
+ // load third pixel, which is full, process
+ YCbCr p1(data + 3);
+ p1.process(hue);
+
+ // and finally, interpolate and output the middle pixel
+ p.interpolate(p0, p1);
+ YUV_TO_RGB<version>(p, data);
+ data += 3;
+ }
+
+ assert(x + 2 == w);
+ assert(x % 2 == 0);
+
+ // Last two pixels, the format is:
+ // p0 p1
+ // .. [ Y1 Cb Cr ] [ Y2 ... ... ]
+
+ // load, process and output first pixel, which is full
+ YCbCr p(data);
+ p.process(hue);
+ YUV_TO_RGB<version>(p, data);
+ data += 3;
+
+ // load Y from second pixel, keep Cb/Cr from previous pixel, and output
+ YCbCr::LoadY(&p, data);
+ YUV_TO_RGB<version>(p, data);
+ data += 3;
+}
+
+template <int version>
+inline void Cr2sRawInterpolator::interpolate_422(int w, int h) {
+ assert(w > 0);
+ assert(h > 0);
+
+ for (int y = 0; y < h; y++) {
+ auto data = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+
+ interpolate_422_row<version>(data, w);
+ }
+}
+
+// NOTE: Not thread safe, since it writes inplace.
+template <int version>
+inline void
+Cr2sRawInterpolator::interpolate_420_row(std::array<ushort16*, 3> line, int w) {
+ assert(line[0]);
+ assert(line[1]);
+ assert(line[2]);
+
+ // the format is:
+ // p0 p1 p2 p3
+ // row 0: [ Y1 Cb Cr ] [ Y2 ... ... ] [ Y1 Cb Cr ] [ Y2 ... ... ] ...
+ // row 1: [ Y3 ... ... ] [ Y4 ... ... ] [ Y3 ... ... ] [ Y4 ... ... ] ...
+ // row 2: [ Y1 Cb Cr ] [ Y2 ... ... ] [ Y1 Cb Cr ] [ Y2 ... ... ] ...
+ // row 3: [ Y3 ... ... ] [ Y4 ... ... ] [ Y3 ... ... ] [ Y4 ... ... ] ...
+ // .. . . .. . . .. . . .. . .
+ // i.e. on even rows, even pixels are full, rest of pixels need interpolation
+ // first, on even rows, odd pixels are interpolated using 422 algo (marked *)
+ // p0 p1 p2 p3
+ // row 0: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 1: [ Y3 ... ... ] [ Y4 ... ... ] [ Y3 ... ... ] [ Y4 ... ... ] ...
+ // row 2: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 3: [ Y3 ... ... ] [ Y4 ... ... ] [ Y3 ... ... ] [ Y4 ... ... ] ...
+ // .. . . .. . . .. . .
+ // then, on odd rows, even pixels are interpolated (marked with #)
+ // p0 p1 p2 p3
+ // row 0: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 1: [ Y3 Cb# Cr# ] [ Y4 ... ... ] [ Y3 Cb# Cr# ] [ Y4 ... ... ] ...
+ // row 2: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 3: [ Y3 Cb# Cr# ] [ Y4 ... ... ] [ Y3 Cb# Cr# ] [ Y4 ... ... ] ...
+ // .. . . .. . . .. . .
+ // and finally, on odd rows, odd pixels are interpolated from * (marked $)
+ // p0 p1 p2 p3
+ // row 0: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 1: [ Y3 Cb# Cr# ] [ Y4 Cb$ Cr$ ] [ Y3 Cb# Cr# ] [ Y4 Cb$ Cr$ ] ...
+ // row 2: [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] [ Y1 Cb Cr ] [ Y2 Cb* Cr* ] ...
+ // row 3: [ Y3 Cb# Cr# ] [ Y4 Cb$ Cr$ ] [ Y3 Cb# Cr# ] [ Y4 Cb$ Cr$ ] ...
+ // .. . . .. . . .. . .
+ // see http://lclevy.free.fr/cr2/#sraw
+
+ int x;
+ for (x = 0; x < w - 2; x += 2) {
+ assert(x + 4 <= w);
+ assert(x % 2 == 0);
+
+ // load, process and output first pixel of first row, which is full
+ YCbCr p0(line[0]);
+ p0.process(hue);
+ YUV_TO_RGB<version>(p0, line[0]);
+ line[0] += 3;
+
+ // load Y from second pixel of first row
+ YCbCr ph;
+ YCbCr::LoadY(&ph, line[0]);
+
+ // load Cb/Cr from third pixel of first row
+ YCbCr p1;
+ YCbCr::LoadCbCr(&p1, line[0] + 3);
+ p1.process(hue);
+
+ // and finally, interpolate and output the middle pixel of first row
+ ph.interpolate(p0, p1);
+ YUV_TO_RGB<version>(ph, line[0]);
+ line[0] += 3;
+
+ // load Y from first pixel of second row
+ YCbCr pv;
+ YCbCr::LoadY(&pv, line[1]);
+
+ // load Cb/Cr from first pixel of third row
+ YCbCr p2;
+ YCbCr::LoadCbCr(&p2, line[2]);
+ p2.process(hue);
+
+ // and finally, interpolate and output the first pixel of second row
+ pv.interpolate(p0, p2);
+ YUV_TO_RGB<version>(pv, line[1]);
+ line[1] += 3;
+ line[2] += 6;
+
+ // load Y from second pixel of second row
+ YCbCr p;
+ YCbCr::LoadY(&p, line[1]);
+
+ // load Cb/Cr from third pixel of third row
+ YCbCr p3;
+ YCbCr::LoadCbCr(&p3, line[2]);
+ p3.process(hue);
+
+ // and finally, interpolate and output the second pixel of second row
+ // NOTE: we interpolate 4 full pixels here, located on diagonals
+ // dcraw interpolates from already interpolated pixels
+ p.interpolate(p0, p1, p2, p3);
+ YUV_TO_RGB<version>(p, line[1]);
+ line[1] += 3;
+ }
+
+ assert(x + 2 == w);
+ assert(x % 2 == 0);
+
+ // Last two pixels of the lines, the format is:
+ // p0 p1
+ // row 0: ... [ Y1 Cb Cr ] [ Y2 ... ... ]
+ // row 1: ... [ Y3 ... ... ] [ Y4 ... ... ]
+ // row 2: ... [ Y1 Cb Cr ] [ Y2 ... ... ]
+ // row 3: ... [ Y3 ... ... ] [ Y4 ... ... ]
+ // .. . . .. . .
+
+ // load, process and output first pixel of first row, which is full
+ YCbCr p0(line[0]);
+ p0.process(hue);
+ YUV_TO_RGB<version>(p0, line[0]);
+ line[0] += 3;
+
+ // keep Cb/Cr from first pixel of first row
+ // load Y from second pixel of first row, output
+ YCbCr::LoadY(&p0, line[0]);
+ YUV_TO_RGB<version>(p0, line[0]);
+ line[0] += 3;
+
+ // load Y from first pixel of second row
+ YCbCr pv;
+ YCbCr::LoadY(&pv, line[1]);
+
+ // load Cb/Cr from first pixel of third row
+ YCbCr p2;
+ YCbCr::LoadCbCr(&p2, line[2]);
+ p2.process(hue);
+
+ // and finally, interpolate and output the first pixel of second row
+ pv.interpolate(p0, p2);
+ YUV_TO_RGB<version>(pv, line[1]);
+ line[1] += 3;
+
+ // keep Cb/Cr from first pixel of second row
+ // load Y from second pixel of second row, output
+ YCbCr::LoadY(&pv, line[1]);
+ YUV_TO_RGB<version>(pv, line[1]);
+ line[1] += 3;
+}
+
+// NOTE: Not thread safe, since it writes inplace.
+template <int version>
+inline void Cr2sRawInterpolator::interpolate_420(int w, int h) {
+ assert(w >= 2);
+ assert(w % 2 == 0);
+
+ assert(h >= 2);
+ assert(h % 2 == 0);
+
+ array<ushort16*, 3> line;
+
+ int y;
+ for (y = 0; y < h - 2; y += 2) {
+ assert(y + 4 <= h);
+ assert(y % 2 == 0);
+
+ line[0] = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ line[1] = reinterpret_cast<ushort16*>(mRaw->getData(0, y + 1));
+ line[2] = reinterpret_cast<ushort16*>(mRaw->getData(0, y + 2));
+
+ interpolate_420_row<version>(line, w);
+ }
+
+ assert(y + 2 == h);
+ assert(y % 2 == 0);
+
+ line[0] = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
+ line[1] = reinterpret_cast<ushort16*>(mRaw->getData(0, y + 1));
+ line[2] = nullptr;
+
+ assert(line[0]);
+ assert(line[1]);
+ assert(line[2] == nullptr);
+
+ // Last two lines, the format is:
+ // p0 p1 p2 p3
+ // .. . . .. . . .. . . .. . .
+ // row 0: [ Y1 Cb Cr ] [ Y2 ... ... ] [ Y1 Cb Cr ] [ Y2 ... ... ] ...
+ // row 1: [ Y3 ... ... ] [ Y4 ... ... ] [ Y3 ... ... ] [ Y4 ... ... ] ...
+
+ int x;
+ for (x = 0; x < w - 2; x += 2) {
+ assert(x + 4 <= w);
+ assert(x % 2 == 0);
+
+ // load, process and output first pixel of first row, which is full
+ YCbCr p0(line[0]);
+ p0.process(hue);
+ YUV_TO_RGB<version>(p0, line[0]);
+ line[0] += 3;
+
+ // load Y from second pixel of first row
+ YCbCr ph;
+ YCbCr::LoadY(&ph, line[0]);
+
+ // load Cb/Cr from third pixel of first row
+ YCbCr p1;
+ YCbCr::LoadCbCr(&p1, line[0] + 3);
+ p1.process(hue);
+
+ // and finally, interpolate and output the middle pixel of first row
+ ph.interpolate(p0, p1);
+ YUV_TO_RGB<version>(ph, line[0]);
+ line[0] += 3;
+
+ // keep Cb/Cr from first pixel of first row
+ // load Y from first pixel of second row; and output
+ YCbCr::LoadY(&p0, line[1]);
+ YUV_TO_RGB<version>(p0, line[1]);
+ line[1] += 3;
+
+ // keep Cb/Cr from second pixel of first row
+ // load Y from second pixel of second row; and output
+ YCbCr::LoadY(&ph, line[1]);
+ YUV_TO_RGB<version>(ph, line[1]);
+ line[1] += 3;
+ }
+
+ assert(line[0]);
+ assert(line[1]);
+ assert(line[2] == nullptr);
+
+ assert(y + 2 == h);
+ assert(y % 2 == 0);
+
+ assert(x + 2 == w);
+ assert(x % 2 == 0);
+
+ // Last two pixels of last two lines, the format is:
+ // p0 p1
+ // .. . . .. . .
+ // row 0: ... [ Y1 Cb Cr ] [ Y2 ... ... ]
+ // row 1: ... [ Y3 ... ... ] [ Y4 ... ... ]
+
+ // load, process and output first pixel of first row, which is full
+ YCbCr p(line[0]);
+ p.process(hue);
+ YUV_TO_RGB<version>(p, line[0]);
+ line[0] += 3;
+
+ // rest keeps Cb/Cr from this original pixel, because rest only have Y
+
+ // load Y from second pixel of first row, and output
+ YCbCr::LoadY(&p, line[0]);
+ YUV_TO_RGB<version>(p, line[0]);
+ line[0] += 3;
+
+ // load Y from first pixel of second row, and output
+ YCbCr::LoadY(&p, line[1]);
+ YUV_TO_RGB<version>(p, line[1]);
+ line[1] += 3;
+
+ // load Y from second pixel of second row, and output
+ YCbCr::LoadY(&p, line[1]);
+ YUV_TO_RGB<version>(p, line[1]);
+ line[1] += 3;
+}
+
+inline void Cr2sRawInterpolator::STORE_RGB(ushort16* X, int r, int g, int b) {
+ assert(X);
+
+ X[0] = clampBits(r >> 8, 16);
+ X[1] = clampBits(g >> 8, 16);
+ X[2] = clampBits(b >> 8, 16);
+}
+
+template </* int version */>
+/* Algorithm found in EOS 40D */
+inline void Cr2sRawInterpolator::YUV_TO_RGB<0>(const YCbCr& p, ushort16* X) {
+ assert(X);
+
+ int r = sraw_coeffs[0] * (p.Y + p.Cr - 512);
+ int g = sraw_coeffs[1] * (p.Y + ((-778 * p.Cb - (p.Cr * 2048)) >> 12) - 512);
+ int b = sraw_coeffs[2] * (p.Y + (p.Cb - 512));
+ STORE_RGB(X, r, g, b);
+}
+
+template </* int version */>
+inline void Cr2sRawInterpolator::YUV_TO_RGB<1>(const YCbCr& p, ushort16* X) {
+ assert(X);
+
+ int r = sraw_coeffs[0] * (p.Y + ((50 * p.Cb + 22929 * p.Cr) >> 12));
+ int g = sraw_coeffs[1] * (p.Y + ((-5640 * p.Cb - 11751 * p.Cr) >> 12));
+ int b = sraw_coeffs[2] * (p.Y + ((29040 * p.Cb - 101 * p.Cr) >> 12));
+ STORE_RGB(X, r, g, b);
+}
+
+template </* int version */>
+/* Algorithm found in EOS 5d Mk III */
+inline void Cr2sRawInterpolator::YUV_TO_RGB<2>(const YCbCr& p, ushort16* X) {
+ assert(X);
+
+ int r = sraw_coeffs[0] * (p.Y + p.Cr);
+ int g = sraw_coeffs[1] * (p.Y + ((-778 * p.Cb - (p.Cr * 2048)) >> 12));
+ int b = sraw_coeffs[2] * (p.Y + p.Cb);
+ STORE_RGB(X, r, g, b);
+}
+
+// Interpolate and convert sRaw data.
+void Cr2sRawInterpolator::interpolate(int version) {
+ assert(version >= 0 && version <= 2);
+
+ const auto& subSampling = mRaw->metadata.subsampling;
+ if (subSampling.y == 1 && subSampling.x == 2) {
+ int width = mRaw->dim.x;
+ int height = mRaw->dim.y;
+
+ switch (version) {
+ case 0:
+ interpolate_422<0>(width, height);
+ break;
+ case 1:
+ interpolate_422<1>(width, height);
+ break;
+ case 2:
+ interpolate_422<2>(width, height);
+ break;
+ default:
+ __builtin_unreachable();
+ }
+ } else if (subSampling.y == 2 && subSampling.x == 2) {
+ int width = mRaw->dim.x;
+ int height = mRaw->dim.y;
+
+ switch (version) {
+ // no known sraws with "version 0"
+ case 1:
+ interpolate_420<1>(width, height);
+ break;
+ case 2:
+ interpolate_420<2>(width, height);
+ break;
+ default:
+ __builtin_unreachable();
+ }
+ } else
+ ThrowRDE("Unknown subsampling: (%i; %i)", subSampling.x, subSampling.y);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.h
b/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.h
new file mode 100644
index 00000000..279e871a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/interpolators/Cr2sRawInterpolator.h
@@ -0,0 +1,57 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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/Common.h" // for ushort16
+#include <array> // for array
+
+namespace rawspeed {
+
+class RawImage;
+
+class Cr2sRawInterpolator final {
+ const RawImage& mRaw;
+ std::array<int, 3> sraw_coeffs;
+ int hue;
+
+ struct YCbCr;
+
+public:
+ Cr2sRawInterpolator(const RawImage& mRaw_, std::array<int, 3> sraw_coeffs_,
+ int hue_)
+ : mRaw(mRaw_), sraw_coeffs(sraw_coeffs_), hue(hue_) {}
+
+ void interpolate(int version);
+
+protected:
+ template <int version> inline void YUV_TO_RGB(const YCbCr& p, ushort16* X);
+
+ inline void STORE_RGB(ushort16* X, int r, int g, int b);
+
+ template <int version> inline void interpolate_422_row(ushort16* data, int w);
+ template <int version> inline void interpolate_422(int w, int h);
+
+ template <int version>
+ inline void interpolate_420_row(std::array<ushort16*, 3> line, int w);
+ template <int version> inline void interpolate_420(int w, int h);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/interpolators/meson.build
b/subprojects/rawspeed/src/librawspeed/interpolators/meson.build
new file mode 100644
index 00000000..cba28373
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/interpolators/meson.build
@@ -0,0 +1,10 @@
+sources = files(
+ 'Cr2sRawInterpolator.cpp',
+ 'Cr2sRawInterpolator.h',
+)
+
+librawspeed_interpolators = static_library(
+ 'rawspeed-interpolators',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitPumpJPEG.h
b/subprojects/rawspeed/src/librawspeed/io/BitPumpJPEG.h
new file mode 100644
index 00000000..c1f78604
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitPumpJPEG.h
@@ -0,0 +1,92 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 uchar8, uint32
+#include "io/BitStream.h" // for BitStreamCacheRightInLeftOut, BitStream
+#include "io/Buffer.h" // for Buffer::size_type
+#include "io/Endianness.h" // for getBE
+
+namespace rawspeed {
+
+struct JPEGBitPumpTag;
+
+// The JPEG data is ordered in MSB bit order,
+// i.e. we push into the cache from the right and read it from the left
+using BitPumpJPEG = BitStream<JPEGBitPumpTag, BitStreamCacheRightInLeftOut>;
+
+template <> struct BitStreamTraits<BitPumpJPEG> final {
+ static constexpr bool canUseWithHuffmanTable = true;
+};
+
+template <>
+inline BitPumpJPEG::size_type BitPumpJPEG::fillCache(const uchar8* input,
+ size_type bufferSize,
+ size_type* bufPos) {
+ static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
+
+ // short-cut path for the most common case (no FF marker in the next 4 bytes)
+ // this is slightly faster than the else-case alone.
+ // TODO: investigate applicability of vector intrinsics to speed up if-cascade
+ if (input[0] != 0xFF &&
+ input[1] != 0xFF &&
+ input[2] != 0xFF &&
+ input[3] != 0xFF ) {
+ cache.push(getBE<uint32>(input), 32);
+ return 4;
+ }
+
+ size_type p = 0;
+ for (size_type i = 0; i < 4; ++i) {
+ // Pre-execute most common case, where next byte is 'normal'/non-FF
+ const int c0 = input[p++];
+ cache.push(c0, 8);
+ if (c0 == 0xFF) {
+ // Found FF -> pre-execute case of FF/00, which represents an FF data byte -> ignore the 00
+ const int c1 = input[p++];
+ if (c1 != 0) {
+ // Found FF/xx with xx != 00. This is the end of stream marker.
+
+ // Clear low 8 bits (0xFF, from c0) that we optimistically pushed.
+ // We should not pop() them, to avoid issues with fillLevel becoming 0.
+ cache.cache &= ~0xFFULL;
+ // And fully fill the empty space in cache with zeros.
+ cache.cache <<= 64 - cache.fillLevel;
+ cache.fillLevel = 64;
+
+ // No further reading from this buffer shall happen.
+ // Do signal that by stating that we are at the end of the buffer.
+ *bufPos = bufferSize;
+ return 0;
+ }
+ }
+ }
+ return p;
+}
+
+template <> inline BitPumpJPEG::size_type BitPumpJPEG::getBufferPosition() const
+{
+ // the current number of bytes we consumed -> at the end of the stream pos, it
+ // points to the JPEG marker FF
+ return pos;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitPumpLSB.h
b/subprojects/rawspeed/src/librawspeed/io/BitPumpLSB.h
new file mode 100644
index 00000000..3601f4da
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitPumpLSB.h
@@ -0,0 +1,53 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, uchar8
+#include "io/BitStream.h" // for BitStream, BitStreamCacheLeftInRightOut
+#include "io/Buffer.h" // for Buffer::size_type
+#include "io/Endianness.h" // for getLE
+
+namespace rawspeed {
+
+struct LSBBitPumpTag;
+
+// The LSBPump is ordered in LSB bit order,
+// i.e. we push into the cache from the left and read it from the right
+
+using BitPumpLSB = BitStream<LSBBitPumpTag, BitStreamCacheLeftInRightOut>;
+
+template <>
+inline BitPumpLSB::size_type BitPumpLSB::fillCache(const uchar8* input,
+ size_type bufferSize,
+ size_type* bufPos) {
+ static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
+
+ cache.push(getLE<uint32>(input), 32);
+ return 4;
+}
+
+template <> inline void BitPumpLSB::setBufferPosition(size_type newPos) {
+ pos = newPos;
+ cache.fillLevel = 0;
+ cache.cache = 0;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB.h
b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB.h
new file mode 100644
index 00000000..b6043c6a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, uchar8
+#include "io/BitStream.h" // for BitStream, BitStreamCacheRightInLeftOut
+#include "io/Endianness.h" // for getBE
+
+namespace rawspeed {
+
+struct MSBBitPumpTag;
+
+// The MSB data is ordered in MSB bit order,
+// i.e. we push into the cache from the right and read it from the left
+
+using BitPumpMSB = BitStream<MSBBitPumpTag, BitStreamCacheRightInLeftOut>;
+
+template <> struct BitStreamTraits<BitPumpMSB> final {
+ static constexpr bool canUseWithHuffmanTable = true;
+};
+
+template <>
+inline BitPumpMSB::size_type BitPumpMSB::fillCache(const uchar8* input,
+ size_type bufferSize,
+ size_type* bufPos) {
+ static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
+
+ cache.push(getBE<uint32>(input), 32);
+ return 4;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB16.h
b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB16.h
new file mode 100644
index 00000000..c6be348d
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB16.h
@@ -0,0 +1,48 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 ushort16, uchar8
+#include "io/BitStream.h" // for BitStream, BitStreamCacheRightInLeftOut
+#include "io/Buffer.h" // for Buffer::size_type
+#include "io/Endianness.h" // for getLE
+
+namespace rawspeed {
+
+struct MSB16BitPumpTag;
+
+// The MSB data is ordered in MSB bit order,
+// i.e. we push into the cache from the right and read it from the left
+
+using BitPumpMSB16 = BitStream<MSB16BitPumpTag, BitStreamCacheRightInLeftOut>;
+
+template <>
+inline BitPumpMSB16::size_type BitPumpMSB16::fillCache(const uchar8* input,
+ size_type bufferSize,
+ size_type* bufPos) {
+ static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
+
+ for (size_type i = 0; i < 4; i += sizeof(ushort16))
+ cache.push(getLE<ushort16>(input + i), 16);
+ return 4;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB32.h
b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB32.h
new file mode 100644
index 00000000..4109f2d0
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitPumpMSB32.h
@@ -0,0 +1,50 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, uchar8
+#include "io/BitStream.h" // for BitStream, BitStreamCacheRightInLeftOut
+#include "io/Endianness.h" // for getLE
+
+namespace rawspeed {
+
+struct MSB32BitPumpTag;
+
+// The MSB data is ordered in MSB bit order,
+// i.e. we push into the cache from the right and read it from the left
+
+using BitPumpMSB32 = BitStream<MSB32BitPumpTag, BitStreamCacheRightInLeftOut>;
+
+template <> struct BitStreamTraits<BitPumpMSB32> final {
+ static constexpr bool canUseWithHuffmanTable = true;
+};
+
+template <>
+inline BitPumpMSB32::size_type BitPumpMSB32::fillCache(const uchar8* input,
+ size_type bufferSize,
+ size_type* bufPos) {
+ static_assert(BitStreamCacheBase::MaxGetBits >= 32, "check implementation");
+
+ cache.push(getLE<uint32>(input), 32);
+ return 4;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/BitStream.h
b/subprojects/rawspeed/src/librawspeed/io/BitStream.h
new file mode 100644
index 00000000..6bcea3cb
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/BitStream.h
@@ -0,0 +1,234 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+ Copyright (C) 2017-2019 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/Common.h" // for uint32, uchar8, uint64
+#include "io/Buffer.h" // for Buffer::size_type, BUFFER_PADDING
+#include "io/ByteStream.h" // for ByteStream
+#include "io/IOException.h" // for IOException (ptr only), ThrowIOE
+#include <cassert> // for assert
+#include <cstring> // for memcpy
+
+namespace rawspeed {
+
+// simple 64-bit wide cache implementation that acts like a FiFo.
+// There are two variants:
+// * L->R: new bits are pushed in on the left and pulled out on the right
+// * L<-R: new bits are pushed in on the right and pulled out on the left
+// Each BitStream specialization uses one of the two.
+
+struct BitStreamCacheBase
+{
+ uint64 cache = 0; // the actual bits stored in the cache
+ unsigned int fillLevel = 0; // bits left in cache
+ static constexpr unsigned Size = sizeof(cache)*8;
+
+ // how many bits could be requested to be filled
+ static constexpr unsigned MaxGetBits = Size/2;
+
+ // maximal number of bytes the implementation may read.
+ // NOTE: this is not the same as MaxGetBits/8 !!!
+ static constexpr unsigned MaxProcessBytes = 8;
+};
+
+struct BitStreamCacheLeftInRightOut : BitStreamCacheBase
+{
+ inline void push(uint64 bits, uint32 count) noexcept {
+ assert(count + fillLevel <= Size);
+ cache |= bits << fillLevel;
+ fillLevel += count;
+ }
+
+ inline uint32 peek(uint32 count) const noexcept {
+ return cache & ((1U << count) - 1U);
+ }
+
+ inline void skip(uint32 count) noexcept {
+ cache >>= count;
+ fillLevel -= count;
+ }
+};
+
+struct BitStreamCacheRightInLeftOut : BitStreamCacheBase
+{
+ inline void push(uint64 bits, uint32 count) noexcept {
+ assert(count + fillLevel <= Size);
+ assert(count < BitStreamCacheBase::Size);
+ cache = cache << count | bits;
+ fillLevel += count;
+ }
+
+ inline uint32 peek(uint32 count) const noexcept {
+ return (cache >> (fillLevel - count)) & ((1U << count) - 1U);
+ }
+
+ inline void skip(uint32 count) noexcept {
+ fillLevel -= count;
+ }
+};
+
+template <typename BIT_STREAM> struct BitStreamTraits final {
+ static constexpr bool canUseWithHuffmanTable = false;
+};
+
+template <typename Tag, typename Cache>
+class BitStream final : public ByteStream {
+ Cache cache;
+
+ // this method hase to be implemented in the concrete BitStream template
+ // specializations. It will return the number of bytes processed. It needs
+ // to process up to BitStreamCacheBase::MaxProcessBytes bytes of input.
+ size_type fillCache(const uchar8* input, size_type bufferSize,
+ size_type* bufPos);
+
+public:
+ BitStream() = default;
+
+ explicit BitStream(const ByteStream& s)
+ : ByteStream(s.getSubStream(s.getPosition(), s.getRemainSize())) {}
+
+ // deprecated:
+ BitStream(const Buffer* f, size_type offset)
+ : ByteStream(DataBuffer(f->getSubView(offset))) {}
+
+private:
+ inline void fillSafe() {
+ assert(data);
+ if (pos + BitStreamCacheBase::MaxProcessBytes <= size) {
+ std::array<uchar8, BitStreamCacheBase::MaxProcessBytes> tmp;
+ tmp.fill(0);
+ assert(!(size - pos < BitStreamCacheBase::MaxProcessBytes));
+ memcpy(tmp.data(), data + pos, BitStreamCacheBase::MaxProcessBytes);
+ pos += fillCache(tmp.data(), size, &pos);
+ } else if (pos < size) {
+ std::array<uchar8, BitStreamCacheBase::MaxProcessBytes> tmp;
+ tmp.fill(0);
+ assert(size - pos < BitStreamCacheBase::MaxProcessBytes);
+ memcpy(tmp.data(), data + pos, size - pos);
+ pos += fillCache(tmp.data(), size, &pos);
+ } else if (pos <= size + BitStreamCacheBase::MaxProcessBytes) {
+ std::array<uchar8, BitStreamCacheBase::MaxProcessBytes> tmp;
+ tmp.fill(0);
+ pos += fillCache(tmp.data(), size, &pos);
+ } else {
+ // assert(size < pos);
+ ThrowIOE("Buffer overflow read in BitStream");
+ }
+ }
+
+ // In non-DEBUG builds, fillSafe() will be called at most once
+ // per the life-time of the BitStream therefore it should *NOT* be inlined
+ // into the normal codepath.
+ inline void __attribute__((noinline, cold)) fillSafeNoinline() { fillSafe(); }
+
+public:
+ inline void fill(uint32 nbits = Cache::MaxGetBits) {
+ assert(data);
+ assert(nbits <= Cache::MaxGetBits);
+ if (cache.fillLevel < nbits) {
+#if defined(DEBUG)
+ // really slow, but best way to check all the assumptions.
+ fillSafe();
+#elif BUFFER_PADDING >= 8
+ static_assert(BitStreamCacheBase::MaxProcessBytes == 8,
+ "update these too");
+ // FIXME: this looks very wrong. We don't check pos at all here.
+ // I suspect this should be: if (pos <= size)
+ pos += fillCache(data + pos, size, &pos);
+#else
+ // disabling this run-time bounds check saves about 1% on intel x86-64
+ if (pos + BitStreamCacheBase::MaxProcessBytes <= size)
+ pos += fillCache(data + pos, size, &pos);
+ else
+ fillSafeNoinline();
+#endif
+ }
+ }
+
+ // these methods might be specialized by implementations that support it
+ inline size_type getBufferPosition() const {
+ return pos - (cache.fillLevel >> 3);
+ }
+
+ inline size_type getFillLevel() const { return cache.fillLevel; }
+
+ // rewinds to the beginning of the buffer.
+ void resetBufferPosition() {
+ pos = 0;
+ cache.fillLevel = 0;
+ cache.cache = 0;
+ }
+
+ void setBufferPosition(size_type newPos);
+
+ inline uint32 __attribute__((pure)) peekBitsNoFill(uint32 nbits) {
+ assert(nbits <= Cache::MaxGetBits);
+ assert(nbits <= cache.fillLevel);
+ return cache.peek(nbits);
+ }
+
+ inline uint32 getBitsNoFill(uint32 nbits) {
+ uint32 ret = peekBitsNoFill(nbits);
+ cache.skip(nbits);
+ return ret;
+ }
+
+ inline void skipBitsNoFill(uint32 nbits) {
+ assert(nbits <= Cache::MaxGetBits);
+ assert(nbits <= cache.fillLevel);
+ cache.skip(nbits);
+ }
+
+ inline uint32 peekBits(uint32 nbits) {
+ fill(nbits);
+ return peekBitsNoFill(nbits);
+ }
+
+ inline uint32 getBits(uint32 nbits) {
+ fill(nbits);
+ return getBitsNoFill(nbits);
+ }
+
+ inline void skipBits(uint32 nbits) {
+ if (nbits > cache.fillLevel)
+ ThrowIOE("skipBits overflow");
+ cache.skip(nbits);
+ }
+
+ // This may be used to skip arbitrarily large number of *bytes*,
+ // not limited by the fill level.
+ inline void skipBytes(uint32 nbytes) {
+ uint32 remainingBitsToSkip = 8 * nbytes;
+ for (; remainingBitsToSkip >= Cache::MaxGetBits;
+ remainingBitsToSkip -= Cache::MaxGetBits) {
+ fill(Cache::MaxGetBits);
+ skipBitsNoFill(Cache::MaxGetBits);
+ }
+ if (remainingBitsToSkip > 0) {
+ fill(remainingBitsToSkip);
+ skipBitsNoFill(remainingBitsToSkip);
+ }
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/Buffer.cpp
b/subprojects/rawspeed/src/librawspeed/io/Buffer.cpp
new file mode 100644
index 00000000..7c9307bf
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/Buffer.cpp
@@ -0,0 +1,129 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "io/Buffer.h"
+#include "AddressSanitizer.h" // for ASan
+#include "common/Common.h" // for uchar8, roundUp
+#include "common/Memory.h" // for alignedFree, alignedFreeConstPtr, alig...
+#include "io/IOException.h" // for ThrowIOE
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+
+using std::unique_ptr;
+
+namespace rawspeed {
+
+unique_ptr<uchar8, decltype(&alignedFree)> Buffer::Create(size_type size) {
+ if (!size)
+ ThrowIOE("Trying to allocate 0 bytes sized buffer.");
+
+ unique_ptr<uchar8, decltype(&alignedFree)> data(
+ alignedMalloc<uchar8, 16>(roundUp(size + BUFFER_PADDING, 16)),
+ &alignedFree);
+ if (!data)
+ ThrowIOE("Failed to allocate %uz bytes memory buffer.", size);
+
+ assert(!ASan::RegionIsPoisoned(data.get(), size));
+
+ return data;
+}
+
+Buffer::Buffer(unique_ptr<uchar8, decltype(&alignedFree)> data_,
+ size_type size_)
+ : size(size_) {
+ if (!size)
+ ThrowIOE("Buffer has zero size?");
+
+ if (data_.get_deleter() != &alignedFree)
+ ThrowIOE("Wrong deleter. Expected rawspeed::alignedFree()");
+
+ data = data_.release();
+ if (!data)
+ ThrowIOE("Memory buffer is nonexistent");
+
+ assert(!ASan::RegionIsPoisoned(data, size));
+
+ isOwner = true;
+}
+
+Buffer::~Buffer() {
+ if (isOwner) {
+ alignedFreeConstPtr(data);
+ }
+}
+
+Buffer& Buffer::operator=(Buffer&& rhs) noexcept {
+ if (this == &rhs) {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ return *this;
+ }
+
+ if (isOwner)
+ alignedFreeConstPtr(data);
+
+ data = rhs.data;
+ size = rhs.size;
+ isOwner = rhs.isOwner;
+
+ assert(!ASan::RegionIsPoisoned(data, size));
+
+ rhs.isOwner = false;
+
+ return *this;
+}
+
+Buffer& Buffer::operator=(const Buffer& rhs) {
+ if (this == &rhs) {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ return *this;
+ }
+
+ Buffer unOwningTmp(rhs.data, rhs.size);
+ *this = std::move(unOwningTmp);
+ assert(!isOwner);
+ assert(!ASan::RegionIsPoisoned(data, size));
+
+ return *this;
+}
+
+#if 0
+Buffer* Buffer::clone() {
+ Buffer *new_map = new Buffer(size);
+ memcpy(new_map->data, data, size);
+ return new_map;
+}
+
+Buffer* Buffer::cloneRandomSize() {
+ uint32 new_size = (rand() | (rand() << 15)) % size;
+ Buffer *new_map = new Buffer(new_size);
+ memcpy(new_map->data, data, new_size);
+ return new_map;
+}
+
+void Buffer::corrupt(int errors) {
+ for (int i = 0; i < errors; i++) {
+ uint32 pos = (rand() | (rand() << 15)) % size;
+ data[pos] = rand() & 0xff;
+ }
+}
+#endif
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/Buffer.h
b/subprojects/rawspeed/src/librawspeed/io/Buffer.h
new file mode 100644
index 00000000..f9419e1f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/Buffer.h
@@ -0,0 +1,212 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "AddressSanitizer.h" // for ASan
+#include "common/Common.h" // for uchar8, uint64, uint32
+#include "common/Memory.h" // for alignedFree
+#include "io/Endianness.h" // for Endianness, Endianness::little, getHos...
+#include "io/IOException.h" // for ThrowIOE
+#include <cassert> // for assert
+#include <memory> // for unique_ptr
+#include <utility> // for swap
+
+namespace rawspeed {
+
+// This allows to specify the nuber of bytes that each Buffer needs to
+// allocate additionally to be able to remove one runtime bounds check
+// in BitStream::fill. There are two sane choices:
+// 0 : allocate exactly as much data as required, or
+// set it to the value of BitStreamCacheBase::MaxProcessBytes
+#define BUFFER_PADDING 0UL
+
+// if the padding is >= 4, bounds checking in BitStream::fill are not compiled,
+// which supposedly saves about 1% on modern CPUs
+// WARNING: if the padding is >= 4, do *NOT* create Buffer from
+// passed unowning pointer and size. Or, subtract BUFFER_PADDING from size.
+// else bound checks will malfunction => bad things can happen !!!
+
+/*************************************************************************
+ * This is the buffer abstaction.
+ *
+ * It allows access to some piece of memory, typically a whole or part
+ * of a raw file. The underlying memory may be owned by the buffer or not.
+ * It supports move operations to properly deal with owneship transfer.
+ * It intentionally supports only read/const access to the underlying memory.
+ *
+ *************************************************************************/
+class Buffer
+{
+public:
+ using size_type = uint32;
+
+protected:
+ const uchar8* data = nullptr;
+ size_type size = 0;
+ bool isOwner = false;
+
+public:
+ // allocates the databuffer, and returns owning non-const pointer.
+ static std::unique_ptr<uchar8, decltype(&alignedFree)> Create(size_type size);
+
+ // constructs an empty buffer
+ Buffer() = default;
+
+ // Allocates the memory
+ explicit Buffer(size_type size_) : Buffer(Create(size_), size_) {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ }
+
+ // creates buffer from owning unique_ptr
+ Buffer(std::unique_ptr<uchar8, decltype(&alignedFree)> data_,
+ size_type size_);
+
+ // Data already allocated
+ explicit Buffer(const uchar8* data_, size_type size_)
+ : data(data_), size(size_) {
+ static_assert(BUFFER_PADDING == 0, "please do make sure that you do NOT "
+ "call this function from YOUR code, and "
+ "then comment-out this assert.");
+ assert(!ASan::RegionIsPoisoned(data, size));
+ }
+
+ // creates a (non-owning) copy / view of rhs
+ Buffer(const Buffer& rhs) : data(rhs.data), size(rhs.size) {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ }
+
+ // Move data and ownership from rhs to this
+ Buffer(Buffer&& rhs) noexcept
+ : data(rhs.data), size(rhs.size), isOwner(rhs.isOwner) {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ rhs.isOwner = false;
+ }
+
+ // Frees memory if owned
+ ~Buffer();
+
+ Buffer& operator=(Buffer&& rhs) noexcept;
+ Buffer& operator=(const Buffer& rhs);
+
+ Buffer getSubView(size_type offset, size_type size_) const {
+ if (!isValid(0, offset))
+ ThrowIOE("Buffer overflow: image file may be truncated");
+
+ return Buffer(getData(offset, size_), size_);
+ }
+
+ Buffer getSubView(size_type offset) const {
+ if (!isValid(0, offset))
+ ThrowIOE("Buffer overflow: image file may be truncated");
+
+ size_type newSize = size - offset;
+ return getSubView(offset, newSize);
+ }
+
+ // get pointer to memory at 'offset', make sure at least 'count' bytes are accessible
+ const uchar8* getData(size_type offset, size_type count) const {
+ if (!isValid(offset, count))
+ ThrowIOE("Buffer overflow: image file may be truncated");
+
+ assert(data);
+ assert(!ASan::RegionIsPoisoned(data + offset, count));
+
+ return data + offset;
+ }
+
+ // convenience getter for single bytes
+ uchar8 operator[](size_type offset) const {
+ return *getData(offset, 1);
+ }
+
+ // std begin/end iterators to allow for range loop
+ const uchar8* begin() const {
+ assert(data);
+ assert(!ASan::RegionIsPoisoned(data, 0));
+ return data;
+ }
+ const uchar8* end() const {
+ assert(data);
+ assert(!ASan::RegionIsPoisoned(data, size));
+ return data + size;
+ }
+
+ // get memory of type T from byte offset 'offset + sizeof(T)*index' and swap byte order if required
+ template<typename T> inline T get(bool inNativeByteOrder, size_type offset, size_type index = 0) const {
+ return getByteSwapped<T>(
+ getData(offset + index * static_cast<size_type>(sizeof(T)),
+ static_cast<size_type>(sizeof(T))),
+ !inNativeByteOrder);
+ }
+
+ inline size_type getSize() const {
+ assert(!ASan::RegionIsPoisoned(data, size));
+ return size;
+ }
+
+ inline bool isValid(size_type offset, size_type count = 1) const {
+ return static_cast<uint64>(offset) + count <=
+ static_cast<uint64>(size) + BUFFER_PADDING;
+ }
+
+// Buffer* clone();
+// /* For testing purposes */
+// void corrupt(int errors);
+// Buffer* cloneRandomSize();
+};
+
+/*
+ * DataBuffer is a simple extension to Buffer. It knows about the byte order
+ * of its contents and can therefore provide save access to larger than
+ * byte sized data, like int, float, etc.
+ */
+class DataBuffer : public Buffer {
+ // FIXME: default should be Endianness::unknown !
+
+ Endianness endianness = Endianness::little;
+
+public:
+ DataBuffer() = default;
+
+ explicit DataBuffer(const Buffer& data_,
+ Endianness endianness_ = Endianness::little)
+ : Buffer(data_), endianness(endianness_) {}
+
+ // get memory of type T from byte offset 'offset + sizeof(T)*index' and swap
+ // byte order if required
+ template <typename T>
+ inline T get(size_type offset, size_type index = 0) const {
+ assert(Endianness::unknown != endianness);
+ assert(Endianness::little == endianness || Endianness::big == endianness);
+
+ return Buffer::get<T>(getHostEndianness() == endianness, offset, index);
+ }
+
+ inline Endianness getByteOrder() const { return endianness; }
+
+ inline Endianness setByteOrder(Endianness endianness_) {
+ std::swap(endianness, endianness_);
+ return endianness_;
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/ByteStream.h
b/subprojects/rawspeed/src/librawspeed/io/ByteStream.h
new file mode 100644
index 00000000..cd2c1059
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/ByteStream.h
@@ -0,0 +1,239 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "AddressSanitizer.h" // for ASan::RegionIsPoisoned
+#include "common/Common.h" // for uchar8, int32, uint32, ushort16, roundUp
+#include "common/Memory.h" // for alignedMalloc
+#include "io/Buffer.h" // for Buffer::size_type, Buffer, DataBuffer
+#include "io/Endianness.h" // for Endianness, Endianness::little
+#include "io/IOException.h" // for IOException (ptr only), ThrowIOE
+#include <cassert> // for assert
+#include <cstring> // for memcmp, memcpy
+#include <limits> // for numeric_limits
+
+namespace rawspeed {
+
+class ByteStream : public DataBuffer
+{
+protected:
+ size_type pos = 0; // position of stream in bytes (this is next byte to deliver)
+
+public:
+ ByteStream() = default;
+ explicit ByteStream(const DataBuffer& buffer) : DataBuffer(buffer) {}
+ ByteStream(const Buffer& buffer, size_type offset, size_type size_,
+ Endianness endianness_ = Endianness::little)
+ : DataBuffer(buffer.getSubView(offset, size_), endianness_) {
+ check(0);
+ }
+ ByteStream(const Buffer& buffer, size_type offset,
+ Endianness endianness_ = Endianness::little)
+ : DataBuffer(buffer, endianness_), pos(offset) {
+ check(0);
+ }
+
+ // deprecated:
+ ByteStream(const Buffer* f, size_type offset, size_type size_,
+ Endianness endianness_ = Endianness::little)
+ : ByteStream(*f, offset, size_, endianness_) {}
+ ByteStream(const Buffer* f, size_type offset,
+ Endianness endianness_ = Endianness::little)
+ : ByteStream(*f, offset, endianness_) {}
+
+ // return ByteStream that starts at given offset
+ // i.e. this->data + offset == getSubStream(offset).data
+ ByteStream getSubStream(size_type offset, size_type size_) const {
+ return ByteStream(getSubView(offset, size_), 0, getByteOrder());
+ }
+
+ ByteStream getSubStream(size_type offset) const {
+ return ByteStream(getSubView(offset), 0, getByteOrder());
+ }
+
+ inline size_type check(size_type bytes) const {
+ if (static_cast<uint64>(pos) + bytes > size)
+ ThrowIOE("Out of bounds access in ByteStream");
+ assert(!ASan::RegionIsPoisoned(data + pos, bytes));
+ return bytes;
+ }
+
+ inline size_type check(size_type nmemb, size_type size_) const {
+ if (size_ && nmemb > std::numeric_limits<size_type>::max() / size_)
+ ThrowIOE("Integer overflow when calculating stream length");
+ return check(nmemb * size_);
+ }
+
+ inline size_type getPosition() const {
+ assert(size >= pos);
+ check(0);
+ return pos;
+ }
+ inline void setPosition(size_type newPos) {
+ pos = newPos;
+ check(0);
+ }
+ inline size_type getRemainSize() const {
+ assert(size >= pos);
+ check(0);
+ return size - pos;
+ }
+ inline const uchar8* peekData(size_type count) const {
+ return Buffer::getData(pos, count);
+ }
+ inline const uchar8* getData(size_type count) {
+ const uchar8* ret = Buffer::getData(pos, count);
+ pos += count;
+ return ret;
+ }
+ inline Buffer getBuffer(size_type size_) {
+ Buffer ret = getSubView(pos, size_);
+ pos += size_;
+ return ret;
+ }
+ inline ByteStream peekStream(size_type size_) const {
+ return getSubStream(pos, size_);
+ }
+ inline ByteStream peekStream(size_type nmemb, size_type size_) const {
+ if (size_ && nmemb > std::numeric_limits<size_type>::max() / size_)
+ ThrowIOE("Integer overflow when calculating stream length");
+ return peekStream(nmemb * size_);
+ }
+ inline ByteStream getStream(size_type size_) {
+ ByteStream ret = peekStream(size_);
+ pos += size_;
+ return ret;
+ }
+ inline ByteStream getStream(size_type nmemb, size_type size_) {
+ if (size_ && nmemb > std::numeric_limits<size_type>::max() / size_)
+ ThrowIOE("Integer overflow when calculating stream length");
+ return getStream(nmemb * size_);
+ }
+
+ inline uchar8 peekByte(size_type i = 0) const {
+ assert(data);
+ check(i+1);
+ return data[pos+i];
+ }
+
+ inline void skipBytes(size_type nbytes) { pos += check(nbytes); }
+ inline void skipBytes(size_type nmemb, size_type size_) {
+ pos += check(nmemb, size_);
+ }
+
+ inline bool hasPatternAt(const char *pattern, size_type size_,
+ size_type relPos) const {
+ assert(data);
+ if (!isValid(pos + relPos, size_))
+ return false;
+ return memcmp(&data[pos + relPos], pattern, size_) == 0;
+ }
+
+ inline bool hasPrefix(const char *prefix, size_type size_) const {
+ return hasPatternAt(prefix, size_, 0);
+ }
+
+ inline bool skipPrefix(const char *prefix, size_type size_) {
+ bool has_prefix = hasPrefix(prefix, size_);
+ if (has_prefix)
+ pos += size_;
+ return has_prefix;
+ }
+
+ inline uchar8 getByte() {
+ assert(data);
+ check(1);
+ return data[pos++];
+ }
+
+ template<typename T> inline T peek(size_type i = 0) const {
+ return DataBuffer::get<T>(pos, i);
+ }
+
+ inline ushort16 peekU16() { return peek<ushort16>(); }
+
+ template<typename T> inline T get() {
+ auto ret = peek<T>();
+ pos += sizeof(T);
+ return ret;
+ }
+
+ inline ushort16 getU16() { return get<ushort16>(); }
+ inline int32 getI32() { return get<int32>(); }
+ inline uint32 getU32() { return get<uint32>(); }
+ inline float getFloat() { return get<float>(); }
+
+ const char* peekString() const {
+ assert(data);
+ if (memchr(peekData(getRemainSize()), 0, getRemainSize()) == nullptr)
+ ThrowIOE("String is not null-terminated");
+ return reinterpret_cast<const char*>(&data[pos]);
+ }
+
+ // Increments the stream to after the next zero byte and returns the bytes in between (not a copy).
+ // If the first byte is zero, stream is incremented one.
+ const char* getString() {
+ assert(data);
+ size_type start = pos;
+ bool isNullTerminator = false;
+ do {
+ check(1);
+ isNullTerminator = (data[pos] == '\0');
+ pos++;
+ } while (!isNullTerminator);
+ return reinterpret_cast<const char*>(&data[start]);
+ }
+
+ // recalculate the internal data/position information such that current position
+ // i.e. getData() before == getData() after but getPosition() after == newPosition
+ // this is only used for DNGPRIVATEDATA handling to restore the original offset
+ // in case the private data / maker note has been moved within in the file
+ // TODO: could add a lower bound check later if required.
+ void rebase(const size_type newPosition, const size_type newSize) {
+ const uchar8* const oldData = getData(newSize);
+
+ pos = newPosition;
+ size = pos + newSize;
+ data = oldData - pos;
+
+#ifndef NDEBUG
+ // check that all the assumptions still hold, and we rebased correctly
+ assert(getData(0) == oldData);
+ assert(getPosition() == newPosition);
+ assert(getRemainSize() == newSize);
+#endif
+ }
+
+ // special factory function to set up internal buffer with copy of passed data.
+ // only necessary to create 'fake' TiffEntries (see e.g. RAF)
+ static ByteStream createCopy(void* data_, size_type size_) {
+ ByteStream bs;
+ auto* new_data = alignedMalloc<uchar8, 8>(roundUp(size_, 8));
+ memcpy(new_data, data_, size_);
+ bs.data = new_data;
+ bs.size = size_;
+ bs.isOwner = true;
+ return bs; // hint: copy elision or move will happen
+ }
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/Endianness.h
b/subprojects/rawspeed/src/librawspeed/io/Endianness.h
new file mode 100644
index 00000000..7e05e3d7
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/Endianness.h
@@ -0,0 +1,136 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, ushort16, uint64, int32, short16
+#include <cassert> // for assert
+#include <cstring> // for memcpy
+// IWYU pragma: no_include "io/EndiannessTest.h"
+
+namespace rawspeed {
+
+enum class Endianness { little = 0xDEAD, big = 0xBEEF, unknown = 0x0BAD };
+
+inline Endianness getHostEndiannessRuntime() {
+ ushort16 testvar = 0xfeff;
+ uint32 firstbyte = (reinterpret_cast<uchar8*>(&testvar))[0];
+ if (firstbyte == 0xff)
+ return Endianness::little;
+ if (firstbyte == 0xfe)
+ return Endianness::big;
+
+ assert(false);
+
+ // Return something to make compilers happy
+ return Endianness::unknown;
+}
+
+inline Endianness getHostEndianness() {
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return Endianness::little;
+#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ return Endianness::big;
+#elif defined(__BYTE_ORDER__)
+#error "uhm, __BYTE_ORDER__ has some strange value"
+#else
+ return getHostEndiannessRuntime();
+#endif
+}
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#define BSWAP16(A) _byteswap_ushort(A)
+#define BSWAP32(A) _byteswap_ulong(A)
+#define BSWAP64(A) _byteswap_uint64(A)
+#else
+#define BSWAP16(A) __builtin_bswap16(A)
+#define BSWAP32(A) __builtin_bswap32(A)
+#define BSWAP64(A) __builtin_bswap64(A)
+#endif
+
+inline short16 getByteSwapped(short16 v) {
+ return static_cast<short16>(BSWAP16(static_cast<ushort16>(v)));
+}
+inline ushort16 getByteSwapped(ushort16 v) {
+ return static_cast<ushort16>(BSWAP16(v));
+}
+inline int32 getByteSwapped(int32 v) {
+ return static_cast<int32>(BSWAP32(static_cast<uint32>(v)));
+}
+inline uint32 getByteSwapped(uint32 v) {
+ return static_cast<uint32>(BSWAP32(v));
+}
+inline uint64 getByteSwapped(uint64 v) {
+ return BSWAP64(static_cast<uint64>(v));
+}
+
+// the float/double versions use two memcpy which guarantee strict aliasing
+// and are compiled into the same assembly as the popular union trick.
+inline float getByteSwapped(float f) {
+ uint32 i;
+ memcpy(&i, &f, sizeof(i));
+ i = getByteSwapped(i);
+ memcpy(&f, &i, sizeof(i));
+ return f;
+}
+inline double getByteSwapped(double d) {
+ uint64 i;
+ memcpy(&i, &d, sizeof(i));
+ i = getByteSwapped(i);
+ memcpy(&d, &i, sizeof(i));
+ return d;
+}
+
+template <typename T> inline T getByteSwapped(const void* data, bool bswap) {
+ T ret;
+ // all interesting compilers optimize this memcpy into a single move
+ // this is the most effective way to load some bytes without running into
+ // alignment or aliasing issues
+ memcpy(&ret, data, sizeof(T));
+ return bswap ? getByteSwapped(ret) : ret;
+}
+
+// The following functions may be used to get a multi-byte sized tyoe from some
+// memory location converted to the native byte order of the host.
+// 'BE' suffix: source byte order is known to be big endian
+// 'LE' suffix: source byte order is known to be little endian
+// Note: these functions should be avoided if higher level access from
+// Buffer/DataBuffer classes is available.
+
+template <typename T> inline T getBE(const void* data) {
+ return getByteSwapped<T>(data, getHostEndianness() == Endianness::little);
+}
+
+template <typename T> inline T getLE(const void* data) {
+ return getByteSwapped<T>(data, getHostEndianness() == Endianness::big);
+}
+
+inline ushort16 getU16BE(const void* data) { return getBE<ushort16>(data); }
+inline ushort16 getU16LE(const void* data) { return getLE<ushort16>(data); }
+inline uint32 getU32BE(const void* data) { return getBE<uint32>(data); }
+inline uint32 getU32LE(const void* data) { return getLE<uint32>(data); }
+
+#undef BSWAP64
+#undef BSWAP32
+#undef BSWAP16
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileIO.h
b/subprojects/rawspeed/src/librawspeed/io/FileIO.h
new file mode 100644
index 00000000..b9bfc8d8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileIO.h
@@ -0,0 +1,59 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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
+*/
+
+#if !(defined(__unix__) || defined(__APPLE__))
+#ifndef NOMINMAX
+#define NOMINMAX // do not want the min()/max() macros!
+#endif
+
+#include "io/FileIOException.h" // for FileIOException (ptr only), ThrowFIE
+#include <Windows.h>
+#include <functional> // for bind
+#include <io.h>
+#include <tchar.h>
+#include <vector> // for vector
+
+namespace rawspeed {
+
+inline std::wstring widenFileName(const char* fileName) {
+ assert(fileName);
+
+ std::wstring wFileName;
+
+ auto f = std::bind(MultiByteToWideChar, CP_UTF8, 0, fileName, -1,
+ std::placeholders::_1, std::placeholders::_2);
+
+ // how many wide characters are needed to store converted string?
+ const auto expectedLen = f(nullptr, 0);
+ wFileName.resize(expectedLen);
+
+ // convert.
+ const auto actualLen = f(&wFileName[0], wFileName.size());
+
+ // did we get expected number of characters?
+ if (actualLen != expectedLen)
+ ThrowFIE("Could not convert filename \"%s\".", fileName);
+
+ return wFileName;
+}
+
+} // namespace rawspeed
+
+#endif
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileIOException.h
b/subprojects/rawspeed/src/librawspeed/io/FileIOException.h
new file mode 100644
index 00000000..315a3bd2
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileIOException.h
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 "decoders/RawDecoderException.h" // for RawDecoderException
+#include <string> // for string
+
+namespace rawspeed {
+
+class FileIOException final : public RawDecoderException {
+public:
+ explicit FileIOException(const std::string& msg) : RawDecoderException(msg) {}
+ explicit FileIOException(const char* msg) : RawDecoderException(msg) {}
+};
+
+#define ThrowFIE(...) \
+ ThrowExceptionHelper(rawspeed::FileIOException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileReader.cpp
b/subprojects/rawspeed/src/librawspeed/io/FileReader.cpp
new file mode 100644
index 00000000..fe1518cc
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileReader.cpp
@@ -0,0 +1,120 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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
+*/
+
+#include "io/FileReader.h"
+#include "io/Buffer.h" // for Buffer, Buffer::size_type
+#include "io/FileIOException.h" // for ThrowFIE
+#include <cstdio> // for fseek, fclose, feof, ferror, fopen
+#include <fcntl.h> // for SEEK_END, SEEK_SET
+#include <limits> // for numeric_limits
+#include <memory> // for unique_ptr, make_unique, operator==
+#include <utility> // for move
+
+#if !(defined(__unix__) || defined(__APPLE__))
+#ifndef NOMINMAX
+#define NOMINMAX // do not want the min()/max() macros!
+#endif
+
+#include "io/FileIO.h" // for widenFileName
+#include <Windows.h>
+#include <io.h>
+#include <tchar.h>
+#endif
+
+namespace rawspeed {
+
+std::unique_ptr<const Buffer> FileReader::readFile() {
+ size_t fileSize = 0;
+
+#if defined(__unix__) || defined(__APPLE__)
+ using file_ptr = std::unique_ptr<FILE, decltype(&fclose)>;
+ file_ptr file(fopen(fileName, "rb"), &fclose);
+
+ if (file == nullptr)
+ ThrowFIE("Could not open file \"%s\".", fileName);
+
+ fseek(file.get(), 0, SEEK_END);
+ const auto size = ftell(file.get());
+
+ if (size <= 0)
+ ThrowFIE("File is 0 bytes.");
+
+ fileSize = size;
+
+ if (fileSize > std::numeric_limits<Buffer::size_type>::max())
+ ThrowFIE("File is too big (%zu bytes).", fileSize);
+
+ fseek(file.get(), 0, SEEK_SET);
+
+ auto dest = Buffer::Create(fileSize);
+
+ auto bytes_read = fread(dest.get(), 1, fileSize, file.get());
+ if (fileSize != bytes_read) {
+ ThrowFIE("Could not read file, %s.",
+ feof(file.get()) ? "reached end-of-file"
+ : (ferror(file.get()) ? "file reading error"
+ : "unknown problem"));
+ }
+
+#else // __unix__
+
+ auto wFileName = widenFileName(fileName);
+
+ using file_ptr = std::unique_ptr<std::remove_pointer<HANDLE>::type,
+ decltype(&CloseHandle)>;
+ file_ptr file(CreateFileW(wFileName.data(), GENERIC_READ, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
+ nullptr),
+ &CloseHandle);
+
+ if (file.get() == INVALID_HANDLE_VALUE)
+ ThrowFIE("Could not open file \"%s\".", fileName);
+
+ LARGE_INTEGER size;
+ GetFileSizeEx(file.get(), &size);
+
+ static_assert(
+ std::numeric_limits<Buffer::size_type>::max() ==
+ std::numeric_limits<decltype(size.LowPart)>::max(),
+ "once Buffer migrates to 64-bit index, this needs to be updated.");
+
+ if (size.HighPart > 0)
+ ThrowFIE("File is too big.");
+ if (size.LowPart <= 0)
+ ThrowFIE("File is 0 bytes.");
+
+ auto dest = Buffer::Create(size.LowPart);
+
+ DWORD bytes_read;
+ if (!ReadFile(file.get(), dest.get(), size.LowPart, &bytes_read, nullptr))
+ ThrowFIE("Could not read file.");
+
+ if (size.LowPart != bytes_read)
+ ThrowFIE("Could not read file.");
+
+ fileSize = size.LowPart;
+
+#endif // __unix__
+
+ return std::make_unique<Buffer>(move(dest), fileSize);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileReader.h
b/subprojects/rawspeed/src/librawspeed/io/FileReader.h
new file mode 100644
index 00000000..7efdd113
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileReader.h
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class Buffer;
+
+class FileReader
+{
+ const char* fileName;
+
+public:
+ explicit FileReader(const char* fileName_) : fileName(fileName_) {}
+
+ std::unique_ptr<const Buffer> readFile();
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileWriter.cpp
b/subprojects/rawspeed/src/librawspeed/io/FileWriter.cpp
new file mode 100644
index 00000000..5f05a75c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileWriter.cpp
@@ -0,0 +1,81 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "io/FileWriter.h"
+#include "common/Common.h" // for uint32
+#include "io/Buffer.h" // for Buffer
+#include "io/FileIOException.h" // for FileIOException
+#include <cstdio> // for fclose, fopen, fwrite, FILE, NULL
+
+#if !defined(__unix__) && !defined(__APPLE__)
+#ifndef NOMINMAX
+#define NOMINMAX // do not want the min()/max() macros!
+#endif
+
+#include "io/FileIO.h" // for widenFileName
+#include <Windows.h>
+#include <io.h>
+#include <tchar.h>
+#endif // !defined(__unix__) && !defined(__APPLE__)
+
+namespace rawspeed {
+
+FileWriter::FileWriter(const char *_filename) : mFilename(_filename) {}
+
+void FileWriter::writeFile(Buffer* filemap, uint32 size) {
+ if (size > filemap->getSize())
+ size = filemap->getSize();
+#if defined(__unix__) || defined(__APPLE__)
+ size_t bytes_written = 0;
+ FILE *file;
+
+ file = fopen(mFilename, "wb");
+ if (file == nullptr)
+ ThrowFIE("Could not open file.");
+
+ const auto src = filemap->getData(0, filemap->getSize());
+ bytes_written = fwrite(src, 1, size != 0 ? size : filemap->getSize(), file);
+ fclose(file);
+ if (size != bytes_written) {
+ ThrowFIE("Could not write file.");
+ }
+
+#else // __unix__
+ auto wFileName = widenFileName(mFilename);
+ HANDLE file_h; // File handle
+ file_h =
+ CreateFileW(wFileName.data(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+ if (file_h == INVALID_HANDLE_VALUE) {
+ ThrowFIE("Could not open file.");
+ }
+
+ DWORD bytes_written;
+ if (!WriteFile(file_h, filemap->getData(0, filemap->getSize()),
+ size ? size : filemap->getSize(), &bytes_written, nullptr)) {
+ CloseHandle(file_h);
+ ThrowFIE("Could not read file.");
+ }
+ CloseHandle(file_h);
+
+#endif // __unix__
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/FileWriter.h
b/subprojects/rawspeed/src/librawspeed/io/FileWriter.h
new file mode 100644
index 00000000..639e3830
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/FileWriter.h
@@ -0,0 +1,42 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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
+
+namespace rawspeed {
+
+class Buffer;
+
+class FileWriter
+{
+public:
+ explicit FileWriter(const char* filename);
+
+ void writeFile(Buffer* fileMap, uint32 size = 0);
+ const char* Filename() const { return mFilename; }
+ // void Filename(const char * val) { mFilename = val; }
+
+private:
+ const char* mFilename;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/IOException.h
b/subprojects/rawspeed/src/librawspeed/io/IOException.h
new file mode 100644
index 00000000..74f5c038
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/IOException.h
@@ -0,0 +1,37 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 RawspeedException
+#include <string> // for string
+
+namespace rawspeed {
+
+class IOException final : public RawspeedException {
+public:
+ explicit IOException(const std::string& msg) : RawspeedException(msg) {}
+ explicit IOException(const char* msg) : RawspeedException(msg) {}
+};
+
+#define ThrowIOE(...) ThrowExceptionHelper(rawspeed::IOException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/io/meson.build
b/subprojects/rawspeed/src/librawspeed/io/meson.build
new file mode 100644
index 00000000..479bbebf
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/io/meson.build
@@ -0,0 +1,25 @@
+sources = files(
+ 'BitPumpJPEG.h',
+ 'BitPumpLSB.h',
+ 'BitPumpMSB.h',
+ 'BitPumpMSB16.h',
+ 'BitPumpMSB32.h',
+ 'BitStream.h',
+ 'Buffer.cpp',
+ 'Buffer.h',
+ 'ByteStream.h',
+ 'Endianness.h',
+ 'FileIO.h',
+ 'FileIOException.h',
+ 'FileReader.cpp',
+ 'FileReader.h',
+ 'FileWriter.cpp',
+ 'FileWriter.h',
+ 'IOException.h',
+)
+
+librawspeed_io = static_library(
+ 'rawspeed-io',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/meson.build
b/subprojects/rawspeed/src/librawspeed/meson.build
new file mode 100644
index 00000000..937b5fcb
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/meson.build
@@ -0,0 +1,40 @@
+librawspeed_default_library = get_option('default_library')
+rawspeed_librawspeed_include = include_directories('.')
+
+librawspeed_install = true
+if librawspeed_default_library == 'static'
+ librawspeed_install = false
+endif
+
+subdir('common')
+subdir('decoders')
+subdir('decompressors')
+subdir('interpolators')
+subdir('io')
+subdir('metadata')
+subdir('parsers')
+subdir('tiff')
+
+librawspeed_libraries = [
+ librawspeed_common,
+ librawspeed_decoders,
+ librawspeed_decompressors,
+ librawspeed_interpolators,
+ librawspeed_io,
+ librawspeed_metadata,
+ librawspeed_parsers,
+ librawspeed_tiff,
+]
+
+librawspeed_sources = []
+
+librawspeed = library(
+ meson.project_name(),
+ librawspeed_sources,
+ include_directories: rawspeed_librawspeed_include,
+ install: librawspeed_install,
+ install_dir: rawspeed_libdir,
+ link_with: librawspeed_libraries,
+)
+
+rawspeed_dep = declare_dependency(link_with: librawspeed)
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/BlackArea.h
b/subprojects/rawspeed/src/librawspeed/metadata/BlackArea.h
new file mode 100644
index 00000000..e80aa218
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/BlackArea.h
@@ -0,0 +1,36 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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
+
+namespace rawspeed {
+
+class BlackArea final {
+public:
+ BlackArea(int offset_, int size_, bool isVertical_)
+ : offset(offset_), size(size_), isVertical(isVertical_) {}
+ uint32 offset; // Offset in bayer pixels.
+ uint32 size; // Size in bayer pixels.
+ bool isVertical; // Otherwise horizontal
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/Camera.cpp
b/subprojects/rawspeed/src/librawspeed/metadata/Camera.cpp
new file mode 100644
index 00000000..f8652686
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/Camera.cpp
@@ -0,0 +1,335 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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 "metadata/Camera.h"
+#include "common/Common.h" // for split_string, uint32
+#include "common/Point.h" // for iPoint2D
+#include "metadata/CameraMetadataException.h" // for ThrowCME
+#include <cctype> // for tolower
+#include <cstdio> // for size_t
+#include <map> // for map
+#include <stdexcept> // for out_of_range
+#include <string> // for string, allocator, ope...
+#include <vector> // for vector
+
+#ifdef HAVE_PUGIXML
+#include <pugixml.hpp> // for xml_node, xml_attribute
+using pugi::xml_node;
+#endif
+
+using std::vector;
+using std::string;
+using std::map;
+
+namespace rawspeed {
+
+#ifdef HAVE_PUGIXML
+Camera::Camera(const pugi::xml_node& camera) : cfa(iPoint2D(0, 0)) {
+ make = canonical_make = camera.attribute("make").as_string();
+ if (make.empty())
+ ThrowCME(R"("make" attribute not found.)");
+
+ model = canonical_model = canonical_alias = camera.attribute("model").as_string();
+ // chdk cameras seem to have an empty model?
+ if (!camera.attribute("model")) // (model.empty())
+ ThrowCME(R"("model" attribute not found.)");
+
+ canonical_id = make + " " + model;
+
+ supported = camera.attribute("supported").as_string("yes") == string("yes");
+ mode = camera.attribute("mode").as_string("");
+ decoderVersion = camera.attribute("decoder_version").as_int(0);
+
+ for (xml_node c : camera.children()) {
+ parseCameraChild(c);
+ }
+}
+#endif
+
+Camera::Camera(const Camera* camera, uint32 alias_num) : cfa(iPoint2D(0,0))
+{
+ if (alias_num >= camera->aliases.size())
+ ThrowCME("Internal error, alias number out of range specified.");
+
+ *this = *camera;
+ model = camera->aliases[alias_num];
+ canonical_alias = camera->canonical_aliases[alias_num];
+ aliases.clear();
+ canonical_aliases.clear();
+}
+
+#ifdef HAVE_PUGIXML
+static string name(const xml_node &a) {
+ return string(a.name());
+}
+#endif
+
+const map<char, CFAColor> Camera::char2enum = {
+ {'g', CFA_GREEN}, {'r', CFA_RED}, {'b', CFA_BLUE},
+ {'f', CFA_FUJI_GREEN}, {'c', CFA_CYAN}, {'m', CFA_MAGENTA},
+ {'y', CFA_YELLOW},
+};
+
+const map<string, CFAColor> Camera::str2enum = {
+ {"GREEN", CFA_GREEN}, {"RED", CFA_RED},
+ {"BLUE", CFA_BLUE}, {"FUJI_GREEN", CFA_FUJI_GREEN},
+ {"CYAN", CFA_CYAN}, {"MAGENTA", CFA_MAGENTA},
+ {"YELLOW", CFA_YELLOW},
+};
+
+#ifdef HAVE_PUGIXML
+void Camera::parseCFA(const xml_node &cur) {
+ if (name(cur) != "CFA" && name(cur) != "CFA2")
+ ThrowCME("Not an CFA/CFA2 node!");
+
+ cfa.setSize(iPoint2D(cur.attribute("width").as_int(0),
+ cur.attribute("height").as_int(0)));
+ for (xml_node c : cur.children()) {
+ if (name(c) == "ColorRow") {
+ int y = c.attribute("y").as_int(-1);
+ if (y < 0 || y >= cfa.getSize().y) {
+ ThrowCME("Invalid y coordinate in CFA array of camera %s %s",
+ make.c_str(), model.c_str());
+ }
+ string key = c.child_value();
+ if (static_cast<int>(key.size()) != cfa.getSize().x) {
+ ThrowCME("Invalid number of colors in definition for row %d in "
+ "camera %s %s. Expected %d, found %zu.",
+ y, make.c_str(), model.c_str(), cfa.getSize().x, key.size());
+ }
+ for (size_t x = 0; x < key.size(); ++x) {
+ auto c1 = key[x];
+ CFAColor c2;
+
+ try {
+ c2 = char2enum.at(static_cast<char>(tolower(c1)));
+ } catch (std::out_of_range&) {
+ ThrowCME("Invalid color in CFA array of camera %s %s: %c",
+ make.c_str(), model.c_str(), c1);
+ }
+
+ cfa.setColorAt(iPoint2D(static_cast<int>(x), y), c2);
+ }
+ } else if (name(c) == "Color") {
+ int x = c.attribute("x").as_int(-1);
+ if (x < 0 || x >= cfa.getSize().x) {
+ ThrowCME("Invalid x coordinate in CFA array of camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ int y = c.attribute("y").as_int(-1);
+ if (y < 0 || y >= cfa.getSize().y) {
+ ThrowCME("Invalid y coordinate in CFA array of camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ auto c1 = c.child_value();
+ CFAColor c2;
+
+ try {
+ c2 = str2enum.at(c1);
+ } catch (std::out_of_range&) {
+ ThrowCME("Invalid color in CFA array of camera %s %s: %s",
+ make.c_str(), model.c_str(), c1);
+ }
+
+ cfa.setColorAt(iPoint2D(x, y), c2);
+ }
+ }
+}
+
+void Camera::parseCrop(const xml_node &cur) {
+ if (name(cur) != "Crop")
+ ThrowCME("Not an Crop node!");
+
+ cropSize.x = cur.attribute("width").as_int(0);
+ cropSize.y = cur.attribute("height").as_int(0);
+ cropPos.x = cur.attribute("x").as_int(0);
+ cropPos.y = cur.attribute("y").as_int(0);
+
+ if (cropPos.x < 0)
+ ThrowCME("Negative X axis crop specified in camera %s %s", make.c_str(),
+ model.c_str());
+ if (cropPos.y < 0)
+ ThrowCME("Negative Y axis crop specified in camera %s %s", make.c_str(),
+ model.c_str());
+}
+
+void Camera::parseBlackAreas(const xml_node &cur) {
+
+ if (name(cur) != "BlackAreas")
+ ThrowCME("Not an BlackAreas node!");
+
+ for (xml_node c : cur.children()) {
+ if (name(c) == "Vertical") {
+ int x = c.attribute("x").as_int(-1);
+ if (x < 0) {
+ ThrowCME(
+ "Invalid x coordinate in vertical BlackArea of in camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ int w = c.attribute("width").as_int(-1);
+ if (w < 0) {
+ ThrowCME("Invalid width in vertical BlackArea of in camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ blackAreas.emplace_back(x, w, true);
+
+ } else if (name(c) == "Horizontal") {
+
+ int y = c.attribute("y").as_int(-1);
+ if (y < 0) {
+ ThrowCME("Invalid y coordinate in horizontal BlackArea of camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ int h = c.attribute("height").as_int(-1);
+ if (h < 0) {
+ ThrowCME("Invalid height in horizontal BlackArea of camera %s %s",
+ make.c_str(), model.c_str());
+ }
+
+ blackAreas.emplace_back(y, h, false);
+ }
+ }
+}
+
+void Camera::parseAliases(const xml_node &cur) {
+ if (name(cur) != "Aliases")
+ ThrowCME("Not an Aliases node!");
+
+ for (xml_node c : cur.children("Alias")) {
+ aliases.emplace_back(c.child_value());
+ canonical_aliases.emplace_back(
+ c.attribute("id").as_string(c.child_value()));
+ }
+}
+
+void Camera::parseHints(const xml_node &cur) {
+ if (name(cur) != "Hints")
+ ThrowCME("Not an Hints node!");
+
+ for (xml_node c : cur.children("Hint")) {
+ string name = c.attribute("name").as_string();
+ if (name.empty())
+ ThrowCME("Could not find name for hint for %s %s camera.", make.c_str(),
+ model.c_str());
+
+ string value = c.attribute("value").as_string();
+
+ hints.add(name, value);
+ }
+}
+
+void Camera::parseID(const xml_node &cur) {
+ if (name(cur) != "ID")
+ ThrowCME("Not an ID node!");
+
+ canonical_make = cur.attribute("make").as_string();
+ if (canonical_make.empty())
+ ThrowCME("Could not find make for ID for %s %s camera.", make.c_str(),
+ model.c_str());
+
+ canonical_alias = canonical_model = cur.attribute("model").as_string();
+ if (canonical_model.empty())
+ ThrowCME("Could not find model for ID for %s %s camera.", make.c_str(),
+ model.c_str());
+
+ canonical_id = cur.child_value();
+}
+
+void Camera::parseSensor(const xml_node &cur) {
+ if (name(cur) != "Sensor")
+ ThrowCME("Not an Sensor node!");
+
+ auto stringToListOfInts = [&cur](const char* attribute) {
+ vector<int> ret;
+ for (const string& s : splitString(cur.attribute(attribute).as_string()))
+ ret.push_back(stoi(s));
+ return ret;
+ };
+
+ int min_iso = cur.attribute("iso_min").as_int(0);
+ int max_iso = cur.attribute("iso_max").as_int(0);
+ int black = cur.attribute("black").as_int(-1);
+ int white = cur.attribute("white").as_int(65536);
+
+ vector<int> black_colors = stringToListOfInts("black_colors");
+ vector<int> iso_list = stringToListOfInts("iso_list");
+ if (!iso_list.empty()) {
+ for (int iso : iso_list) {
+ sensorInfo.emplace_back(black, white, iso, iso, black_colors);
+ }
+ } else {
+ sensorInfo.emplace_back(black, white, min_iso, max_iso, black_colors);
+ }
+}
+
+void Camera::parseCameraChild(const xml_node &cur) {
+ if (name(cur) == "CFA" || name(cur) == "CFA2") {
+ parseCFA(cur);
+ } else if (name(cur) == "Crop") {
+ parseCrop(cur);
+ } else if (name(cur) == "BlackAreas") {
+ parseBlackAreas(cur);
+ } else if (name(cur) == "Aliases") {
+ parseAliases(cur);
+ } else if (name(cur) == "Hints") {
+ parseHints(cur);
+ } else if (name(cur) == "ID") {
+ parseID(cur);
+ } else if (name(cur) == "Sensor") {
+ parseSensor(cur);
+ }
+}
+#endif
+
+const CameraSensorInfo* Camera::getSensorInfo(int iso) const {
+ if (sensorInfo.empty()) {
+ ThrowCME("Camera '%s' '%s', mode '%s' has no <Sensor> entries.",
+ make.c_str(), model.c_str(), mode.c_str());
+ }
+
+ // If only one, just return that
+ if (sensorInfo.size() == 1)
+ return &sensorInfo.front();
+
+ vector<const CameraSensorInfo*> candidates;
+ for (auto& i : sensorInfo) {
+ if (i.isIsoWithin(iso))
+ candidates.push_back(&i);
+ }
+
+ if (candidates.size() == 1)
+ return candidates.front();
+
+ for (auto i : candidates) {
+ if (!i->isDefault())
+ return i;
+ }
+
+ // Several defaults??? Just return first
+ return candidates.front();
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/Camera.h
b/subprojects/rawspeed/src/librawspeed/metadata/Camera.h
new file mode 100644
index 00000000..4a650d68
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/Camera.h
@@ -0,0 +1,121 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h"
+#include "common/Common.h" // for uint32
+#include "common/Point.h" // for iPoint2D
+#include "metadata/BlackArea.h" // for BlackArea
+#include "metadata/CameraSensorInfo.h" // for CameraSensorInfo
+#include "metadata/ColorFilterArray.h" // for ColorFilterArray
+#include <map> // for map, _Rb_tree_const_iterator
+#include <sstream> // for istringstream, basic_istream
+#include <string> // for string, basic_string, operator>>
+#include <utility> // for pair
+#include <vector> // for vector
+
+#ifdef HAVE_PUGIXML
+
+namespace pugi {
+class xml_node;
+} // namespace pugi
+
+#endif
+
+namespace rawspeed {
+
+class Hints
+{
+ std::map<std::string, std::string> data;
+public:
+ void add(const std::string& key, const std::string& value)
+ {
+ data.insert({key, value});
+ }
+
+ bool has(const std::string& key) const
+ {
+ return data.find(key) != data.end();
+ }
+
+ template <typename T>
+ T get(const std::string& key, T defaultValue) const
+ {
+ auto hint = data.find(key);
+ if (hint != data.end() && !hint->second.empty()) {
+ std::istringstream iss(hint->second);
+ iss >> defaultValue;
+ }
+ return defaultValue;
+ }
+
+ bool get(const std::string& key, bool defaultValue) const {
+ auto hint = data.find(key);
+ if (hint == data.end())
+ return defaultValue;
+ return "true" == hint->second;
+ }
+};
+
+class Camera
+{
+public:
+#ifdef HAVE_PUGIXML
+ explicit Camera(const pugi::xml_node& camera);
+#endif
+
+ Camera(const Camera* camera, uint32 alias_num);
+ const CameraSensorInfo* getSensorInfo(int iso) const;
+ std::string make;
+ std::string model;
+ std::string mode;
+ std::string canonical_make;
+ std::string canonical_model;
+ std::string canonical_alias;
+ std::string canonical_id;
+ std::vector<std::string> aliases;
+ std::vector<std::string> canonical_aliases;
+ ColorFilterArray cfa;
+ bool supported;
+ iPoint2D cropSize;
+ iPoint2D cropPos;
+ std::vector<BlackArea> blackAreas;
+ std::vector<CameraSensorInfo> sensorInfo;
+ int decoderVersion;
+ Hints hints;
+protected:
+ static const std::map<char, CFAColor> char2enum;
+ static const std::map<std::string, CFAColor> str2enum;
+
+#ifdef HAVE_PUGIXML
+ void parseCFA(const pugi::xml_node &node);
+ void parseCrop(const pugi::xml_node &node);
+ void parseBlackAreas(const pugi::xml_node &node);
+ void parseAliases(const pugi::xml_node &node);
+ void parseHints(const pugi::xml_node &node);
+ void parseID(const pugi::xml_node &node);
+ void parseSensor(const pugi::xml_node &node);
+
+ void parseCameraChild(const pugi::xml_node &node);
+#endif
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.cpp
b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.cpp
new file mode 100644
index 00000000..748caf6c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.cpp
@@ -0,0 +1,161 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "metadata/CameraMetaData.h"
+#include "common/Common.h" // for uint32, trimSpaces
+#include "metadata/Camera.h" // for Camera
+#include "metadata/CameraMetadataException.h" // for ThrowCME
+#include <algorithm> // for find_if
+#include <map> // for _Rb_tree_iterator, map
+#include <string> // for string, operator==
+#include <utility> // for pair
+#include <vector> // for vector
+
+#ifdef HAVE_PUGIXML
+#include <pugixml.hpp> // for xml_document, xml_pars...
+using pugi::xml_node;
+using pugi::xml_document;
+using pugi::xml_parse_result;
+#endif
+
+using std::string;
+
+namespace rawspeed {
+
+#ifdef HAVE_PUGIXML
+CameraMetaData::CameraMetaData(const char *docname) {
+ xml_document doc;
+
+#if defined(__unix__) || defined(__APPLE__)
+ xml_parse_result result = doc.load_file(docname);
+#else
+ xml_parse_result result = doc.load_file(pugi::as_wide(docname).c_str());
+#endif
+
+ if (!result) {
+ ThrowCME(
+ "XML Document could not be parsed successfully. Error was: %s in %s",
+ result.description(), doc.child("node").attribute("attr").value());
+ }
+
+ for (xml_node camera : doc.child("Cameras").children("Camera")) {
+ const auto* cam = addCamera(std::make_unique<Camera>(camera));
+
+ if (cam == nullptr)
+ continue;
+
+ // Create cameras for aliases.
+ for (auto i = 0UL; i < cam->aliases.size(); i++) {
+ addCamera(std::make_unique<Camera>(cam, i));
+ }
+ }
+}
+#endif
+
+static inline CameraId getId(const string& make, const string& model,
+ const string& mode) {
+ CameraId id;
+ id.make = trimSpaces(make);
+ id.model = trimSpaces(model);
+ id.mode = trimSpaces(mode);
+
+ return id;
+}
+
+const Camera* CameraMetaData::getCamera(const string& make, const string& model,
+ const string& mode) const {
+ auto camera = cameras.find(getId(make, model, mode));
+ return camera == cameras.end() ? nullptr : camera->second.get();
+}
+
+const Camera* CameraMetaData::getCamera(const string& make,
+ const string& model) const {
+ auto id = getId(make, model, "");
+
+ auto iter = find_if(cameras.cbegin(), cameras.cend(),
+ [&id](decltype(*cameras.cbegin())& i) -> bool {
+ const auto& cid = i.first;
+ return tie(id.make, id.model) ==
+ tie(cid.make, cid.model);
+ });
+
+ if (iter == cameras.end())
+ return nullptr;
+
+ return iter->second.get();
+}
+
+bool CameraMetaData::hasCamera(const string& make, const string& model,
+ const string& mode) const {
+ return getCamera(make, model, mode);
+}
+
+const Camera* __attribute__((pure))
+CameraMetaData::getChdkCamera(uint32 filesize) const {
+ auto camera = chdkCameras.find(filesize);
+ return camera == chdkCameras.end() ? nullptr : camera->second;
+}
+
+bool __attribute__((pure))
+CameraMetaData::hasChdkCamera(uint32 filesize) const {
+ return chdkCameras.end() != chdkCameras.find(filesize);
+}
+
+const Camera* CameraMetaData::addCamera(std::unique_ptr<Camera> cam) {
+ auto id = getId(cam->make, cam->model, cam->mode);
+ if (cameras.end() != cameras.find(id)) {
+ writeLog(
+ DEBUG_PRIO_WARNING,
+ "CameraMetaData: Duplicate entry found for camera: %s %s, Skipping!",
+ cam->make.c_str(), cam->model.c_str());
+ return nullptr;
+ }
+ cameras[id] = std::move(cam);
+
+ if (string::npos != cameras[id]->mode.find("chdk")) {
+ auto filesize_hint = cameras[id]->hints.get("filesize", string());
+ if (filesize_hint.empty()) {
+ writeLog(DEBUG_PRIO_WARNING,
+ "CameraMetaData: CHDK camera: %s %s, no \"filesize\" hint set!",
+ cameras[id]->make.c_str(), cameras[id]->model.c_str());
+ } else {
+ chdkCameras[stoi(filesize_hint)] = cameras[id].get();
+ // writeLog(DEBUG_PRIO_WARNING, "CHDK camera: %s %s size:%u",
+ // cameras[id]->make.c_str(), cameras[id]->model.c_str(), size);
+ }
+ }
+ return cameras[id].get();
+}
+
+void CameraMetaData::disableMake(const string &make) {
+ for (const auto& cam : cameras) {
+ if (cam.second->make == make)
+ cam.second->supported = false;
+ }
+}
+
+void CameraMetaData::disableCamera(const string &make, const string &model) {
+ for (const auto& cam : cameras) {
+ if (cam.second->make == make && cam.second->model == model)
+ cam.second->supported = false;
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.h
b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.h
new file mode 100644
index 00000000..0bc73ac5
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetaData.h
@@ -0,0 +1,76 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "rawspeedconfig.h"
+#include "common/Common.h" // for uint32
+#include "metadata/Camera.h" // for Camera
+#include <map> // for map
+#include <memory> // for unique_ptr
+#include <string> // for string
+#include <tuple> // for tuple
+
+namespace rawspeed {
+
+class Camera;
+
+struct CameraId {
+ std::string make;
+ std::string model;
+ std::string mode;
+
+ bool operator<(const CameraId& rhs) const {
+ return std::tie(make, model, mode) <
+ std::tie(rhs.make, rhs.model, rhs.mode);
+ }
+};
+
+class CameraMetaData final {
+public:
+ CameraMetaData() = default;
+
+#ifdef HAVE_PUGIXML
+ explicit CameraMetaData(const char* docname);
+#endif
+
+ std::map<CameraId, std::unique_ptr<Camera>> cameras;
+ std::map<uint32,Camera*> chdkCameras;
+
+ // searches for camera with given make + model + mode
+ const Camera* getCamera(const std::string& make, const std::string& model,
+ const std::string& mode) const;
+
+ // searches for camera with given make + model, with ANY mode
+ const Camera* getCamera(const std::string& make,
+ const std::string& model) const;
+
+ bool hasCamera(const std::string& make, const std::string& model,
+ const std::string& mode) const;
+ const Camera* __attribute__((pure)) getChdkCamera(uint32 filesize) const;
+ bool __attribute__((pure)) hasChdkCamera(uint32 filesize) const;
+ void disableMake(const std::string &make);
+ void disableCamera(const std::string &make, const std::string &model);
+
+protected:
+ const Camera* addCamera(std::unique_ptr<Camera> cam);
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/CameraMetadataException.h
b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetadataException.h
new file mode 100644
index 00000000..b9b9fa8d
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/CameraMetadataException.h
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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"
+#include <string> // for string
+
+namespace rawspeed {
+
+class CameraMetadataException final : public RawspeedException {
+public:
+ explicit CameraMetadataException(const std::string& msg)
+ : RawspeedException(msg) {}
+ explicit CameraMetadataException(const char* msg) : RawspeedException(msg) {}
+};
+
+#define ThrowCME(...) \
+ ThrowExceptionHelper(rawspeed::CameraMetadataException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.cpp
b/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.cpp
new file mode 100644
index 00000000..c75ceda2
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.cpp
@@ -0,0 +1,43 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2011 Klaus Post
+
+ 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 "metadata/CameraSensorInfo.h"
+#include <utility> // for move
+#include <vector> // for vector
+
+using std::vector;
+
+namespace rawspeed {
+
+CameraSensorInfo::CameraSensorInfo(int black_level, int white_level,
+ int min_iso, int max_iso,
+ vector<int> black_separate)
+ : mBlackLevel(black_level), mWhiteLevel(white_level), mMinIso(min_iso),
+ mMaxIso(max_iso), mBlackLevelSeparate(std::move(black_separate)) {}
+
+bool __attribute__((pure)) CameraSensorInfo::isIsoWithin(int iso) const {
+ return (iso >= mMinIso && iso <= mMaxIso) || (iso >= mMinIso && 0 == mMaxIso);
+}
+
+bool __attribute__((pure)) CameraSensorInfo::isDefault() const {
+ return (0 == mMinIso && 0 == mMaxIso);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.h
b/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.h
new file mode 100644
index 00000000..c369e6b2
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/CameraSensorInfo.h
@@ -0,0 +1,40 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2011 Klaus Post
+
+ 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 <vector> // for vector
+
+namespace rawspeed {
+
+class CameraSensorInfo final {
+public:
+ CameraSensorInfo(int black_level, int white_level, int min_iso, int max_iso,
+ std::vector<int> black_separate);
+ bool __attribute__((pure)) isIsoWithin(int iso) const;
+ bool __attribute__((pure)) isDefault() const;
+ int mBlackLevel;
+ int mWhiteLevel;
+ int mMinIso;
+ int mMaxIso;
+ std::vector<int> mBlackLevelSeparate;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.cpp
b/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.cpp
new file mode 100644
index 00000000..0e9b865c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.cpp
@@ -0,0 +1,225 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "metadata/ColorFilterArray.h"
+#include "common/Common.h" // for writeLog, uint32, DEBUG_PR...
+#include "common/Point.h" // for iPoint2D, iPoint2D::value_...
+#include "decoders/RawDecoderException.h" // for ThrowRDE
+#include <algorithm> // for fill
+#include <cmath> // for abs
+#include <cstdarg> // for va_arg, va_end, va_list
+#include <cstdlib> // for size_t, abs
+#include <map> // for map
+#include <stdexcept> // for out_of_range
+#include <string> // for string
+
+using std::vector;
+using std::string;
+using std::out_of_range;
+using std::map;
+
+namespace rawspeed {
+
+ColorFilterArray::ColorFilterArray(const iPoint2D &_size) {
+ setSize(_size);
+}
+
+void ColorFilterArray::setSize(const iPoint2D& _size)
+{
+ size = _size;
+
+ if (size.area() > 36) {
+ // Bayer, FC() supports 2x8 pattern
+ // X-Trans is 6x6 pattern
+ // is there anything bigger?
+ ThrowRDE("if your CFA pattern is really %llu pixels "
+ "in area we may as well give up now",
+ size.area());
+ }
+ if (size.area() <= 0)
+ return;
+ cfa.resize(size.area());
+ fill(cfa.begin(), cfa.end(), CFA_UNKNOWN);
+}
+
+CFAColor ColorFilterArray::getColorAt( int x, int y ) const
+{
+ if (cfa.empty())
+ ThrowRDE("No CFA size set");
+
+ // calculate the positive modulo [0 .. size-1]
+ x = (x % size.x + size.x) % size.x;
+ y = (y % size.y + size.y) % size.y;
+
+ return cfa[x + static_cast<size_t>(y) * size.x];
+}
+
+void ColorFilterArray::setCFA( iPoint2D in_size, ... )
+{
+ if (in_size != size) {
+ setSize(in_size);
+ }
+ va_list arguments;
+ va_start(arguments, in_size);
+ for (auto i = 0UL; i < size.area(); i++) {
+ cfa[i] = static_cast<CFAColor>(va_arg(arguments, int));
+ }
+ va_end (arguments);
+}
+
+void ColorFilterArray::shiftLeft(int n) {
+ if (cfa.empty())
+ ThrowRDE("No CFA size set (or set to zero)");
+
+ writeLog(DEBUG_PRIO_EXTRA, "Shift left:%d", n);
+ n %= size.x;
+ if (n == 0)
+ return;
+
+ vector<CFAColor> tmp(size.area());
+ for (int y = 0; y < size.y; ++y) {
+ for (int x = 0; x < size.x; ++x) {
+ tmp[x + static_cast<size_t>(y) * size.x] = getColorAt(x + n, y);
+ }
+ }
+ cfa = tmp;
+}
+
+void ColorFilterArray::shiftDown(int n) {
+ if (cfa.empty())
+ ThrowRDE("No CFA size set (or set to zero)");
+
+ writeLog(DEBUG_PRIO_EXTRA, "Shift down:%d", n);
+ n %= size.y;
+ if (n == 0)
+ return;
+ vector<CFAColor> tmp(size.area());
+ for (int y = 0; y < size.y; ++y) {
+ for (int x = 0; x < size.x; ++x) {
+ tmp[x + static_cast<size_t>(y) * size.x] = getColorAt(x, y + n);
+ }
+ }
+ cfa = tmp;
+}
+
+string ColorFilterArray::asString() const {
+ string dst;
+ for (int y = 0; y < size.y; y++) {
+ for (int x = 0; x < size.x; x++) {
+ dst += colorToString(getColorAt(x,y));
+ dst += (x == size.x - 1) ? "\n" : ",";
+ }
+ }
+ return dst;
+}
+
+uint32 ColorFilterArray::shiftDcrawFilter(uint32 filter, int x, int y)
+{
+ // filter is a series of 4 bytes that describe a 2x8 matrix. 2 is the width,
+ // 8 is the height. each byte describes a 2x2 pixel block. so each pixel has
+ // 2 bits of information. This allows to distinguish 4 different colors.
+
+ if (std::abs(x) & 1) {
+ // A shift in x direction means swapping the first and second half of each
+ // nibble.
+ // see http://graphics.stanford.edu/~seander/bithacks.html#SwappingBitsXOR
+ for (int n = 0; n < 8; ++n) {
+ int i = n * 4;
+ int j = i + 2;
+ uint32 t = ((filter >> i) ^ (filter >> j)) & ((1U << 2) - 1);
+ filter = filter ^ ((t << i) | (t << j));
+ }
+ }
+
+ if (y == 0)
+ return filter;
+
+ // A shift in y direction means rotating the whole int by 4 bits.
+ y *= 4;
+ y = y >= 0 ? y % 32 : 32 - ((-y) % 32);
+ filter = (filter >> y) | (filter << (32 - y));
+
+ return filter;
+}
+
+const map<CFAColor, string> ColorFilterArray::color2String = {
+ {CFA_RED, "RED"}, {CFA_GREEN, "GREEN"},
+ {CFA_BLUE, "BLUE"}, {CFA_CYAN, "CYAN"},
+ {CFA_MAGENTA, "MAGENTA"}, {CFA_YELLOW, "YELLOW"},
+ {CFA_WHITE, "WHITE"}, {CFA_FUJI_GREEN, "FUJIGREEN"},
+ {CFA_UNKNOWN, "UNKNOWN"}};
+
+string ColorFilterArray::colorToString(CFAColor c)
+{
+ try {
+ return color2String.at(c);
+ } catch (std::out_of_range&) {
+ ThrowRDE("Unsupported CFA Color: %u", c);
+ }
+}
+
+void ColorFilterArray::setColorAt(iPoint2D pos, CFAColor c) {
+ if (pos.x >= size.x || pos.x < 0)
+ ThrowRDE("position out of CFA pattern");
+ if (pos.y >= size.y || pos.y < 0)
+ ThrowRDE("position out of CFA pattern");
+ cfa[pos.x + static_cast<size_t>(pos.y) * size.x] = c;
+}
+
+static uint32 toDcrawColor( CFAColor c )
+{
+ switch (c) {
+ case CFA_FUJI_GREEN:
+ case CFA_RED: return 0;
+ case CFA_MAGENTA:
+ case CFA_GREEN: return 1;
+ case CFA_CYAN:
+ case CFA_BLUE: return 2;
+ case CFA_YELLOW: return 3;
+ default:
+ throw out_of_range(ColorFilterArray::colorToString(c));
+ }
+}
+
+uint32 ColorFilterArray::getDcrawFilter() const
+{
+ //dcraw magic
+ if (size.x == 6 && size.y == 6)
+ return 9;
+
+ if (cfa.empty() || size.x > 2 || size.y > 8 || !isPowerOfTwo(size.y))
+ return 1;
+
+ uint32 ret = 0;
+ for (int x = 0; x < 2; x++) {
+ for (int y = 0; y < 8; y++) {
+ uint32 c = toDcrawColor(getColorAt(x,y));
+ int g = (x >> 1) * 8;
+ ret |= c << ((x&1)*2 + y*4 + g);
+ }
+ }
+
+ writeLog(DEBUG_PRIO_EXTRA, "%s", asString().c_str());
+ writeLog(DEBUG_PRIO_EXTRA, "DCRAW filter:%x", ret);
+
+ return ret;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.h
b/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.h
new file mode 100644
index 00000000..8b66e75f
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/ColorFilterArray.h
@@ -0,0 +1,78 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "common/Point.h" // for iPoint2D
+#include <map> // for map
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+enum CFAColor : uchar8 {
+ // see also DngDecoder
+ CFA_RED = 0,
+ CFA_GREEN = 1,
+ CFA_BLUE = 2,
+ CFA_CYAN = 3,
+ CFA_MAGENTA = 4,
+ CFA_YELLOW = 5,
+ CFA_WHITE = 6,
+ CFA_FUJI_GREEN = 7,
+ CFA_END, // keep it last!
+ CFA_UNKNOWN = 255,
+
+};
+
+class ColorFilterArray
+{
+ std::vector<CFAColor> cfa;
+ iPoint2D size;
+
+public:
+ ColorFilterArray() = default;
+ explicit ColorFilterArray(const iPoint2D& size);
+
+ void setSize(const iPoint2D& size);
+ void setColorAt(iPoint2D pos, CFAColor c);
+ void setCFA(iPoint2D size, ...);
+ void shiftLeft(int n = 1);
+ void shiftDown(int n = 1);
+
+ CFAColor getColorAt(int x, int y) const;
+ uint32 getDcrawFilter() const;
+ std::string asString() const;
+ iPoint2D getSize() const { return size; }
+
+ static std::string colorToString(CFAColor c);
+ static uint32 __attribute__((const))
+ shiftDcrawFilter(uint32 filter, int x, int y);
+
+protected:
+ static const std::map<CFAColor, std::string> color2String;
+};
+
+// FC macro from dcraw outputs, given the filters definition, the dcraw color
+// number for that given position in the CFA pattern
+// #define FC(filters,row,col) ((filters) >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/metadata/meson.build
b/subprojects/rawspeed/src/librawspeed/metadata/meson.build
new file mode 100644
index 00000000..73eb5f30
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/meson.build
@@ -0,0 +1,18 @@
+sources = files(
+ 'BlackArea.h',
+ 'Camera.cpp',
+ 'Camera.h',
+ 'CameraMetaData.cpp',
+ 'CameraMetaData.h',
+ 'CameraMetadataException.h',
+ 'CameraSensorInfo.cpp',
+ 'CameraSensorInfo.h',
+ 'ColorFilterArray.cpp',
+ 'ColorFilterArray.h',
+)
+
+librawspeed_metadata = static_library(
+ 'rawspeed-metadata',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.cpp
b/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.cpp
new file mode 100644
index 00000000..c32700b0
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.cpp
@@ -0,0 +1,81 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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
+*/
+
+#include "parsers/CiffParser.h"
+#include "common/Common.h" // for trimSpaces, uint32, ushort16
+#include "decoders/CrwDecoder.h" // for CrwDecoder
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianness::little
+#include "parsers/CiffParserException.h" // for ThrowCPE
+#include "tiff/CiffEntry.h" // for CiffEntry
+#include "tiff/CiffIFD.h" // for CiffIFD
+#include "tiff/CiffTag.h" // for CIFF_MAKEMODEL
+#include <memory> // for unique_ptr, make_unique
+#include <string> // for operator==, string
+#include <utility> // for move
+#include <vector> // for vector
+
+using std::string;
+
+namespace rawspeed {
+
+CiffParser::CiffParser(const Buffer* inputData) : RawParser(inputData) {}
+
+void CiffParser::parseData() {
+ ByteStream bs(*mInput, 0);
+ bs.setByteOrder(Endianness::little);
+
+ const ushort16 byteOrder = bs.getU16();
+ if (byteOrder != 0x4949) // "II" / little-endian
+ ThrowCPE("Not a CIFF file (endianness)");
+
+ // Offset to the beginning of the CIFF
+ const uint32 headerLength = bs.getU32();
+
+ // 8 bytes of Signature
+ if (!CrwDecoder::isCRW(mInput))
+ ThrowCPE("Not a CIFF file (ID)");
+
+ // *Everything* after the header is the root CIFF Directory
+ ByteStream CIFFRootDirectory(bs.getSubStream(headerLength));
+ mRootIFD = std::make_unique<CiffIFD>(nullptr, CIFFRootDirectory);
+}
+
+std::unique_ptr<RawDecoder> CiffParser::getDecoder(const CameraMetaData* meta) {
+ if (!mRootIFD)
+ parseData();
+
+ const auto potentials(mRootIFD->getIFDsWithTag(CIFF_MAKEMODEL));
+
+ for (const auto& potential : potentials) {
+ const auto mm = potential->getEntry(CIFF_MAKEMODEL);
+ const string make = trimSpaces(mm->getString());
+
+ if (make == "Canon")
+ return std::make_unique<CrwDecoder>(move(mRootIFD), mInput);
+ }
+
+ ThrowCPE("No decoder found. Sorry.");
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.h
b/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.h
new file mode 100644
index 00000000..83008385
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/CiffParser.h
@@ -0,0 +1,48 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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/CiffIFD.h" // for CiffIFD
+#include <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class Buffer;
+
+class RawDecoder;
+
+class CameraMetaData;
+
+class CiffParser final : public RawParser {
+ std::unique_ptr<const CiffIFD> mRootIFD;
+
+public:
+ explicit CiffParser(const Buffer* input);
+
+ void parseData();
+
+ std::unique_ptr<RawDecoder>
+ getDecoder(const CameraMetaData* meta = nullptr) override;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/CiffParserException.h
b/subprojects/rawspeed/src/librawspeed/parsers/CiffParserException.h
new file mode 100644
index 00000000..8803fb14
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/CiffParserException.h
@@ -0,0 +1,41 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 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 <string>
+
+namespace rawspeed {
+
+class CiffParserException final : public RawParserException {
+public:
+ explicit CiffParserException(const std::string& msg)
+ : RawParserException(msg) {}
+ explicit CiffParserException(const char* msg) : RawParserException(msg) {}
+};
+
+#define ThrowCPE(...) \
+ ThrowExceptionHelper(rawspeed::CiffParserException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.cpp
b/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.cpp
new file mode 100644
index 00000000..d90d3b90
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.cpp
@@ -0,0 +1,139 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+ 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
+ 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/FiffParser.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "decoders/RafDecoder.h" // for RafDecoder
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "io/Buffer.h" // for Buffer, DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for Endianness, Endianness::big
+#include "parsers/FiffParserException.h" // for ThrowFPE
+#include "parsers/RawParser.h" // for RawParser
+#include "parsers/TiffParser.h" // for TiffParser
+#include "parsers/TiffParserException.h" // for TiffParserException
+#include "tiff/TiffEntry.h" // for TiffEntry, TIFF_SHORT, TIFF...
+#include "tiff/TiffIFD.h" // for TiffIFD, TiffRootIFDOwner
+#include "tiff/TiffTag.h" // for FUJIOLDWB, FUJI_STRIPBYTECO...
+#include <limits> // for numeric_limits
+#include <memory> // for make_unique, unique_ptr
+#include <utility> // for move
+
+using std::numeric_limits;
+
+namespace rawspeed {
+
+FiffParser::FiffParser(const Buffer* inputData) : RawParser(inputData) {}
+
+void FiffParser::parseData() {
+ ByteStream bs(DataBuffer(*mInput, Endianness::big));
+ bs.skipBytes(0x54);
+
+ uint32 first_ifd = bs.getU32();
+ if (first_ifd >= numeric_limits<uint32>::max() - 12)
+ ThrowFPE("Not Fiff. First IFD too far away");
+
+ first_ifd += 12;
+
+ bs.skipBytes(4);
+ const uint32 third_ifd = bs.getU32();
+ bs.skipBytes(4);
+ const uint32 second_ifd = bs.getU32();
+
+ rootIFD = TiffParser::parse(nullptr, mInput->getSubView(first_ifd));
+ TiffIFDOwner subIFD = std::make_unique<TiffIFD>(rootIFD.get());
+
+ if (mInput->isValid(second_ifd)) {
+ // RAW Tiff on newer models, pointer to raw data on older models
+ // -> so we try parsing as Tiff first and add it as data if parsing fails
+ try {
+ rootIFD->add(
+ TiffParser::parse(rootIFD.get(), mInput->getSubView(second_ifd)));
+ } catch (TiffParserException&) {
+ // the offset will be interpreted relative to the rootIFD where this
+ // subIFD gets inserted
+
+ if (second_ifd <= first_ifd)
+ ThrowFPE("Fiff is corrupted: second IFD is not after the first IFD");
+
+ uint32 rawOffset = second_ifd - first_ifd;
+ subIFD->add(std::make_unique<TiffEntry>(
+ subIFD.get(), FUJI_STRIPOFFSETS, TIFF_OFFSET, 1,
+ ByteStream::createCopy(&rawOffset, 4)));
+ uint32 max_size = mInput->getSize() - second_ifd;
+ subIFD->add(std::make_unique<TiffEntry>(
+ subIFD.get(), FUJI_STRIPBYTECOUNTS, TIFF_LONG, 1,
+ ByteStream::createCopy(&max_size, 4)));
+ }
+ }
+
+ if (mInput->isValid(third_ifd)) {
+ // RAW information IFD on older
+
+ // This Fuji directory structure is similar to a Tiff IFD but with two
+ // differences:
+ // a) no type info and b) data is always stored in place.
+ // 4b: # of entries, for each entry: 2b tag, 2b len, xb data
+ ByteStream bytes(mInput, third_ifd, Endianness::big);
+ uint32 entries = bytes.getU32();
+
+ if (entries > 255)
+ ThrowFPE("Too many entries");
+
+ for (uint32 i = 0; i < entries; i++) {
+ ushort16 tag = bytes.getU16();
+ ushort16 length = bytes.getU16();
+ TiffDataType type = TIFF_UNDEFINED;
+
+ if (tag == IMAGEWIDTH || tag == FUJIOLDWB) // also 0x121?
+ type = TIFF_SHORT;
+
+ uint32 count = type == TIFF_SHORT ? length / 2 : length;
+ subIFD->add(std::make_unique<TiffEntry>(
+ subIFD.get(), static_cast<TiffTag>(tag), type, count,
+ bytes.getSubStream(bytes.getPosition(), length)));
+
+ bytes.skipBytes(length);
+ }
+ }
+
+ rootIFD->add(move(subIFD));
+}
+
+std::unique_ptr<RawDecoder> FiffParser::getDecoder(const CameraMetaData* meta) {
+ if (!rootIFD)
+ parseData();
+
+ // WARNING: do *NOT* fallback to ordinary TIFF parser here!
+ // All the FIFF raws are '.RAF' (Fujifilm). Do use RafDecoder directly.
+
+ try {
+ if (!RafDecoder::isAppropriateDecoder(rootIFD.get(), mInput))
+ ThrowFPE("Not a FUJIFILM RAF FIFF.");
+
+ return std::make_unique<RafDecoder>(std::move(rootIFD), mInput);
+ } catch (TiffParserException&) {
+ ThrowFPE("No decoder found. Sorry.");
+ }
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.h
b/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.h
new file mode 100644
index 00000000..bdc7e6a8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/FiffParser.h
@@ -0,0 +1,46 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 "parsers/RawParser.h" // for RawParser
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class RawDecoder;
+
+class FiffParser final : public RawParser {
+ TiffRootIFDOwner rootIFD;
+
+public:
+ explicit FiffParser(const Buffer* input);
+
+ void parseData();
+ std::unique_ptr<RawDecoder>
+ getDecoder(const CameraMetaData* meta = nullptr) override;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/FiffParserException.h
b/subprojects/rawspeed/src/librawspeed/parsers/FiffParserException.h
new file mode 100644
index 00000000..d6cb9827
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/FiffParserException.h
@@ -0,0 +1,39 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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 <string>
+
+namespace rawspeed {
+
+class FiffParserException final : public RawParserException {
+public:
+ explicit FiffParserException(const std::string& msg)
+ : RawParserException(msg) {}
+ explicit FiffParserException(const char* msg) : RawParserException(msg) {}
+};
+
+#define ThrowFPE(...) \
+ ThrowExceptionHelper(rawspeed::FiffParserException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/RawParser.cpp
b/subprojects/rawspeed/src/librawspeed/parsers/RawParser.cpp
new file mode 100644
index 00000000..151eb466
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/RawParser.cpp
@@ -0,0 +1,94 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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/RawParser.h"
+#include "decoders/MrwDecoder.h" // for MrwDecoder
+#include "decoders/NakedDecoder.h" // for NakedDecoder
+#include "decoders/RafDecoder.h" // for RafDecoder
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "decoders/RawDecoderException.h" // for RawDecoderException, ThrowRDE
+#include "io/Buffer.h" // for Buffer
+#include "metadata/CameraMetaData.h" // for CameraMetaData
+#include "parsers/CiffParser.h" // for CiffParser
+#include "parsers/CiffParserException.h" // for CiffParserException
+#include "parsers/FiffParser.h" // for FiffParser
+#include "parsers/FiffParserException.h" // for FiffParserException
+#include "parsers/TiffParser.h" // for TiffParser
+#include "parsers/TiffParserException.h" // for TiffParserException
+
+namespace rawspeed {
+
+class Camera;
+
+std::unique_ptr<RawDecoder> RawParser::getDecoder(const CameraMetaData* meta) {
+ // We need some data.
+ // For now it is 104 bytes for RAF/FUJIFIM images.
+ // FIXME: each decoder/parser should check it on their own.
+ if (mInput->getSize() <= 104)
+ ThrowRDE("File too small");
+
+ // MRW images are easy to check for, let's try that first
+ if (MrwDecoder::isMRW(mInput)) {
+ try {
+ return std::make_unique<MrwDecoder>(mInput);
+ } catch (RawDecoderException &) {
+ }
+ }
+
+ // FUJI has pointers to IFD's at fixed byte offsets
+ // So if camera is FUJI, we cannot use ordinary TIFF parser
+ if (RafDecoder::isRAF(mInput)) {
+ try {
+ FiffParser p(mInput);
+ return p.getDecoder(meta);
+ } catch (FiffParserException&) {
+ }
+ }
+
+ // Ordinary TIFF images
+ try {
+ TiffParser p(mInput);
+ return p.getDecoder(meta);
+ } catch (TiffParserException &) {
+ }
+
+ // CIFF images
+ try {
+ CiffParser p(mInput);
+ return p.getDecoder(meta);
+ } catch (CiffParserException &) {
+ }
+
+ // Detect camera on filesize (CHDK).
+ if (meta != nullptr && meta->hasChdkCamera(mInput->getSize())) {
+ const Camera* c = meta->getChdkCamera(mInput->getSize());
+
+ try {
+ return std::make_unique<NakedDecoder>(mInput, c);
+ } catch (RawDecoderException &) {
+ }
+ }
+
+ // File could not be decoded, so no further options for now.
+ ThrowRDE("No decoder found. Sorry.");
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/RawParser.h
b/subprojects/rawspeed/src/librawspeed/parsers/RawParser.h
new file mode 100644
index 00000000..6580e22c
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/RawParser.h
@@ -0,0 +1,45 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 <memory> // for unique_ptr
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class RawDecoder;
+
+class RawParser {
+public:
+ explicit RawParser(const Buffer* inputData) : mInput(inputData) {}
+ virtual ~RawParser() = default;
+
+ virtual std::unique_ptr<RawDecoder>
+ getDecoder(const CameraMetaData* meta = nullptr);
+
+protected:
+ const Buffer* mInput;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/RawParserException.h
b/subprojects/rawspeed/src/librawspeed/parsers/RawParserException.h
new file mode 100644
index 00000000..c93a3f69
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/RawParserException.h
@@ -0,0 +1,38 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2017 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"
+#include <string>
+
+namespace rawspeed {
+
+class RawParserException : public RawspeedException {
+public:
+ explicit RawParserException(const std::string& msg)
+ : RawspeedException(msg) {}
+ explicit RawParserException(const char* msg) : RawspeedException(msg) {}
+};
+
+#define ThrowRPE(...) \
+ ThrowExceptionHelper(rawspeed::RawParserException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.cpp
b/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.cpp
new file mode 100644
index 00000000..a74bbee4
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.cpp
@@ -0,0 +1,146 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ Copyright (C) 2017 Axel Waggershauser
+ Copyright (C) 2017 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
+*/
+
+#include "parsers/TiffParser.h"
+#include "common/Common.h" // for uint32, ushort16
+#include "common/NORangesSet.h" // for set
+#include "decoders/ArwDecoder.h" // for ArwDecoder
+#include "decoders/Cr2Decoder.h" // for Cr2Decoder
+#include "decoders/DcrDecoder.h" // for DcrDecoder
+#include "decoders/DcsDecoder.h" // for DcsDecoder
+#include "decoders/DngDecoder.h" // for DngDecoder
+#include "decoders/ErfDecoder.h" // for ErfDecoder
+#include "decoders/IiqDecoder.h" // for IiqDecoder
+#include "decoders/KdcDecoder.h" // for KdcDecoder
+#include "decoders/MefDecoder.h" // for MefDecoder
+#include "decoders/MosDecoder.h" // for MosDecoder
+#include "decoders/NefDecoder.h" // for NefDecoder
+#include "decoders/OrfDecoder.h" // for OrfDecoder
+#include "decoders/PefDecoder.h" // for PefDecoder
+#include "decoders/RawDecoder.h" // for RawDecoder
+#include "decoders/Rw2Decoder.h" // for Rw2Decoder
+#include "decoders/SrwDecoder.h" // for SrwDecoder
+#include "decoders/ThreefrDecoder.h" // for ThreefrDecoder
+#include "io/ByteStream.h" // for ByteStream
+#include "parsers/TiffParserException.h" // for ThrowTPE
+#include <cassert> // for assert
+#include <cstdint> // for UINT32_MAX
+#include <memory> // for make_unique, unique_ptr
+#include <string> // for string
+#include <tuple> // for tie, tuple
+#include <vector> // for vector
+// IWYU pragma: no_include <ext/alloc_traits.h>
+
+using std::string;
+
+namespace rawspeed {
+
+TiffParser::TiffParser(const Buffer* file) : RawParser(file) {}
+
+std::unique_ptr<RawDecoder> TiffParser::getDecoder(const CameraMetaData* meta) {
+ return TiffParser::makeDecoder(TiffParser::parse(nullptr, *mInput), *mInput);
+}
+
+TiffRootIFDOwner TiffParser::parse(TiffIFD* parent, const Buffer& data) {
+ ByteStream bs(data, 0);
+ bs.setByteOrder(getTiffByteOrder(bs, 0, "TIFF header"));
+ bs.skipBytes(2);
+
+ ushort16 magic = bs.getU16();
+ if (magic != 42 && magic != 0x4f52 && magic != 0x5352 && magic != 0x55) // ORF has 0x4f52/0x5352, RW2 0x55
- Brillant!
+ ThrowTPE("Not a TIFF file (magic 42)");
+
+ TiffRootIFDOwner root = std::make_unique<TiffRootIFD>(
+ parent, nullptr, bs,
+ UINT32_MAX); // tell TiffIFD constructur not to parse bs as IFD
+
+ NORangesSet<Buffer> ifds;
+
+ for (uint32 IFDOffset = bs.getU32(); IFDOffset;
+ IFDOffset = root->getSubIFDs().back()->getNextIFD()) {
+ root->add(std::make_unique<TiffIFD>(root.get(), &ifds, bs, IFDOffset));
+ }
+
+ return root;
+}
+
+std::unique_ptr<RawDecoder> TiffParser::makeDecoder(TiffRootIFDOwner root,
+ const Buffer& data) {
+ const Buffer* mInput = &data;
+ if (!root)
+ ThrowTPE("TiffIFD is null.");
+
+ for (const auto& decoder : Map) {
+ checker_t dChecker = nullptr;
+ constructor_t dConstructor = nullptr;
+
+ std::tie(dChecker, dConstructor) = decoder;
+
+ assert(dChecker);
+ assert(dConstructor);
+
+ if (!dChecker(root.get(), mInput))
+ continue;
+
+ return dConstructor(move(root), mInput);
+ }
+
+ ThrowTPE("No decoder found. Sorry.");
+}
+
+template <class Decoder>
+std::unique_ptr<RawDecoder> TiffParser::constructor(TiffRootIFDOwner&& root,
+ const Buffer* data) {
+ return std::make_unique<Decoder>(std::move(root), data);
+}
+
+#define DECODER(name) \
+ { \
+ std::make_pair( \
+ static_cast<TiffParser::checker_t>(&name::isAppropriateDecoder), \
+ &constructor<name>) \
+ }
+
+const std::array<std::pair<TiffParser::checker_t, TiffParser::constructor_t>,
+ 16>
+ TiffParser::Map = {{
+ DECODER(DngDecoder),
+ DECODER(MosDecoder),
+ DECODER(IiqDecoder),
+ DECODER(Cr2Decoder),
+ DECODER(NefDecoder),
+ DECODER(OrfDecoder),
+ DECODER(ArwDecoder),
+ DECODER(PefDecoder),
+ DECODER(Rw2Decoder),
+ DECODER(SrwDecoder),
+ DECODER(MefDecoder),
+ DECODER(DcrDecoder),
+ DECODER(DcsDecoder),
+ DECODER(KdcDecoder),
+ DECODER(ErfDecoder),
+ DECODER(ThreefrDecoder),
+
+ }};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.h
b/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.h
new file mode 100644
index 00000000..24672ac8
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/TiffParser.h
@@ -0,0 +1,60 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+
+ 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 "decoders/RawDecoder.h" // IWYU pragma: keep
+#include "parsers/RawParser.h" // for RawParser
+#include "tiff/TiffIFD.h" // for TiffRootIFDOwner
+#include <array> // for array
+#include <memory> // for unique_ptr
+#include <utility> // for pair
+
+namespace rawspeed {
+
+class Buffer;
+
+class CameraMetaData;
+
+class TiffParser final : public RawParser {
+public:
+ explicit TiffParser(const Buffer* file);
+
+ std::unique_ptr<RawDecoder>
+ getDecoder(const CameraMetaData* meta = nullptr) override;
+
+ // TiffRootIFDOwner contains pointers into 'data' but if is is non-owning, it
+ // may be deleted immediately
+ static TiffRootIFDOwner parse(TiffIFD* parent, const Buffer& data);
+
+ // transfers ownership of TiffIFD into RawDecoder
+ static std::unique_ptr<RawDecoder> makeDecoder(TiffRootIFDOwner root,
+ const Buffer& data);
+
+ template <class Decoder>
+ static std::unique_ptr<RawDecoder> constructor(TiffRootIFDOwner&& root,
+ const Buffer* data);
+ using checker_t = bool (*)(const TiffRootIFD* root, const Buffer* data);
+ using constructor_t = std::unique_ptr<RawDecoder> (*)(TiffRootIFDOwner&& root,
+ const Buffer* data);
+ static const std::array<std::pair<checker_t, constructor_t>, 16> Map;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/TiffParserException.h
b/subprojects/rawspeed/src/librawspeed/parsers/TiffParserException.h
new file mode 100644
index 00000000..6cb30fba
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/TiffParserException.h
@@ -0,0 +1,40 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2017 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 <string>
+
+namespace rawspeed {
+
+class TiffParserException final : public RawParserException {
+public:
+ explicit TiffParserException(const std::string& msg)
+ : RawParserException(msg) {}
+ explicit TiffParserException(const char* msg) : RawParserException(msg) {}
+};
+
+#define ThrowTPE(...) \
+ ThrowExceptionHelper(rawspeed::TiffParserException, __VA_ARGS__)
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/parsers/meson.build
b/subprojects/rawspeed/src/librawspeed/parsers/meson.build
new file mode 100644
index 00000000..d4324154
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/parsers/meson.build
@@ -0,0 +1,20 @@
+sources = files(
+ 'CiffParser.cpp',
+ 'CiffParser.h',
+ 'CiffParserException.h',
+ 'FiffParser.cpp',
+ 'FiffParser.h',
+ 'FiffParserException.h',
+ 'RawParser.cpp',
+ 'RawParser.h',
+ 'RawParserException.h',
+ 'TiffParser.cpp',
+ 'TiffParser.h',
+ 'TiffParserException.h',
+)
+
+librawspeed_parsers = static_library(
+ 'rawspeed-parsers',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.cpp
b/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.cpp
new file mode 100644
index 00000000..329b7115
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.cpp
@@ -0,0 +1,172 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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
+ 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/CiffEntry.h"
+#include "common/Common.h" // for uint32, uchar8, ushort16
+#include "common/NORangesSet.h" // for set
+#include "io/Buffer.h" // for Buffer
+#include "io/ByteStream.h" // for ByteStream
+#include "parsers/CiffParserException.h" // for ThrowCPE
+#include <string> // for string
+#include <utility> // for pair
+#include <vector> // for vector
+
+using std::string;
+using std::vector;
+
+namespace rawspeed {
+
+CiffEntry::CiffEntry(NORangesSet<Buffer>* valueDatas,
+ const ByteStream* valueData, ByteStream dirEntry) {
+ ushort16 p = dirEntry.getU16();
+
+ tag = static_cast<CiffTag>(p & 0x3fff);
+ ushort16 datalocation = (p & 0xc000);
+ type = static_cast<CiffDataType>(p & 0x3800);
+
+ uint32 data_offset;
+ uint32 bytesize;
+
+ switch (datalocation) {
+ case 0x0000:
+ // Data is offset in value_data
+ bytesize = dirEntry.getU32();
+ data_offset = dirEntry.getU32();
+ data = valueData->getSubStream(data_offset, bytesize);
+ if (!valueDatas->emplace(data).second)
+ ThrowCPE("Two valueData's overlap. Raw corrupt!");
+ break;
+ case 0x4000:
+ // Data is stored directly in entry
+ data_offset = dirEntry.getPosition();
+ // Maximum of 8 bytes of data (the size and offset fields)
+ bytesize = 8;
+ data = dirEntry.getStream(bytesize);
+ break;
+ default:
+ ThrowCPE("Don't understand data location 0x%x", datalocation);
+ }
+
+ // Set the number of items using the shift
+ count = bytesize >> getElementShift();
+}
+
+uint32 __attribute__((pure)) CiffEntry::getElementShift() const {
+ switch (type) {
+ case CIFF_SHORT:
+ return 1;
+ case CIFF_LONG:
+ case CIFF_MIX:
+ case CIFF_SUB1:
+ case CIFF_SUB2:
+ return 2;
+ default:
+ // e.g. CIFF_BYTE or CIFF_ASCII
+ return 0;
+ }
+}
+
+uint32 __attribute__((pure)) CiffEntry::getElementSize() const {
+ switch (type) {
+ case CIFF_BYTE:
+ case CIFF_ASCII:
+ return 1;
+ case CIFF_SHORT:
+ return 2;
+ case CIFF_LONG:
+ case CIFF_MIX:
+ case CIFF_SUB1:
+ case CIFF_SUB2:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+bool __attribute__((pure)) CiffEntry::isInt() const {
+ return (type == CIFF_LONG || type == CIFF_SHORT || type == CIFF_BYTE);
+}
+
+uint32 CiffEntry::getU32(uint32 num) const {
+ if (!isInt()) {
+ ThrowCPE(
+ "Wrong type 0x%x encountered. Expected Long, Short or Byte at 0x%x",
+ type, tag);
+ }
+
+ if (type == CIFF_BYTE)
+ return getByte(num);
+ if (type == CIFF_SHORT)
+ return getU16(num);
+
+ return data.peek<uint32>(num);
+}
+
+ushort16 CiffEntry::getU16(uint32 num) const {
+ if (type != CIFF_SHORT && type != CIFF_BYTE)
+ ThrowCPE("Wrong type 0x%x encountered. Expected Short at 0x%x", type, tag);
+
+ return data.peek<ushort16>(num);
+}
+
+uchar8 CiffEntry::getByte(uint32 num) const {
+ if (type != CIFF_BYTE)
+ ThrowCPE("Wrong type 0x%x encountered. Expected Byte at 0x%x", type, tag);
+
+ return data.peek<uchar8>(num);
+}
+
+string CiffEntry::getString() const {
+ if (type != CIFF_ASCII)
+ ThrowCPE("Wrong type 0x%x encountered. Expected Ascii", type);
+
+ if (count == 0)
+ return string("");
+
+ return data.peekString();
+}
+
+vector<string> CiffEntry::getStrings() const {
+ if (type != CIFF_ASCII)
+ ThrowCPE("Wrong type 0x%x encountered. Expected Ascii", type);
+
+ const string str(reinterpret_cast<const char*>(data.peekData(count)), count);
+
+ vector<string> strs;
+
+ uint32 start = 0;
+ for (uint32 i = 0; i < count; i++) {
+ if (str[i] != 0)
+ continue;
+
+ strs.emplace_back(reinterpret_cast<const char*>(&str[start]));
+ start = i + 1;
+ }
+
+ return strs;
+}
+
+bool __attribute__((pure)) CiffEntry::isString() const {
+ return (type == CIFF_ASCII);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.h
b/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.h
new file mode 100644
index 00000000..2e087321
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/CiffEntry.h
@@ -0,0 +1,82 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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/Common.h" // for uint32, uchar8, ushort16
+#include "common/NORangesSet.h" // for set
+#include "io/ByteStream.h" // for ByteStream
+#include "tiff/CiffTag.h" // for CiffTag
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class Buffer;
+class CiffIFD; // IWYU pragma: keep
+
+/*
+ * Tag data type information.
+ */
+enum CiffDataType {
+ CIFF_BYTE = 0x0000, /* 8-bit unsigned integer */
+ CIFF_ASCII = 0x0800, /* 8-bit bytes w/ last byte null */
+ CIFF_SHORT = 0x1000, /* 16-bit unsigned integer */
+ CIFF_LONG = 0x1800, /* 32-bit unsigned integer */
+ CIFF_MIX = 0x2000, /* 32-bit unsigned integer */
+ CIFF_SUB1 = 0x2800, /* 32-bit unsigned integer */
+ CIFF_SUB2 = 0x3000, /* 32-bit unsigned integer */
+
+};
+
+class CiffEntry
+{
+ friend class CiffIFD;
+
+ ByteStream data;
+
+public:
+ explicit CiffEntry(NORangesSet<Buffer>* valueDatas,
+ const ByteStream* valueData, ByteStream dirEntry);
+
+ const ByteStream& getData() const { return data; }
+
+ uchar8 getByte(uint32 num = 0) const;
+ uint32 getU32(uint32 num = 0) const;
+ ushort16 getU16(uint32 num = 0) const;
+
+ std::string getString() const;
+ std::vector<std::string> getStrings() const;
+
+ uint32 __attribute__((pure)) getElementSize() const;
+ uint32 __attribute__((pure)) getElementShift() const;
+
+ // variables:
+ CiffTag tag;
+ CiffDataType type;
+ uint32 count;
+
+ bool __attribute__((pure)) isInt() const;
+ bool __attribute__((pure)) isString() const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.cpp
b/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.cpp
new file mode 100644
index 00000000..f39436fe
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.cpp
@@ -0,0 +1,286 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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
+ 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/CiffIFD.h"
+#include "common/Common.h" // for isIn, uint32, ushort16
+#include "common/NORangesSet.h" // for set
+#include "io/ByteStream.h" // for ByteStream
+#include "parsers/CiffParserException.h" // for ThrowCPE
+#include <cassert> // for assert
+#include <initializer_list> // for initializer_list
+#include <map> // for map, _Rb_tree_const_iterator
+#include <memory> // for unique_ptr, make_unique
+#include <string> // for operator==, string
+#include <utility> // for move, pair
+#include <vector> // for vector, vector<>::size_type
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+
+namespace rawspeed {
+
+class Buffer;
+
+void CiffIFD::parseIFDEntry(NORangesSet<Buffer>* valueDatas,
+ const ByteStream* valueData,
+ ByteStream* dirEntries) {
+ assert(valueDatas);
+ assert(valueData);
+ assert(dirEntries);
+
+ ByteStream dirEntry = dirEntries->getStream(10); // Entry is 10 bytes.
+
+ auto t = std::make_unique<CiffEntry>(valueDatas, valueData, dirEntry);
+
+ switch (t->type) {
+ case CIFF_SUB1:
+ case CIFF_SUB2: {
+ add(std::make_unique<CiffIFD>(this, t->data));
+ break;
+ }
+
+ default:
+ // Will we ever look for this entry?
+ if (!isIn(t->tag, CiffTagsWeCareAbout))
+ return;
+ add(move(t));
+ }
+}
+
+CiffIFD::CiffIFD(CiffIFD* const parent_) : parent(parent_) {
+ recursivelyCheckSubIFDs(1);
+ // If we are good (can add this IFD without violating the limits),
+ // we are still here. However, due to the way we add parsed sub-IFD's (lazy),
+ // we need to count this IFD right *NOW*, not when adding it at the end.
+ recursivelyIncrementSubIFDCount();
+}
+
+CiffIFD::CiffIFD(CiffIFD* const parent_, ByteStream directory)
+ : CiffIFD(parent_) {
+ if (directory.getSize() < 4)
+ ThrowCPE("CIFF directory is too short.");
+
+ directory.setPosition(directory.getSize() - 4);
+ const uint32 valueDataSize = directory.getU32();
+
+ // The Recursion. Directory entries store data here. May contain IFDs.
+ directory.setPosition(0);
+ const ByteStream valueData(directory.getStream(valueDataSize));
+
+ // count of the Directory entries in this IFD
+ const ushort16 entryCount = directory.getU16();
+
+ // each entry is 10 bytes
+ ByteStream dirEntries(directory.getStream(entryCount, 10));
+
+ // IFDData might still contain OtherData until the valueDataSize at the end.
+ // But we do not care about that.
+
+ // Each IFD has it's own valueData area.
+ // In that area, no two entries may overlap.
+ NORangesSet<Buffer> valueDatas;
+
+ for (uint32 i = 0; i < entryCount; i++)
+ parseIFDEntry(&valueDatas, &valueData, &dirEntries);
+
+ assert(valueDatas.size() <= entryCount);
+ assert(mEntry.size() <= CiffTagsWeCareAbout.size());
+ assert(mSubIFD.size() == decltype(mSubIFD)::size_type(subIFDCount));
+ assert(subIFDCount <= subIFDCountRecursive);
+ assert(mEntry.size() + mSubIFD.size() <= entryCount);
+}
+
+void CiffIFD::recursivelyIncrementSubIFDCount() {
+ CiffIFD* p = this->parent;
+ if (!p)
+ return;
+
+ p->subIFDCount++;
+
+ for (; p != nullptr; p = p->parent)
+ p->subIFDCountRecursive++;
+}
+
+void CiffIFD::checkSubIFDs(int headroom) const {
+ int count = headroom + subIFDCount;
+ if (!headroom)
+ assert(count <= CiffIFD::Limits::SubIFDCount);
+ else if (count > CiffIFD::Limits::SubIFDCount)
+ ThrowCPE("TIFF IFD has %u SubIFDs", count);
+
+ count = headroom + subIFDCountRecursive;
+ if (!headroom)
+ assert(count <= CiffIFD::Limits::RecursiveSubIFDCount);
+ else if (count > CiffIFD::Limits::RecursiveSubIFDCount)
+ ThrowCPE("TIFF IFD file has %u SubIFDs (recursively)", count);
+}
+
+void CiffIFD::recursivelyCheckSubIFDs(int headroom) const {
+ int depth = 0;
+ for (const CiffIFD* p = this; p != nullptr;) {
+ if (!headroom)
+ assert(depth <= CiffIFD::Limits::Depth);
+ else if (depth > CiffIFD::Limits::Depth)
+ ThrowCPE("CiffIFD cascading overflow, found %u level IFD", depth);
+
+ p->checkSubIFDs(headroom);
+
+ // And step up
+ p = p->parent;
+ depth++;
+ }
+}
+
+void CiffIFD::add(std::unique_ptr<CiffIFD> subIFD) {
+ assert(subIFD->parent == this);
+
+ // We are good, and actually can add this sub-IFD, right?
+ subIFD->recursivelyCheckSubIFDs(0);
+
+ mSubIFD.push_back(move(subIFD));
+}
+
+void CiffIFD::add(std::unique_ptr<CiffEntry> entry) {
+ assert(isIn(entry->tag, CiffTagsWeCareAbout));
+ mEntry[entry->tag] = move(entry);
+ assert(mEntry.size() <= CiffTagsWeCareAbout.size());
+}
+
+template <typename Lambda>
+std::vector<const CiffIFD*> CiffIFD::getIFDsWithTagIf(CiffTag tag,
+ const Lambda& f) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+
+ std::vector<const CiffIFD*> matchingIFDs;
+
+ const auto found = mEntry.find(tag);
+ if (found != mEntry.end()) {
+ const auto entry = found->second.get();
+ if (f(entry))
+ matchingIFDs.push_back(this);
+ }
+
+ for (const auto& i : mSubIFD) {
+ const auto t = i->getIFDsWithTagIf(tag, f);
+ matchingIFDs.insert(matchingIFDs.end(), t.begin(), t.end());
+ }
+
+ return matchingIFDs;
+}
+
+template <typename Lambda>
+const CiffEntry* CiffIFD::getEntryRecursiveIf(CiffTag tag,
+ const Lambda& f) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+
+ const auto found = mEntry.find(tag);
+ if (found != mEntry.end()) {
+ const auto entry = found->second.get();
+ if (f(entry))
+ return entry;
+ }
+
+ for (const auto& i : mSubIFD) {
+ const CiffEntry* entry = i->getEntryRecursiveIf(tag, f);
+ if (entry)
+ return entry;
+ }
+
+ return nullptr;
+}
+
+vector<const CiffIFD*> CiffIFD::getIFDsWithTag(CiffTag tag) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getIFDsWithTagIf(tag,
+ [](const CiffEntry* /*unused*/) { return true; });
+}
+
+vector<const CiffIFD*> CiffIFD::getIFDsWithTagWhere(CiffTag tag,
+ uint32 isValue) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getIFDsWithTagIf(tag, [&isValue](const CiffEntry* entry) {
+ return entry->isInt() && entry->getU32() == isValue;
+ });
+}
+
+vector<const CiffIFD*>
+CiffIFD::getIFDsWithTagWhere(CiffTag tag, const string& isValue) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getIFDsWithTagIf(tag, [&isValue](const CiffEntry* entry) {
+ return entry->isString() && isValue == entry->getString();
+ });
+}
+
+bool __attribute__((pure)) CiffIFD::hasEntry(CiffTag tag) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+
+ return mEntry.count(tag) > 0;
+}
+
+bool __attribute__((pure)) CiffIFD::hasEntryRecursive(CiffTag tag) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+
+ if (mEntry.count(tag) > 0)
+ return true;
+
+ for (const auto& i : mSubIFD) {
+ if (i->hasEntryRecursive(tag))
+ return true;
+ }
+
+ return false;
+}
+
+const CiffEntry* CiffIFD::getEntry(CiffTag tag) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+
+ const auto found = mEntry.find(tag);
+ if (found != mEntry.end())
+ return found->second.get();
+
+ ThrowCPE("Entry 0x%x not found.", tag);
+}
+
+const CiffEntry* CiffIFD::getEntryRecursive(CiffTag tag) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getEntryRecursiveIf(tag,
+ [](const CiffEntry* /*unused*/) { return true; });
+}
+
+const CiffEntry* CiffIFD::getEntryRecursiveWhere(CiffTag tag,
+ uint32 isValue) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getEntryRecursiveIf(tag, [&isValue](const CiffEntry* entry) {
+ return entry->isInt() && entry->getU32() == isValue;
+ });
+}
+
+const CiffEntry* CiffIFD::getEntryRecursiveWhere(CiffTag tag,
+ const string& isValue) const {
+ assert(isIn(tag, CiffTagsWeCareAbout));
+ return getEntryRecursiveIf(tag, [&isValue](const CiffEntry* entry) {
+ return entry->isString() && isValue == entry->getString();
+ });
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.h
b/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.h
new file mode 100644
index 00000000..53183532
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/CiffIFD.h
@@ -0,0 +1,110 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+ 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/Common.h" // for uint32
+#include "common/NORangesSet.h" // for set
+#include "tiff/CiffEntry.h" // IWYU pragma: keep
+#include "tiff/CiffTag.h" // for CiffTag
+#include <map> // for map
+#include <memory> // for unique_ptr
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class Buffer;
+class ByteStream;
+
+class CiffIFD final {
+ CiffIFD* const parent;
+
+ std::vector<std::unique_ptr<const CiffIFD>> mSubIFD;
+ std::map<CiffTag, std::unique_ptr<const CiffEntry>> mEntry;
+
+ int subIFDCount = 0;
+ int subIFDCountRecursive = 0;
+
+ void recursivelyIncrementSubIFDCount();
+ void checkSubIFDs(int headroom) const;
+ void recursivelyCheckSubIFDs(int headroom) const;
+
+ // CIFF IFD are tree-like structure, with branches.
+ // A branch (IFD) can have branches (IFDs) of it's own.
+ // We must be careful to weed-out all the degenerative cases that
+ // can be produced e.g. via fuzzing, or other means.
+ struct Limits final {
+ // How many layers of IFD's can there be?
+ // All RPU samples (as of 2018-02-13) are ok with 3.
+ // However, let's be on the safe side, and pad it by one.
+ static constexpr int Depth = 3 + 1;
+
+ // How many sub-IFD's can this IFD have?
+ // NOTE: only for the given IFD, *NOT* recursively including all sub-IFD's!
+ // All RPU samples (as of 2018-02-13) are ok with 4.
+ // However, let's be on the safe side, and double it.
+ static constexpr int SubIFDCount = 4 * 2;
+
+ // How many sub-IFD's can this IFD have, recursively?
+ // All RPU samples (as of 2018-02-13) are ok with 6.
+ // However, let's be on the safe side, and double it.
+ static constexpr int RecursiveSubIFDCount = 6 * 2;
+ };
+
+ void add(std::unique_ptr<CiffIFD> subIFD);
+ void add(std::unique_ptr<CiffEntry> entry);
+
+ void parseIFDEntry(NORangesSet<Buffer>* valueDatas,
+ const ByteStream* valueData, ByteStream* dirEntries);
+
+ template <typename Lambda>
+ std::vector<const CiffIFD*> __attribute__((pure))
+ getIFDsWithTagIf(CiffTag tag, const Lambda& f) const;
+
+ template <typename Lambda>
+ const CiffEntry* __attribute__((pure))
+ getEntryRecursiveIf(CiffTag tag, const Lambda& f) const;
+
+public:
+ explicit CiffIFD(CiffIFD* parent);
+ CiffIFD(CiffIFD* parent, ByteStream directory);
+
+ std::vector<const CiffIFD*> __attribute__((pure))
+ getIFDsWithTag(CiffTag tag) const;
+ std::vector<const CiffIFD*> __attribute__((pure))
+ getIFDsWithTagWhere(CiffTag tag, uint32 isValue) const;
+ std::vector<const CiffIFD*> __attribute__((pure))
+ getIFDsWithTagWhere(CiffTag tag, const std::string& isValue) const;
+
+ bool __attribute__((pure)) hasEntry(CiffTag tag) const;
+ bool __attribute__((pure)) hasEntryRecursive(CiffTag tag) const;
+
+ const CiffEntry* __attribute__((pure)) getEntry(CiffTag tag) const;
+ const CiffEntry* __attribute__((pure)) getEntryRecursive(CiffTag tag) const;
+ const CiffEntry* __attribute__((pure))
+ getEntryRecursiveWhere(CiffTag tag, uint32 isValue) const;
+ const CiffEntry* __attribute__((pure))
+ getEntryRecursiveWhere(CiffTag tag, const std::string& isValue) const;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/CiffTag.h
b/subprojects/rawspeed/src/librawspeed/tiff/CiffTag.h
new file mode 100644
index 00000000..8a422915
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/CiffTag.h
@@ -0,0 +1,52 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2014 Pedro Côrte-Real
+
+ 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 <initializer_list> // for initializer_list
+
+namespace rawspeed {
+
+enum CiffTag {
+ CIFF_NULL = 0x0000,
+ CIFF_MAKEMODEL = 0x080a,
+ CIFF_SHOTINFO = 0x102a,
+ CIFF_WHITEBALANCE = 0x10a9,
+ CIFF_SENSORINFO = 0x1031,
+ CIFF_IMAGEINFO = 0x1810,
+ CIFF_DECODERTABLE = 0x1835,
+ CIFF_RAWDATA = 0x2005,
+ CIFF_SUBIFD = 0x300a,
+ CIFF_EXIF = 0x300b,
+};
+
+static constexpr std::initializer_list<CiffTag> CiffTagsWeCareAbout = {
+ CIFF_DECODERTABLE,
+ CIFF_MAKEMODEL,
+ CIFF_RAWDATA,
+ CIFF_SENSORINFO,
+ CIFF_SHOTINFO,
+ CIFF_WHITEBALANCE,
+ static_cast<CiffTag>(0x0032), // ???
+ static_cast<CiffTag>(0x102c), // ???
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.cpp
b/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.cpp
new file mode 100644
index 00000000..9c31f826
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.cpp
@@ -0,0 +1,238 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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/TiffEntry.h"
+#include "common/Common.h" // for uint32, short16, ushort16
+#include "parsers/TiffParserException.h" // for ThrowTPE
+#include "tiff/TiffIFD.h" // for TiffIFD, TiffRootIFD
+#include "tiff/TiffTag.h" // for TiffTag, DNGPRIVATEDATA
+#include <cassert> // for assert
+#include <cstdint> // for UINT32_MAX
+#include <cstring> // for strnlen
+#include <initializer_list> // for initializer_list
+#include <string> // for string
+#include <utility> // for move
+
+using std::string;
+
+namespace rawspeed {
+
+class DataBuffer;
+
+// order see TiffDataType
+const std::array<uint32, 14> TiffEntry::datashifts = {0, 0, 0, 1, 2, 3, 0,
+ 0, 1, 2, 3, 2, 3, 2};
+// 0-1-2-3-4-5-6-7-8-9-10-11-12-13
+
+TiffEntry::TiffEntry(TiffIFD* parent_, ByteStream* bs) : parent(parent_) {
+ tag = static_cast<TiffTag>(bs->getU16());
+ const ushort16 numType = bs->getU16();
+ if (numType > TIFF_OFFSET)
+ ThrowTPE("Error reading TIFF structure. Unknown Type 0x%x encountered.", numType);
+ type = static_cast<TiffDataType>(numType);
+ count = bs->getU32();
+
+ // check for count << datashift overflow
+ if (count > UINT32_MAX >> datashifts[type])
+ ThrowTPE("integer overflow in size calculation.");
+
+ uint32 byte_size = count << datashifts[type];
+ uint32 data_offset = UINT32_MAX;
+
+ if (byte_size <= 4) {
+ data_offset = bs->getPosition();
+ data = bs->getSubStream(bs->getPosition(), byte_size);
+ bs->skipBytes(4);
+ } else {
+ data_offset = bs->getU32();
+ if (type == TIFF_OFFSET || isIn(tag, {DNGPRIVATEDATA, MAKERNOTE, MAKERNOTE_ALT, FUJI_RAW_IFD, SUBIFDS,
EXIFIFDPOINTER})) {
+ // preserve offset for SUB_IFD/EXIF/MAKER_NOTE data
+#if 0
+ // limit access to range from 0 to data_offset+byte_size
+ data = ByteStream(bs, data_offset, byte_size, bs.getByteOrder());
+#else
+ // allow access to whole file, necesary if offsets inside the maker note
+ // point to outside data, which is forbidden due to the TIFF/DNG spec but
+ // may happen none the less (see e.g. "old" ORF files like EX-1, note:
+ // the tags outside of the maker note area are currently not used anyway)
+ data = *bs;
+ data.setPosition(data_offset);
+ data.check(byte_size);
+#endif
+ } else {
+ data = bs->getSubStream(data_offset, byte_size);
+ }
+ }
+}
+
+TiffEntry::TiffEntry(TiffIFD* parent_, TiffTag tag_, TiffDataType type_,
+ uint32 count_, ByteStream&& data_)
+ : parent(parent_), data(std::move(data_)), tag(tag_), type(type_),
+ count(count_) {
+ // check for count << datashift overflow
+ if (count > UINT32_MAX >> datashifts[type])
+ ThrowTPE("integer overflow in size calculation.");
+
+ uint32 bytesize = count << datashifts[type];
+
+ if (data.getSize() != bytesize)
+ ThrowTPE("data set larger than entry size given");
+}
+
+bool __attribute__((pure)) TiffEntry::isInt() const {
+ return type == TIFF_LONG || type == TIFF_SHORT || type == TIFF_BYTE;
+}
+
+bool __attribute__((pure)) TiffEntry::isString() const {
+ return type == TIFF_ASCII;
+}
+
+bool __attribute__((pure)) TiffEntry::isFloat() const {
+ switch (type) {
+ case TIFF_FLOAT:
+ case TIFF_DOUBLE:
+ case TIFF_RATIONAL:
+ case TIFF_SRATIONAL:
+ case TIFF_LONG:
+ case TIFF_SLONG:
+ case TIFF_SHORT:
+ case TIFF_SSHORT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uchar8 TiffEntry::getByte(uint32 index) const {
+ if (type != TIFF_BYTE && type != TIFF_UNDEFINED)
+ ThrowTPE("Wrong type %u encountered. Expected Byte on 0x%x", type, tag);
+
+ return data.peekByte(index);
+}
+
+ushort16 TiffEntry::getU16(uint32 index) const {
+ if (type != TIFF_SHORT && type != TIFF_UNDEFINED)
+ ThrowTPE("Wrong type %u encountered. Expected Short or Undefined on 0x%x",
+ type, tag);
+
+ return data.peek<ushort16>(index);
+}
+
+short16 TiffEntry::getI16(uint32 index) const {
+ if (type != TIFF_SSHORT && type != TIFF_UNDEFINED)
+ ThrowTPE("Wrong type %u encountered. Expected Short or Undefined on 0x%x",
+ type, tag);
+
+ return data.peek<short16>(index);
+}
+
+uint32 TiffEntry::getU32(uint32 index) const {
+ if (type == TIFF_SHORT)
+ return getU16(index);
+
+ switch (type) {
+ case TIFF_LONG:
+ case TIFF_OFFSET:
+ case TIFF_BYTE:
+ case TIFF_UNDEFINED:
+ case TIFF_RATIONAL:
+ case TIFF_SRATIONAL:
+ break;
+ default:
+ ThrowTPE("Wrong type %u encountered. Expected Long, Offset, Rational or "
+ "Undefined on 0x%x",
+ type, tag);
+ }
+
+ return data.peek<uint32>(index);
+}
+
+int32 TiffEntry::getI32(uint32 index) const {
+ if (type == TIFF_SSHORT)
+ return getI16(index);
+ if (!(type == TIFF_SLONG || type == TIFF_UNDEFINED))
+ ThrowTPE("Wrong type %u encountered. Expected SLong or Undefined on 0x%x",
+ type, tag);
+
+ return data.peek<int32>(index);
+}
+
+float TiffEntry::getFloat(uint32 index) const {
+ if (!isFloat()) {
+ ThrowTPE("Wrong type 0x%x encountered. Expected Float or something "
+ "convertible on 0x%x",
+ type, tag);
+ }
+
+ switch (type) {
+ case TIFF_DOUBLE: return data.peek<double>(index);
+ case TIFF_FLOAT: return data.peek<float>(index);
+ case TIFF_LONG:
+ case TIFF_SHORT:
+ return static_cast<float>(getU32(index));
+ case TIFF_SLONG:
+ case TIFF_SSHORT:
+ return static_cast<float>(getI32(index));
+ case TIFF_RATIONAL: {
+ uint32 a = getU32(index*2);
+ uint32 b = getU32(index*2+1);
+ return b != 0 ? static_cast<float>(a) / b : 0.0F;
+ }
+ case TIFF_SRATIONAL: {
+ auto a = static_cast<int>(getU32(index * 2));
+ auto b = static_cast<int>(getU32(index * 2 + 1));
+ return b ? static_cast<float>(a) / b : 0.0F;
+ }
+ default:
+ // unreachable
+ return 0.0F;
+ }
+}
+
+string TiffEntry::getString() const {
+ if (type != TIFF_ASCII && type != TIFF_BYTE)
+ ThrowTPE("Wrong type 0x%x encountered. Expected Ascii or Byte", type);
+
+ // *NOT* ByteStream::peekString() !
+ const auto bufSize = data.getRemainSize();
+ const auto* buf = data.peekData(bufSize);
+ const auto* s = reinterpret_cast<const char*>(buf);
+ return string(s, strnlen(s, bufSize));
+}
+
+const DataBuffer &TiffEntry::getRootIfdData() const {
+ TiffIFD* p = parent;
+ TiffRootIFD* r = nullptr;
+ while (p) {
+ r = dynamic_cast<TiffRootIFD*>(p);
+ if (r)
+ break;
+ p = p->parent;
+ }
+ if (!r)
+ ThrowTPE("Internal error in TiffIFD data structure.");
+
+ assert(r != nullptr);
+ return r->rootBuffer;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.h
b/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.h
new file mode 100644
index 00000000..3d882502
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/TiffEntry.h
@@ -0,0 +1,118 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+ Copyright (C) 2017 Axel Waggershauser
+
+ 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, uchar8, ushort16, int32, short16
+#include "io/ByteStream.h" // for ByteStream
+#include "tiff/TiffTag.h" // for TiffTag
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class DataBuffer;
+
+class TiffIFD;
+
+/*
+ * Tag data type information.
+ *
+ * Note: RATIONALs are the ratio of two 32-bit integer values.
+ */
+enum TiffDataType {
+ TIFF_NOTYPE = 0, /* placeholder */
+ TIFF_BYTE = 1, /* 8-bit unsigned integer */
+ TIFF_ASCII = 2, /* 8-bit bytes w/ last byte null */
+ TIFF_SHORT = 3, /* 16-bit unsigned integer */
+ TIFF_LONG = 4, /* 32-bit unsigned integer */
+ TIFF_RATIONAL = 5, /* 64-bit unsigned fraction */
+ TIFF_SBYTE = 6, /* !8-bit signed integer */
+ TIFF_UNDEFINED = 7, /* !8-bit untyped data */
+ TIFF_SSHORT = 8, /* !16-bit signed integer */
+ TIFF_SLONG = 9, /* !32-bit signed integer */
+ TIFF_SRATIONAL = 10, /* !64-bit signed fraction */
+ TIFF_FLOAT = 11, /* !32-bit IEEE floating point */
+ TIFF_DOUBLE = 12, /* !64-bit IEEE floating point */
+ TIFF_OFFSET = 13, /* 32-bit unsigned offset used for IFD and other offsets */
+};
+
+class TiffEntry
+{
+ TiffIFD* parent;
+ ByteStream data;
+
+ friend class TiffIFD;
+
+ template <typename T, T (TiffEntry::*getter)(uint32 index) const>
+ std::vector<T> getArray(uint32 count_) const {
+ std::vector<T> res(count_);
+ for (uint32 i = 0; i < count_; ++i)
+ res[i] = (this->*getter)(i);
+ return res;
+ }
+
+public:
+ TiffTag tag;
+ TiffDataType type;
+ uint32 count;
+
+ TiffEntry(TiffIFD* parent, TiffTag tag, TiffDataType type, uint32 count,
+ ByteStream&& data);
+ TiffEntry(TiffIFD* parent, ByteStream* bs);
+
+ bool __attribute__((pure)) isFloat() const;
+ bool __attribute__((pure)) isInt() const;
+ bool __attribute__((pure)) isString() const;
+ uchar8 getByte(uint32 index = 0) const;
+ uint32 getU32(uint32 index = 0) const;
+ int32 getI32(uint32 index = 0) const;
+ ushort16 getU16(uint32 index = 0) const;
+ short16 getI16(uint32 index = 0) const;
+ float getFloat(uint32 index = 0) const;
+ std::string getString() const;
+
+ inline std::vector<ushort16> getU16Array(uint32 count_) const
+ {
+ return getArray<ushort16, &TiffEntry::getU16>(count_);
+ }
+
+ inline std::vector<uint32> getU32Array(uint32 count_) const
+ {
+ return getArray<uint32, &TiffEntry::getU32>(count_);
+ }
+
+ inline std::vector<float> getFloatArray(uint32 count_) const
+ {
+ return getArray<float, &TiffEntry::getFloat>(count_);
+ }
+
+ ByteStream& getData() { return data; }
+ const uchar8* getData(uint32 size) { return data.getData(size); }
+
+ const DataBuffer& getRootIfdData() const;
+
+protected:
+ static const std::array<uint32, 14> datashifts;
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.cpp
b/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.cpp
new file mode 100644
index 00000000..4a0a08d0
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.cpp
@@ -0,0 +1,362 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ Copyright (C) 2009-2014 Klaus Post
+ Copyright (C) 2015 Pedro Côrte-Real
+ 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
+ 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/TiffIFD.h"
+#include "common/Common.h" // for trimSpaces, uint32
+#include "common/NORangesSet.h" // for set
+#include "common/RawspeedException.h" // for RawspeedException
+#include "io/IOException.h" // for IOException
+#include "tiff/TiffEntry.h" // for TiffEntry
+#include "tiff/TiffTag.h" // for TiffTag, MAKE, DNGPRIVATEDATA
+#include <cassert> // for assert
+#include <cstdint> // for UINT32_MAX
+#include <map> // for map, _Rb_tree_const_iterator
+#include <memory> // for unique_ptr, make_unique
+#include <string> // for string, operator==
+#include <utility> // for move, pair
+#include <vector> // for vector
+
+using std::string;
+using std::vector;
+
+namespace rawspeed {
+
+void TiffIFD::parseIFDEntry(NORangesSet<Buffer>* ifds, ByteStream* bs) {
+ assert(ifds);
+
+ TiffEntryOwner t;
+
+ auto origPos = bs->getPosition();
+
+ try {
+ t = std::make_unique<TiffEntry>(this, bs);
+ } catch (IOException&) { // Ignore unparsable entry
+ // fix probably broken position due to interruption by exception
+ // i.e. setting it to the next entry.
+ bs->setPosition(origPos + 12);
+ return;
+ }
+
+ try {
+ switch (t->tag) {
+ case DNGPRIVATEDATA:
+ // These are arbitrairly 'rebased', to preserve the offsets, but as it is
+ // implemented right now, that could trigger UB (pointer arithmetics,
+ // creating pointer to unowned memory, etc). And since this is not even
+ // used anywhere right now, let's not
+ // add(parseDngPrivateData(ifds, t.get()));
+ // but just add them as entries. (e.g. ArwDecoder uses WB from them)
+ add(move(t));
+ break;
+
+ case MAKERNOTE:
+ case MAKERNOTE_ALT:
+ add(parseMakerNote(ifds, t.get()));
+ break;
+
+ case FUJI_RAW_IFD:
+ case SUBIFDS:
+ case EXIFIFDPOINTER:
+ for (uint32 j = 0; j < t->count; j++)
+ add(std::make_unique<TiffIFD>(this, ifds, *bs, t->getU32(j)));
+ break;
+
+ default:
+ add(move(t));
+ }
+ } catch (RawspeedException&) { // Unparsable private data are added as entries
+ add(move(t));
+ }
+}
+
+TiffIFD::TiffIFD(TiffIFD* parent_) : parent(parent_) {
+ recursivelyCheckSubIFDs(1);
+ // If we are good (can add this IFD without violating the limits),
+ // we are still here. However, due to the way we add parsed sub-IFD's (lazy),
+ // we need to count this IFD right *NOW*, not when adding it at the end.
+ recursivelyIncrementSubIFDCount();
+}
+
+TiffIFD::TiffIFD(TiffIFD* parent_, NORangesSet<Buffer>* ifds,
+ const DataBuffer& data, uint32 offset)
+ : TiffIFD(parent_) {
+ // see TiffParser::parse: UINT32_MAX is used to mark the "virtual" top level
+ // TiffRootIFD in a tiff file
+ if (offset == UINT32_MAX)
+ return;
+
+ assert(ifds);
+
+ ByteStream bs(data);
+ bs.setPosition(offset);
+
+ // Directory entries in this IFD
+ auto numEntries = bs.getU16();
+
+ // 2 bytes for entry count
+ // each entry is 12 bytes
+ // 4-byte offset to the next IFD at the end
+ const auto IFDFullSize = 2 + 4 + 12 * numEntries;
+ const Buffer IFDBuf(data.getSubView(offset, IFDFullSize));
+ if (!ifds->emplace(IFDBuf).second)
+ ThrowTPE("Two IFD's overlap. Raw corrupt!");
+
+ for (uint32 i = 0; i < numEntries; i++)
+ parseIFDEntry(ifds, &bs);
+
+ nextIFD = bs.getU32();
+}
+
+TiffRootIFDOwner TiffIFD::parseDngPrivateData(NORangesSet<Buffer>* ifds,
+ TiffEntry* t) {
+ assert(ifds);
+
+ /*
+ 1. Six bytes containing the zero-terminated string "Adobe".
+ (The DNG specification calls for the DNGPrivateData tag to start with an
+ ASCII string identifying the creator/format).
+ 2. 4 bytes: an ASCII string ("MakN" for a Makernote), indicating what sort of
+ data is being stored here.
+ Note that this is not zero-terminated.
+ 3. A four-byte count (number of data bytes following);
+ This is the length of the original MakerNote data.
+ (This is always in "most significant byte first" format).
+ 4. 2 bytes: the byte-order indicator from the original file
+ (the usual 'MM'/4D4D or 'II'/4949).
+ 5. 4 bytes: the original file offset for the MakerNote tag data
+ (stored according to the byte order given above).
+ 6. The contents of the MakerNote tag.
+ This is a simple byte-for-byte copy, with no modification.
+ */
+ ByteStream& bs = t->getData();
+ if (!bs.skipPrefix("Adobe", 6))
+ ThrowTPE("Not Adobe Private data");
+
+ if (!bs.skipPrefix("MakN", 4))
+ ThrowTPE("Not Makernote");
+
+ bs.setByteOrder(Endianness::big);
+ uint32 makerNoteSize = bs.getU32();
+ if (makerNoteSize > bs.getRemainSize())
+ ThrowTPE("Error reading TIFF structure (invalid size). File Corrupt");
+
+ bs.setByteOrder(getTiffByteOrder(bs, 0, "DNG makernote"));
+ bs.skipBytes(2);
+
+ uint32 makerNoteOffset = bs.getU32();
+ makerNoteSize -= 6; // update size of orinial maker note, we skipped 2+4 bytes
+
+ // Update the underlying buffer of t, such that the maker note data starts at its original offset
+ bs.rebase(makerNoteOffset, makerNoteSize);
+
+ return parseMakerNote(ifds, t);
+}
+
+/* This will attempt to parse makernotes and return it as an IFD */
+TiffRootIFDOwner TiffIFD::parseMakerNote(NORangesSet<Buffer>* ifds,
+ TiffEntry* t) {
+ assert(ifds);
+
+ // go up the IFD tree and try to find the MAKE entry on each level.
+ // we can not go all the way to the top first because this partial tree
+ // is not yet added to the TiffRootIFD.
+ TiffIFD* p = this;
+ TiffEntry* makeEntry;
+ do {
+ makeEntry = p->getEntryRecursive(MAKE);
+ p = p->parent;
+ } while (!makeEntry && p);
+ string make = makeEntry != nullptr ? trimSpaces(makeEntry->getString()) : "";
+
+ ByteStream bs = t->getData();
+
+ // helper function for easy setup of ByteStream buffer for the different maker note types
+ // 'rebase' means position 0 of new stream equals current position
+ // 'newPosition' is the position where the IFD starts
+ // 'byteOrderOffset' is the position wher the 2 magic bytes (II/MM) may be found
+ // 'context' is a string providing error information in case the byte order parsing should fail
+ auto setup = [&bs](bool rebase, uint32 newPosition,
+ uint32 byteOrderOffset = 0,
+ const char *context = nullptr) {
+ if (rebase)
+ bs = bs.getSubStream(bs.getPosition(), bs.getRemainSize());
+ if (context)
+ bs.setByteOrder(getTiffByteOrder(bs, byteOrderOffset, context));
+ bs.skipBytes(newPosition);
+ };
+
+ if (bs.hasPrefix("AOC\0", 4)) {
+ setup(false, 6, 4, "Pentax makernote");
+ } else if (bs.hasPrefix("PENTAX", 6)) {
+ setup(true, 10, 8, "Pentax makernote");
+ } else if (bs.hasPrefix("FUJIFILM\x0c\x00\x00\x00", 12)) {
+ bs.setByteOrder(Endianness::little);
+ setup(true, 12);
+ } else if (bs.hasPrefix("Nikon\x00\x02", 7)) {
+ // this is Nikon type 3 maker note format
+ // TODO: implement Nikon type 1 maker note format
+ // see http://www.ozhiker.com/electronics/pjmt/jpeg_info/nikon_mn.html
+ bs.skipBytes(10);
+ setup(true, 8, 0, "Nikon makernote");
+ } else if (bs.hasPrefix("OLYMPUS", 7)) { // new Olympus
+ setup(true, 12);
+ } else if (bs.hasPrefix("OLYMP", 5)) { // old Olympus
+ setup(true, 8);
+ } else if (bs.hasPrefix("EPSON", 5)) {
+ setup(false, 8);
+ } else if (bs.hasPatternAt("Exif", 4, 6)) {
+ // TODO: for none of the rawsamples.ch files from Panasonic is this true, instead their MakerNote start
with "Panasonic"
+ // Panasonic has the word Exif at byte 6, a complete Tiff header starts at byte 12
+ // This TIFF is 0 offset based
+ setup(false, 20, 12, "Panosonic makernote");
+ } else if (make == "SAMSUNG") {
+ // Samsung has no identification in its MakerNote but starts with the IFD right away
+ setup(true, 0);
+ } else {
+ // cerr << "default MakerNote from " << make << endl; // Canon, Nikon (type 2), Sony, Minolta, Ricoh,
Leica, Hasselblad, etc.
+
+ // At least one MAKE has not been handled explicitly and starts its MakerNote with an endian prefix:
Kodak
+ if (bs.skipPrefix("II", 2)) {
+ bs.setByteOrder(Endianness::little);
+ } else if (bs.skipPrefix("MM", 2)) {
+ bs.setByteOrder(Endianness::big);
+ }
+ }
+
+ // Attempt to parse the rest as an IFD
+ return std::make_unique<TiffRootIFD>(this, ifds, bs, bs.getPosition());
+}
+
+std::vector<const TiffIFD*> TiffIFD::getIFDsWithTag(TiffTag tag) const {
+ vector<const TiffIFD*> matchingIFDs;
+ if (entries.find(tag) != entries.end()) {
+ matchingIFDs.push_back(this);
+ }
+ for (auto& i : subIFDs) {
+ vector<const TiffIFD*> t = i->getIFDsWithTag(tag);
+ matchingIFDs.insert(matchingIFDs.end(), t.begin(), t.end());
+ }
+ return matchingIFDs;
+}
+
+const TiffIFD* TiffIFD::getIFDWithTag(TiffTag tag, uint32 index) const
+{
+ auto ifds = getIFDsWithTag(tag);
+ if (index >= ifds.size())
+ ThrowTPE("failed to find %u ifs with tag 0x%04x", index + 1, tag);
+ return ifds[index];
+}
+
+TiffEntry* __attribute__((pure)) TiffIFD::getEntryRecursive(TiffTag tag) const {
+ auto i = entries.find(tag);
+ if (i != entries.end()) {
+ return i->second.get();
+ }
+ for (auto &j : subIFDs) {
+ TiffEntry *entry = j->getEntryRecursive(tag);
+ if (entry)
+ return entry;
+ }
+ return nullptr;
+}
+
+void TiffIFD::recursivelyIncrementSubIFDCount() {
+ TiffIFD* p = this->parent;
+ if (!p)
+ return;
+
+ p->subIFDCount++;
+
+ for (; p != nullptr; p = p->parent)
+ p->subIFDCountRecursive++;
+}
+
+void TiffIFD::checkSubIFDs(int headroom) const {
+ int count = headroom + subIFDCount;
+ if (!headroom)
+ assert(count <= TiffIFD::Limits::SubIFDCount);
+ else if (count > TiffIFD::Limits::SubIFDCount)
+ ThrowTPE("TIFF IFD has %u SubIFDs", count);
+
+ count = headroom + subIFDCountRecursive;
+ if (!headroom)
+ assert(count <= TiffIFD::Limits::RecursiveSubIFDCount);
+ else if (count > TiffIFD::Limits::RecursiveSubIFDCount)
+ ThrowTPE("TIFF IFD file has %u SubIFDs (recursively)", count);
+}
+
+void TiffIFD::recursivelyCheckSubIFDs(int headroom) const {
+ int depth = 0;
+ for (const TiffIFD* p = this; p != nullptr;) {
+ if (!headroom)
+ assert(depth <= TiffIFD::Limits::Depth);
+ else if (depth > TiffIFD::Limits::Depth)
+ ThrowTPE("TiffIFD cascading overflow, found %u level IFD", depth);
+
+ p->checkSubIFDs(headroom);
+
+ // And step up
+ p = p->parent;
+ depth++;
+ }
+}
+
+void TiffIFD::add(TiffIFDOwner subIFD) {
+ assert(subIFD->parent == this);
+
+ // We are good, and actually can add this sub-IFD, right?
+ subIFD->recursivelyCheckSubIFDs(0);
+
+ subIFDs.push_back(move(subIFD));
+}
+
+void TiffIFD::add(TiffEntryOwner entry) {
+ entry->parent = this;
+ entries[entry->tag] = move(entry);
+}
+
+TiffEntry* TiffIFD::getEntry(TiffTag tag) const {
+ auto i = entries.find(tag);
+ if (i == entries.end())
+ ThrowTPE("Entry 0x%x not found.", tag);
+ return i->second.get();
+}
+
+TiffID TiffRootIFD::getID() const
+{
+ TiffID id;
+ auto makeE = getEntryRecursive(MAKE);
+ auto modelE = getEntryRecursive(MODEL);
+
+ if (!makeE)
+ ThrowTPE("Failed to find MAKE entry.");
+ if (!modelE)
+ ThrowTPE("Failed to find MODEL entry.");
+
+ id.make = trimSpaces(makeE->getString());
+ id.model = trimSpaces(modelE->getString());
+
+ return id;
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.h
b/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.h
new file mode 100644
index 00000000..299bc771
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/TiffIFD.h
@@ -0,0 +1,153 @@
+/*
+ RawSpeed - RAW file decoder.
+
+ 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
+ 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, ushort16
+#include "common/NORangesSet.h" // for NORangesSet
+#include "io/Buffer.h" // for Buffer (ptr only), DataBuffer
+#include "io/ByteStream.h" // for ByteStream
+#include "io/Endianness.h" // for getHostEndianness, Endianne...
+#include "parsers/TiffParserException.h" // for ThrowTPE
+#include "tiff/TiffEntry.h" // IWYU pragma: keep
+#include "tiff/TiffTag.h" // for TiffTag
+#include <map> // for map, _Rb_tree_const_iterator
+#include <memory> // for unique_ptr
+#include <string> // for string
+#include <vector> // for vector
+
+namespace rawspeed {
+
+class TiffIFD;
+
+class TiffRootIFD;
+
+using TiffIFDOwner = std::unique_ptr<TiffIFD>;
+using TiffRootIFDOwner = std::unique_ptr<TiffRootIFD>;
+using TiffEntryOwner = std::unique_ptr<TiffEntry>;
+
+class TiffIFD
+{
+ uint32 nextIFD = 0;
+
+ TiffIFD* const parent;
+
+ std::vector<TiffIFDOwner> subIFDs;
+
+ int subIFDCount = 0;
+ int subIFDCountRecursive = 0;
+
+ std::map<TiffTag, TiffEntryOwner> entries;
+
+ friend class TiffEntry;
+ friend class FiffParser;
+ friend class TiffParser;
+
+ void recursivelyIncrementSubIFDCount();
+ void checkSubIFDs(int headroom) const;
+ void recursivelyCheckSubIFDs(int headroom) const;
+
+ void add(TiffIFDOwner subIFD);
+ void add(TiffEntryOwner entry);
+ TiffRootIFDOwner parseDngPrivateData(NORangesSet<Buffer>* ifds, TiffEntry* t);
+ TiffRootIFDOwner parseMakerNote(NORangesSet<Buffer>* ifds, TiffEntry* t);
+ void parseIFDEntry(NORangesSet<Buffer>* ifds, ByteStream* bs);
+
+ // TIFF IFD are tree-like structure, with branches.
+ // A branch (IFD) can have branches (IFDs) of it's own.
+ // We must be careful to weed-out all the degenerative cases that
+ // can be produced e.g. via fuzzing, or other means.
+ struct Limits final {
+ // How many layers of IFD's can there be?
+ // All RPU samples (as of 2018-02-11) are ok with 4.
+ // However, let's be on the safe side, and pad it by one.
+ static constexpr int Depth = 4 + 1;
+
+ // How many sub-IFD's can this IFD have?
+ // NOTE: only for the given IFD, *NOT* recursively including all sub-IFD's!
+ // All RPU samples (as of 2018-02-11) are ok with 5.
+ // However, let's be on the safe side, and double it.
+ static constexpr int SubIFDCount = 5 * 2;
+
+ // How many sub-IFD's can this IFD have, recursively?
+ // All RPU samples (as of 2018-02-11) are ok with 14.
+ // However, let's be on the safe side, and double it.
+ static constexpr int RecursiveSubIFDCount = 14 * 2;
+ };
+
+public:
+ explicit TiffIFD(TiffIFD* parent);
+
+ TiffIFD(TiffIFD* parent, NORangesSet<Buffer>* ifds, const DataBuffer& data,
+ uint32 offset);
+
+ virtual ~TiffIFD() = default;
+
+ // make sure we never copy-constuct/assign a TiffIFD to keep the owning
+ // subcontainers contents save
+ TiffIFD(const TiffIFD&) = delete;
+ TiffIFD& operator=(const TiffIFD&) = delete;
+
+ uint32 getNextIFD() const {return nextIFD;}
+ std::vector<const TiffIFD*> getIFDsWithTag(TiffTag tag) const;
+ const TiffIFD* getIFDWithTag(TiffTag tag, uint32 index = 0) const;
+ TiffEntry* getEntry(TiffTag tag) const;
+ TiffEntry* __attribute__((pure)) getEntryRecursive(TiffTag tag) const;
+ bool __attribute__((pure)) hasEntry(TiffTag tag) const {
+ return entries.find(tag) != entries.end();
+ }
+ bool hasEntryRecursive(TiffTag tag) const { return getEntryRecursive(tag) != nullptr; }
+
+ const std::vector<TiffIFDOwner>& getSubIFDs() const { return subIFDs; }
+// const std::map<TiffTag, TiffEntry*>& getEntries() const { return entries; }
+};
+
+struct TiffID
+{
+ std::string make;
+ std::string model;
+};
+
+class TiffRootIFD final : public TiffIFD {
+public:
+ const DataBuffer rootBuffer;
+
+ TiffRootIFD(TiffIFD* parent_, NORangesSet<Buffer>* ifds,
+ const DataBuffer& data, uint32 offset)
+ : TiffIFD(parent_, ifds, data, offset), rootBuffer(data) {}
+
+ // find the MAKE and MODEL tags identifying the camera
+ // note: the returned strings are trimmed automatically
+ TiffID getID() const;
+};
+
+inline Endianness getTiffByteOrder(const ByteStream& bs, uint32 pos,
+ const char* context = "") {
+ if (bs.hasPatternAt("II", 2, pos))
+ return Endianness::little;
+ if (bs.hasPatternAt("MM", 2, pos))
+ return Endianness::big;
+
+ ThrowTPE("Failed to parse TIFF endianess information in %s.", context);
+}
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/TiffTag.h
b/subprojects/rawspeed/src/librawspeed/tiff/TiffTag.h
new file mode 100644
index 00000000..78129f0b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/TiffTag.h
@@ -0,0 +1,359 @@
+// Authors:
+// Larry Ewing <lewing novell com>
+//
+//
+// Copyright (C) 2004 - 2006 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#pragma once
+
+namespace rawspeed {
+
+enum TiffTag {
+ INTEROPERABILITYINDEX = 0x0001,
+ INTEROPERABILITYVERSION = 0x0002,
+ CANONSHOTINFO = 0x0004,
+ CANONPOWERSHOTG9WB = 0x0029,
+ PANASONIC_ISO_SPEED = 23,
+ NEWSUBFILETYPE = 0x00FE,
+ SUBFILETYPE = 0x00FF,
+ PANASONIC_BITSPERSAMPLE = 0xa,
+ PANASONIC_RAWFORMAT = 0x2d,
+ MAKERNOTE_ALT = 0x2e,
+
+ IMAGEWIDTH = 0x0100,
+ IMAGELENGTH = 0x0101,
+ BITSPERSAMPLE = 0x0102,
+ COMPRESSION = 0x0103,
+ PHOTOMETRICINTERPRETATION = 0x0106,
+ FILLORDER = 0x010A,
+ DOCUMENTNAME = 0x010D,
+ IMAGEDESCRIPTION = 0x010E,
+ MAKE = 0x010F,
+ MODEL = 0x0110,
+ STRIPOFFSETS = 0x0111,
+ ORIENTATION = 0x0112,
+ SAMPLESPERPIXEL = 0x0115,
+ ROWSPERSTRIP = 0x0116,
+ STRIPBYTECOUNTS = 0x0117,
+ PANASONIC_STRIPOFFSET = 0x118,
+ XRESOLUTION = 0x011A,
+ YRESOLUTION = 0x011B,
+ PLANARCONFIGURATION = 0x011C,
+
+ GRAYRESPONSECURVE = 0x0123,
+
+ T4OPTIONS = 0x0124,
+ T6OPTIONS = 0x0125,
+
+ RESOLUTIONUNIT = 0x0128,
+ TRANSFERFUNCTION = 0x012D,
+ FUJI_LAYOUT = 0x0130,
+ SOFTWARE = 0x0131,
+ DATETIME = 0x0132,
+ ARTIST = 0x013B,
+ PREDICTOR = 0x013D,
+ WHITEPOINT = 0x013E,
+ PRIMARYCHROMATICITIES = 0x013F,
+
+ HALFTONEHINTS = 0x0141,
+ // TILED IMAGES
+ TILEWIDTH = 0x0142,
+ TILELENGTH = 0x0143,
+ TILEOFFSETS = 0x0144,
+ TILEBYTECOUNTS = 0x0145,
+
+ SUBIFDS = 0x014A, // TIFF-EP
+
+ // CMYK IMAGES
+ INKSET = 0x014C,
+ NUMBEROFINKS = 0x014E,
+ INKNAMES = 0x014D,
+ DOTRANGE = 0x0150,
+ TARGETPRINTER = 0x0151,
+ EXTRASAMPLES = 0x0152,
+ SAMPLEFORMAT = 0x0153,
+ SMINSAMPLEVALUE = 0x0154,
+ SMAXSAMPLEVALUE = 0x0155,
+
+ TRANSFERRANGE = 0x0156,
+
+ CLIPPATH = 0x0157, // TIFF PAGEMAKER TECHNOTE #2.
+
+ JPEGTABLES = 0x015B, // TIFF-EP
+
+ JPEGPROC = 0x0200,
+ JPEGINTERCHANGEFORMAT = 0x0201,
+ JPEGINTERCHANGEFORMATLENGTH = 0x0202,
+ JPEGRESTARTINTERVAL = 0x0203,
+ JPEGLOSSLESSPREDICTORS = 0x0205,
+ JPEGPOINTTRANSFORMS = 0x0206,
+ JPEGQTABLES = 0x0207,
+ JPEGDCTABLES = 0x0208,
+ JPEGACTABLES = 0x0209,
+
+ YCBCRCOEFFICIENTS = 0x0211,
+ YCBCRSUBSAMPLING = 0x0212,
+ YCBCRPOSITIONING = 0x0213,
+
+ REFERENCEBLACKWHITE = 0x0214,
+ KODAKWB = 0x0F00,
+ EPSONWB = 0x0E80,
+ RELATEDIMAGEFILEFORMAT = 0x1000,
+ RELATEDIMAGEWIDTH = 0x1001,
+ RELATEDIMAGELENGTH = 0x1002,
+ OLYMPUSREDMULTIPLIER = 0x1017,
+ OLYMPUSBLUEMULTIPLIER = 0x1018,
+ OLYMPUSIMAGEPROCESSING = 0x2040,
+ FUJIOLDWB = 0x2ff0,
+
+ CANONCOLORDATA = 0x4001,
+
+ SONYGRBGLEVELS = 0x7303,
+ SONYRGGBLEVELS = 0x7313,
+
+ CFAREPEATPATTERNDIM = 0x828D,
+ CFAPATTERN = 0x828E,
+ BATTERYLEVEL = 0x828F,
+ COPYRIGHT = 0x8298,
+ EXPOSURETIME = 0x829A,
+ FNUMBER = 0x829D,
+
+ // THESE ARE FROM THE NIFF SPEC AND ONLY REALLY VALID WHEN THE HEADER BEGINS WITH IIN1
+ // SEE THE NIFFTAG ENUM FOR THE SPECIFCATION SPECIFIC NAMES
+ ROTATION = 0x82B9,
+ NAVYCOMPRESSION = 0x82BA,
+ TILEINDEX = 0x82BB,
+ // END NIFF SPECIFIC
+
+ IPTCNAA = 0x83BB,
+
+ LEAFMETADATA = 0x8606,
+
+ PHOTOSHOPPRIVATE = 0x8649,
+
+ EXIFIFDPOINTER = 0x8769,
+ INTERCOLORPROFILE = 0x8773,
+ EXPOSUREPROGRAM = 0x8822,
+ SPECTRALSENSITIVITY = 0x8824,
+ GPSINFOIFDPOINTER = 0x8825,
+ ISOSPEEDRATINGS = 0x8827,
+ OECF = 0x8828,
+ EXIFVERSION = 0x9000,
+ DATETIMEORIGINAL = 0x9003,
+ DATETIMEDIGITIZED = 0x9004,
+ COMPONENTSCONFIGURATION = 0x9101,
+ COMPRESSEDBITSPERPIXEL = 0x9102,
+ SHUTTERSPEEDVALUE = 0x9201,
+ APERTUREVALUE = 0x9202,
+ BRIGHTNESSVALUE = 0x9203,
+ EXPOSUREBIASVALUE = 0x9204,
+ MAXAPERTUREVALUE = 0x9205,
+ SUBJECTDISTANCE = 0x9206,
+ METERINGMODE = 0x9207,
+ LIGHTSOURCE = 0x9208,
+ FLASH = 0x9209,
+ FOCALLENGTH = 0x920A,
+
+ FLASHENERGY_TIFFEP = 0x920B,// TIFF-EP
+ SPACIALFREQUENCYRESPONSE = 0x920C,// TIFF-EP
+ NOISE = 0x920D,// TIFF-EP
+ FOCALPLANEXRESOLUTION_TIFFEP = 0x920E,// TIFF-EP
+ FOCALPLANEYRESOLUTION_TIFFEP = 0x920F,// TIFF-EP
+ FOCALPLANERESOLUTIONUNIT_TIFFEP = 0x9210,// TIFF-EP
+ IMAGENAME = 0x9211,// TIFF-EP
+ SECURITYCLASSIFICATION = 0x9212,// TIFF-EP
+
+ IMAGEHISTORY = 0x9213, // TIFF-EP NULL SEPARATED LIST
+
+ SUBJECTAREA = 0x9214,
+
+ EXPOSUREINDEX_TIFFEP = 0x9215, // TIFF-EP
+ TIFFEPSTANDARDID = 0x9216, // TIFF-EP
+ SENSINGMETHOD_TIFFEP = 0x9217, // TIFF-EP
+
+ MAKERNOTE = 0x927C,
+ USERCOMMENT = 0x9286,
+ SUBSECTIME = 0x9290,
+ SUBSECTIMEORIGINAL = 0x9291,
+ SUBSECTIMEDIGITIZED = 0x9292,
+ FLASHPIXVERSION = 0xA000,
+ COLORSPACE = 0xA001,
+ PIXELXDIMENSION = 0xA002,
+ PIXELYDIMENSION = 0xA003,
+ RELATEDSOUNDFILE = 0xA004,
+ INTEROPERABILITYIFDPOINTER = 0xA005,
+ SAMSUNG_WB_RGGBLEVELSUNCORRECTED = 0xa021,
+ SAMSUNG_WB_RGGBLEVELSBLACK = 0xa028,
+ FLASHENERGY = 0xA20B,
+ SPATIALFREQUENCYRESPONSE = 0xA20C,
+ FOCALPLANEXRESOLUTION = 0xA20E,
+ FOCALPLANEYRESOLUTION = 0xA20F,
+ FOCALPLANERESOLUTIONUNIT = 0xA210,
+ SUBJECTLOCATION = 0xA214,
+ EXPOSUREINDEX = 0xA215,
+ SENSINGMETHOD = 0xA217,
+ FILESOURCE = 0xA300,
+ SCENETYPE = 0xA301,
+ EXIFCFAPATTERN = 0xA302,
+ CUSTOMRENDERED = 0xA401,
+ EXPOSUREMODE = 0xA402,
+ WHITEBALANCE = 0xA403,
+ DIGITALZOOMRATIO = 0xA404,
+ FOCALLENGTHIN35MMFILM = 0xA405,
+ SCENECAPTURETYPE = 0xA406,
+ GAINCONTROL = 0xA407,
+ CONTRAST = 0xA408,
+ SATURATION = 0xA409,
+ SHARPNESS = 0xA40A,
+ DEVICESETTINGDESCRIPTION = 0xA40B,
+ SUBJECTDISTANCERANGE = 0xA40C,
+ IMAGEUNIQUEID = 0xA420,
+
+ // THE FOLLOWING IDS ARE NOT DESCRIBED THE EXIF SPEC
+#ifndef GAMMA
+ GAMMA = 0xA500,
+#endif
+
+ // THE XMP SPEC DECLARES THAT XMP DATA SHOULD LIVE 0x2BC WHEN
+ // EMBEDDED IN TIFF IMAGES.
+ XMP = 0x02BC,
+ // Canon tag for uncompressed RGB preview
+ CANON_UNCOMPRESSED = 0xC5D9,
+
+ // FROM THE DNG SPEC
+ DNGVERSION = 0xC612, // IFD0
+ DNGBACKWARDVERSION = 0xC613, // IFD0
+ UNIQUECAMERAMODEL = 0xC614, // IFD0
+ LOCALIZEDCAMERAMODEL = 0xC615, // IFD0
+ CFAPLANECOLOR = 0xC616, // RAWIFD
+ CFALAYOUT = 0xC617, // RAWIFD
+ LINEARIZATIONTABLE = 0xC618, // RAWIFD
+ BLACKLEVELREPEATDIM = 0xC619, // RAWIFD
+ BLACKLEVEL = 0xC61A, // RAWIFD
+ BLACKLEVELDELTAH = 0xC61B, // RAWIFD
+ BLACKLEVELDELTAV = 0xC61C, // RAWIFD
+ WHITELEVEL = 0xC61D, // RAWIFD
+ DEFAULTSCALE = 0xC61E, // RAWIFD
+ DEFAULTCROPORIGIN = 0xC61F, // RAWIFD
+ DEFAULTCROPSIZE = 0xC620, // RAWIFD
+ COLORMATRIX1 = 0xC621, // IFD0
+ COLORMATRIX2 = 0xC622, // IFD0
+ CAMERACALIBRATION1 = 0xC623, // IFD0
+ CAMERACALIBRATION2 = 0xC624, // IFD0
+ REDUCTIONMATRIX1 = 0xC625, // IFD0
+ REDUCTIONMATRIX2 = 0xC626, // IFD0
+ ANALOGBALANCE = 0xC627, // IFD0
+ ASSHOTNEUTRAL = 0xC628, // IFD0
+ ASSHOTWHITEXY = 0xC629, // IFD0
+ BASELINEEXPOSURE = 0xC62A, // IFD0
+ BASELINENOISE = 0xC62B, // IFD0
+ BASELINESHARPNESS = 0xC62C, // IFD0
+ BAYERGREESPIT = 0xC62D, // IFD0
+ LINEARRESPONSELIMIT = 0xC62E, // IFD0
+ CAMERASERIALNUMBER = 0xC62F, // IFD0
+ LENSINFO = 0xC630, // IFD0
+ CHROMABLURRADIUS = 0xC631, // RAWIFD
+ ANTIALIASSTRENGTH = 0xC632, // RAWIFD
+ DNGPRIVATEDATA = 0xC634, // IFD0
+
+ MAKERNOTESAFETY = 0xC635, // IFD0
+
+ // THE SPEC SAYS BESTQUALITYSCALE IS 0xC635 BUT IT APPEARS TO BE WRONG
+ //BESTQUALITYSCALE = 0xC635, // RAWIFD
+ BESTQUALITYSCALE = 0xC65C, // RAWIFD THIS LOOKS LIKE THE CORRECT VALUE
+ SHADOWSCALE = 50739,
+ RAWDATAUNIQUEID = 50781,
+ ORIGINALRAWFILENAME = 50827,
+ ORIGINALRAWFILEDATA = 50828,
+ ACTIVEAREA = 50829,
+ MASKEDAREAS = 50830,
+ ASSHOTICCPROFILE = 50831,
+ ASSHOTPREPROFILEMATRIX = 50832,
+ CURRENTICCPROFILE = 50833,
+ CURRENTPREPROFILEMATRIX = 50834,
+ COLORIMETRICREFERENCE = 50879,
+ KODAKKDCPRIVATEIFD = 65024,
+ CAMERACALIBRATIONSIGNATURE = 0xC6F3,
+ PROFILECALIBRATIONSIGNATURE = 0xC6F4,
+ EXTRACAMERAPROFILES = 0xC6F5,
+ ASSHOTPROFILENAME = 0xC6F6,
+ NOISEREDUCTIONAPPLIED = 0xC6F7,
+ PROFILENAME = 0xC6F8,
+ PROFILEHUESATMAPDIMS = 0xC6F9,
+ PROFILEHUESATMAPDATA1 = 0xC6FA,
+ PROFILEHUESATMAPDATA2 = 0xC6FB,
+ PROFILETONECURVE = 0xC6FC,
+ PROFILEEMBEDPOLICY = 0xC6FD,
+ PROFILECOPYRIGHT = 0xC6FE,
+ FORWARDMATRIX1 = 0xC714,
+ FORWARDMATRIX2 = 0xC715,
+ PREVIEWAPPLICATIONNAME = 0xC716,
+ PREVIEWAPPLICATIONVERSION = 0xC717,
+ PREVIEWSETTINGSNAME = 0xC718,
+ PREVIEWSETTINGSDIGEST = 0xC719,
+ PREVIEWCOLORSPACE = 0xC71A,
+ PREVIEWDATETIME = 0xC71B,
+ RAWIMAGEDIGEST = 0xC71C,
+ ORIGINALRAWFILEDIGEST = 0xC71D,
+ SUBTILEBLOCKSIZE = 0xC71E,
+ ROWINTERLEAVEFACTOR = 0xC71F,
+ PROFILELOOKTABLEDIMS = 0xC725,
+ PROFILELOOKTABLEDATA = 0xC726,
+ OPCODELIST1 = 0xC740,
+ OPCODELIST2 = 0xC741,
+ OPCODELIST3 = 0xC742,
+ NOISEPROFILE = 0xC761,
+ CANONCR2SLICE = 0xC640, // CANON CR2
+ CANON_SRAWTYPE = 0xC6C5, // IFD3
+ CANON_SENSOR_INFO = 0x00E0, // MakerNote
+ CANON_RAW_DATA_OFFSET = 0x0081, // MakerNote TIF
+
+ CALIBRATIONILLUMINANT1 = 0xC65A, // IFD0
+ CALIBRATIONILLUMINANT2 = 0xC65B, // IFD0
+ SONY_CURVE = 28688,
+ SONY_OFFSET = 0x7200,
+ SONY_LENGTH = 0x7201,
+ SONY_KEY = 0x7221,
+
+ // PRINT IMAGE MATCHING DATA
+ PIMIFDPOINTER = 0xC4A5,
+ FUJI_RAW_IFD = 0xF000,
+ FUJI_RAWIMAGEFULLWIDTH = 0xF001,
+ FUJI_RAWIMAGEFULLHEIGHT = 0xF002,
+ FUJI_BITSPERSAMPLE = 0xF003,
+ FUJI_STRIPOFFSETS = 0xF007,
+ FUJI_STRIPBYTECOUNTS = 0xF008,
+ FUJI_BLACKLEVEL = 0xF00A,
+ FUJI_WB_GRBLEVELS = 0xF00E,
+
+ KODAK_IFD = 0x8290,
+ KODAK_LINEARIZATION = 0x090D,
+ KODAK_KDC_WB = 0xFA2A,
+ KODAK_KDC_OFFSET = 0xFD04,
+ KODAK_KDC_WIDTH = 0xFD00,
+ KODAK_KDC_HEIGHT = 0xFD01,
+ KODAK_KDC_SENSOR_WIDTH = 0xFA13,
+ KODAK_KDC_SENSOR_HEIGHT = 0xFA14,
+ KODAK_IFD2 = 0xFE00,
+};
+
+} // namespace rawspeed
diff --git a/subprojects/rawspeed/src/librawspeed/tiff/meson.build
b/subprojects/rawspeed/src/librawspeed/tiff/meson.build
new file mode 100644
index 00000000..a82c9bf7
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/tiff/meson.build
@@ -0,0 +1,18 @@
+sources = files(
+ 'TiffEntry.cpp',
+ 'TiffEntry.h',
+ 'TiffIFD.cpp',
+ 'TiffIFD.h',
+ 'TiffTag.h',
+ 'CiffEntry.cpp',
+ 'CiffEntry.h',
+ 'CiffIFD.cpp',
+ 'CiffIFD.h',
+ 'CiffTag.h',
+)
+
+librawspeed_tiff = static_library(
+ 'rawspeed-tiff',
+ sources,
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+)
diff --git a/subprojects/rawspeed/src/meson.build b/subprojects/rawspeed/src/meson.build
new file mode 100644
index 00000000..0482dd7e
--- /dev/null
+++ b/subprojects/rawspeed/src/meson.build
@@ -0,0 +1,2 @@
+subdir('external')
+subdir('librawspeed')
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]