[gnome-photos/wip/rishi/buffer-decoder: 11/11] Add Rawspeed



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]