[gnome-photos/wip/rishi/buffer-decoder: 3/7] Add Rawspeed
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/buffer-decoder: 3/7] Add Rawspeed
- Date: Fri, 7 Jun 2019 14:07:46 +0000 (UTC)
commit bbc343e4bd9205d8ebdaaf60751fc425413249dc
Author: Debarshi Ray <debarshir gnome org>
Date: Sat Jun 1 15:39:13 2019 +0000
Add Rawspeed
Subsequent commits will use this to add a codec for RAW files.
Based on Rawspeed commit 9858280412d8898a.
https://gitlab.gnome.org/GNOME/gnome-photos/issues/63
meson.build | 11 +
src/meson.build | 1 +
subprojects/rawspeed/config.h.in | 16 +
subprojects/rawspeed/data/README.md | 138 +
subprojects/rawspeed/data/cameras.xml | 11901 +++++++++++++++++++
subprojects/rawspeed/data/meson.build | 1 +
subprojects/rawspeed/meson.build | 77 +
subprojects/rawspeed/meson_options.txt | 20 +
.../rawspeed/src/external/AddressSanitizer.h | 87 +
.../rawspeed/src/external/MemorySanitizer.h | 67 +
.../rawspeed/src/external/ThreadSafetyAnalysis.h | 130 +
.../rawspeed/src/external/gopro/vc5/table17.inc | 291 +
subprojects/rawspeed/src/external/meson.build | 1 +
subprojects/rawspeed/src/librawspeed/README.md | 243 +
.../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 | 39 +
.../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 | 42 +
.../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 | 21 +
.../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 +
207 files changed, 40961 insertions(+)
---
diff --git a/meson.build b/meson.build
index 6e2c1127..d3b60622 100644
--- a/meson.build
+++ b/meson.build
@@ -13,6 +13,7 @@ photos_datadir = join_paths(photos_prefix, get_option('datadir'))
photos_libexecdir = join_paths(photos_prefix, get_option('libexecdir'))
photos_localedir = join_paths(photos_prefix, get_option('localedir'))
+photos_pkg_datadir = join_paths(photos_datadir, meson.project_name())
photos_docdir = join_paths(photos_datadir, 'doc', meson.project_name())
photos_libdir = join_paths(photos_prefix, get_option('libdir'), meson.project_name())
@@ -149,6 +150,16 @@ libgd = subproject(
)
libgd_dep = libgd.get_variable('libgd_dep')
+rawspeed = subproject(
+ 'rawspeed',
+ default_options: [
+ 'lto=true',
+ 'pkgdatadir=@0@'.format(photos_pkg_datadir),
+ '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 51f6bdf0..fed2f2ea 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -94,6 +94,7 @@ deps = [
libjpeg_dep,
libpng_dep,
m_dep,
+ rawspeed_dep,
]
libgnome_photos = shared_library(
diff --git a/subprojects/rawspeed/config.h.in b/subprojects/rawspeed/config.h.in
new file mode 100644
index 00000000..64cd94f8
--- /dev/null
+++ b/subprojects/rawspeed/config.h.in
@@ -0,0 +1,16 @@
+#mesondefine HAVE_CXX_THREAD_LOCAL
+#mesondefine HAVE_GCC_THREAD_LOCAL
+
+#mesondefine HAVE_POSIX_MEMALIGN
+#mesondefine HAVE_ALIGNED_ALLOC
+#mesondefine HAVE_MM_MALLOC
+#mesondefine HAVE_ALIGNED_MALLOC
+
+#mesondefine HAVE_JPEG
+#mesondefine HAVE_JPEG_MEM_SRC
+
+#mesondefine HAVE_OPENMP
+#define OMPFIRSTPRIVATECLAUSE(...) @ompfirstprivateclause@
+
+#mesondefine HAVE_PUGIXML
+#mesondefine HAVE_ZLIB
diff --git a/subprojects/rawspeed/data/README.md b/subprojects/rawspeed/data/README.md
new file mode 100644
index 00000000..edcccdf6
--- /dev/null
+++ b/subprojects/rawspeed/data/README.md
@@ -0,0 +1,138 @@
+# RawSpeed Camera Definition File
+
+The camera definition file is used to list the explicitly supported (or unsupported) cameras.
+
+WARNING: the format of that file is **not** stable.
+There is **no** backward (or forward) compatibility guarantees.
+Given RawSpeed code version `V`, only that very `cameras.xml` from that same
+version is guaranteed to work.
+`cameras.xml` from version `V+1` may not work.
+`cameras.xml` from version `V-1` may not work.
+
+```xml
+<Camera make="Panasonic" model="DMC-FZ45" mode="4:3" supported="yes" decoder_version="0">
+ <ID make="Panasonic" model="DMC-FZ45">Panasonic DMC-FZ45</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color><Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color><Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-58" height="-10"/>
+ <Sensor black="150" white="4097" iso_min="0" iso_max="0"/>
+ <BlackAreas>
+ <Vertical x="0" width="60"/>
+ <Horizontal y="2" height="46"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ <Aliases>
+ <Alias id="DMC-FZ40">DMC-FZ40</Alias>
+ </Aliases>
+</Camera>
+```
+
+Let’s go through it line for line:
+
+## Camera Name
+
+```xml
+<Camera make="Panasonic" model="DMC-FZ45" mode="4:3" supported="yes" decoder_version="0">
+```
+
+This the basic camera identification. In this the make and model are required. This must be exactly as
specified in the EXIF data of the file.
+
+Mode refers to specific decoder modes which are special for each manufacturer. For cameras with specific
modes there is usually a default (no mode specified) and some for non-default operation. For Canon for
instance mode refers to “sRaw1″ and “sRaw2″. For Panasonic it refers to cropping modes, since they require
different cropping of the output image.
+
+The supported tag specifies whether a camera is supported. If this tag isn’t added it is assumed to be
supported.
+
+The decoder_version is a possibility to disable decoding, if the decoder version is too old to properly
decode the images from this camera. If the code version of RawSpeed is too old to decode this camera type, it
will refuse to do so. If this isn’t specified it is assumed that all older versions of RawSpeed can decode
the image.
+
+## Camera ID
+
+```xml
+<ID make="Panasonic" model="DMC-FZ45">Panasonic DMC-FZ45</ID>
+```
+
+This sets the canonical name for the camera. The content of the tag should be the same as the
UniqueCameraModel DNG field in the Adobe DNG converted raw file and can be used to match the camera against
DCP files or other external references. The make and model attributes are clean names (no repetitions,
spurious words, etc) that can be used in UI. If the Alias tag is omitted the make and model from the Camera
tag are used instead (joined with a space for UniqueCameraModel), so in this particular case the tag is
actually not needed.
+
+## CFA Colors
+
+```xml
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color><Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color><Color x="1" y="1">GREEN</Color>
+ </CFA>
+```
+
+This refers to the color layout of the sensor. This is the position of the colors on the uncropped image, so
it will be the same no matter what crop you specify. Currently only 2×2 CFA patterns are possible.
+
+From version 2, there is an alternative syntax; *CFA2*. This definition allows for sizes *bigger than 2x2*
and has a simpler syntax:
+
+```xml
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+```
+Valid colors are:
+
+Colors are G(reen), R(ed), B(blue) , F(uji green), C(yan), M(agenta) and Y(ellow).
+
+## Image Cropping
+
+```xml
+ <Crop x="0" y="0" width="-58" height="-10"/>
+```
+
+This is the cropping to be applied to the image. x & y are specified relative to the top-left of the image
and are specified in pixels. Width & Height can be a number which is the desired output size in pixels. A
negative number for width or height specifies a number of pixels that must be cropped from the bottom/right
side of the image.
+
+## Sensor Info
+
+```xml
+ <Sensor black="150" white="4097" iso_min="0" iso_max="0"/>
+```
+
+This tag can be added more than 1 time, but at least 1 must be present.
+
+This specifies the black and white levels of images captured. Black and white must be specified. On cameras
(some Nikons for instance) and files (DNG images) where this can be read from the image files themselves this
is overridden.
+
+The iso_min and iso_max are optional which indicates an ISO range where this must be applied. If both are
set to 0, or left undefined they act as default values for all ISO values. Note that not all cameras may
decode the ISO value.
+
+Both ISO values are inclusive, so specify ranges so they don’t overlap (0->399, 400->799, etc). If different
entry ranges overlap the first match will be used.
+
+For backward compatibility, leave the default value as the last entry.
+
+## Sensor Black Areas
+
+```xml
+ <BlackAreas>
+ <Vertical x="0" width="60"/>
+ <Horizontal y="2" height="46"/>
+ </BlackAreas>
+```
+
+This entry specifies one or more “black” areas on the sensor. This is areas where the sensor receives no
light and it can therefore be used to accurately determine the black level of each image. The areas can be
described as a vertical area starting a fixed number of pixels from the left and having a fixed width, or a
horizontal, starting a fixed number of pixels down and having a fixed height.
+
+All the areas are summed up in a histogram for each color component, and the median value is selected as the
black value. This should ensure that noise and minor differences in hardware shouldn’t influence the
calculations.
+
+If any black areas are defined it will override any “black” value set in the Sensor definition.
+
+## Decoder Hints
+
+```xml
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+```
+
+This may contain manufacturer-specific hints for decoding. This can result in the code taking a specific
decoder path, or otherwise treat the image differently. This is mainly used when it isn’t possible to
determine which way to decode the image directly from the image data.
+
+## Camera Model Aliases
+
+```xml
+ <Aliases>
+ <Alias id="DMC-FZ40">DMC-FZ40</Alias>
+ </Aliases>
+```
+
+This is a possibility to add one or more model aliases for a camera, which may have different model names in
different regions. The id attribute specifies the clean model name for this alias, if omitted defaults to
alias value (so in this case is not really needed).
diff --git a/subprojects/rawspeed/data/cameras.xml b/subprojects/rawspeed/data/cameras.xml
new file mode 100644
index 00000000..ca3e3fd4
--- /dev/null
+++ b/subprojects/rawspeed/data/cameras.xml
@@ -0,0 +1,11901 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<?xml-stylesheet type="text/xsl" href="showcameras.xsl" ?>
+<!--
+ License: CC-BY-SA 3.0
+
+ This work is licensed under a
+ Creative Commons Attribution-ShareAlike 3.0 Unported License.
+
+ See more at:
+ http://creativecommons.org/licenses/by-sa/3.0/
+-->
+
+<!DOCTYPE Cameras [
+<!ELEMENT BlackAreas ( Vertical*, Horizontal* ) >
+
+<!ELEMENT Camera ( ID?, CFA?, CFA2?, Crop?, Sensor*, BlackAreas?, Aliases?, Hints? ) >
+<!ATTLIST Camera make CDATA #REQUIRED >
+<!ATTLIST Camera model CDATA #REQUIRED >
+<!ATTLIST Camera supported CDATA #IMPLIED >
+<!ATTLIST Camera mode CDATA #IMPLIED >
+<!ATTLIST Camera decoder_version CDATA #IMPLIED >
+
+<!ELEMENT Cameras ( Camera+ ) >
+<!ATTLIST Cameras version CDATA #IMPLIED >
+
+<!ELEMENT CFA ( Color+ ) >
+<!ATTLIST CFA height NMTOKEN #REQUIRED >
+<!ATTLIST CFA width NMTOKEN #REQUIRED >
+
+<!ELEMENT CFA2 ( Color*, ColorRow* ) >
+<!ATTLIST CFA2 height NMTOKEN #REQUIRED >
+<!ATTLIST CFA2 width NMTOKEN #REQUIRED >
+
+<!ELEMENT Color ( #PCDATA ) >
+<!ATTLIST Color x NMTOKEN #REQUIRED >
+<!ATTLIST Color y NMTOKEN #REQUIRED >
+
+<!ELEMENT ColorRow ( #PCDATA ) >
+<!ATTLIST ColorRow y NMTOKEN #REQUIRED >
+
+<!ELEMENT Crop EMPTY >
+<!ATTLIST Crop height NMTOKEN #REQUIRED >
+<!ATTLIST Crop width NMTOKEN #REQUIRED >
+<!ATTLIST Crop x NMTOKEN #REQUIRED >
+<!ATTLIST Crop y NMTOKEN #REQUIRED >
+
+<!ELEMENT Horizontal EMPTY >
+<!ATTLIST Horizontal height NMTOKEN #REQUIRED >
+<!ATTLIST Horizontal y NMTOKEN #REQUIRED >
+
+<!ELEMENT Sensor EMPTY >
+<!ATTLIST Sensor white NMTOKEN #REQUIRED >
+<!ATTLIST Sensor black NMTOKEN #REQUIRED >
+<!ATTLIST Sensor black_colors NMTOKEN #IMPLIED >
+<!ATTLIST Sensor iso_list NMTOKENS #IMPLIED >
+<!ATTLIST Sensor iso_min NMTOKEN #IMPLIED >
+<!ATTLIST Sensor iso_max NMTOKEN #IMPLIED >
+
+<!ELEMENT Vertical EMPTY >
+<!ATTLIST Vertical width NMTOKEN #REQUIRED >
+<!ATTLIST Vertical x NMTOKEN #REQUIRED >
+
+<!ELEMENT Hints ( Hint+ ) >
+<!ELEMENT Hint EMPTY >
+<!ATTLIST Hint name CDATA #REQUIRED >
+<!ATTLIST Hint value CDATA #REQUIRED >
+
+<!ELEMENT Aliases ( Alias+ ) >
+<!ATTLIST Alias id CDATA #IMPLIED >
+<!ELEMENT Alias (#PCDATA) >
+
+<!ELEMENT ID (#PCDATA) >
+<!ATTLIST ID make CDATA #REQUIRED >
+<!ATTLIST ID model CDATA #REQUIRED >
+]>
+
+<Cameras>
+ <Camera make="ARRI" model="ALEXA" supported="no"> <!-- no samples -->
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 100D">
+ <ID make="Canon" model="EOS 100D">Canon EOS 100D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="52" width="0" height="0"/>
+ <Sensor black="2048" white="15000"/>
+ <Sensor black="2047" white="12277" iso_list="100"/>
+ <Sensor black="2047" white="15000" iso_list="200 6400"/>
+ <Sensor black="2048" white="15000" iso_list="800 3200"/>
+ <Sensor black="2049" white="15000" iso_list="1600"/>
+ <Sensor black="2046" white="15000" iso_list="25600"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="8" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel SL1">Canon EOS REBEL SL1</Alias>
+ <Alias id="EOS Kiss X7">Canon EOS Kiss X7</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 200D">
+ <ID make="Canon" model="EOS 200D">Canon EOS 200D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2049" white="14338"/>
+ <Sensor black="511" white="11892" iso_list="100"/>
+ <Sensor black="511" white="14338" iso_list="200"/>
+ <Sensor black="2041" white="14338" iso_list="51200"/>
+ <BlackAreas>
+ <Vertical x="0" width="260"/>
+ <Horizontal y="0" height="38"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel SL2">Canon EOS Rebel SL2</Alias>
+ <Alias id="EOS Kiss X9">Canon EOS Kiss X9</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 300D DIGITAL">
+ <ID make="Canon" model="EOS 300D">Canon EOS 300D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="64" y="12" width="0" height="0"/>
+ <Sensor black="126" white="4000"/>
+ <Sensor black="127" white="4000" iso_list="200"/>
+ <Sensor black="128" white="4000" iso_list="400"/>
+ <Sensor black="129" white="4000" iso_list="800"/>
+ <Sensor black="251" white="4000" iso_list="1600"/>
+ <Sensor black="250" white="4000" iso_list="3200"/>
+ <BlackAreas>
+ <Vertical x="6" width="58"/>
+ <Horizontal y="4" height="8"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Digital Rebel">Canon EOS DIGITAL REBEL</Alias>
+ <Alias id="EOS Kiss Digital">Canon EOS Kiss Digital</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS D30">
+ <ID make="Canon" model="EOS D30">Canon EOS D30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="52" y="10" width="0" height="-6"/>
+ <Sensor black="126" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="46"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS D60">
+ <ID make="Canon" model="EOS D60">Canon EOS D60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="68" y="18" width="-4" height="-4"/>
+ <Sensor black="127" white="4000"/>
+ <BlackAreas>
+ <Vertical x="0" width="60"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 10D">
+ <ID make="Canon" model="EOS 10D">Canon EOS 10D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="64" y="12" width="0" height="0"/>
+ <Sensor black="127" white="4000"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 20D">
+ <ID make="Canon" model="EOS 20D">Canon EOS 20D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="12" width="3522" height="2348"/>
+ <Sensor black="126" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="2" height="8"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="50"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 30D">
+ <ID make="Canon" model="EOS 30D">Canon EOS 30D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="12" width="3522" height="2348"/>
+ <Sensor black="127" white="3398"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="2" height="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 350D DIGITAL">
+ <ID make="Canon" model="EOS 350D">Canon EOS 350D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="14" width="3474" height="2314"/>
+ <Sensor black="255" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="40"/>
+ <Horizontal y="0" height="12"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="Digital Rebel XT">Canon EOS DIGITAL REBEL XT</Alias>
+ <Alias id="Kiss Digital N">Canon EOS Kiss Digital N</Alias>
+ <Alias id="EOS 350D">Canon EOS 350D</Alias>
+ <Alias id="EOS 350D">Canon EOS 350D Digital</Alias>
+ </Aliases>
+ <Hints>
+ <Hint name="wb_offset" value="50"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 40D" decoder_version="2">
+ <ID make="Canon" model="EOS 40D">Canon EOS 40D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="30" y="18" width="3908" height="2602"/>
+ <Sensor black="1021" white="13600"/>
+ <BlackAreas>
+ <Vertical x="0" width="28"/>
+ <Horizontal y="4" height="12"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 40D" mode="sRaw1">
+ <ID make="Canon" model="EOS 40D">Canon EOS 40D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_40d" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 40D" mode="sRaw2">
+ <ID make="Canon" model="EOS 40D">Canon EOS 40D</ID>
+ <Crop x="0" y="0" width="1944" height="1296"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_40d" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 450D">
+ <ID make="Canon" model="EOS 450D">Canon EOS 450D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="22" y="18" width="4290" height="2856"/>
+ <Sensor black="1020" white="14500"/>
+ <Aliases>
+ <Alias id="Digital Rebel XSi">Canon EOS DIGITAL REBEL XSi</Alias>
+ <Alias id="Kiss Digital X2">Canon EOS Kiss Digital X2</Alias>
+ <Alias id="Kiss X2">Canon EOS Kiss X2</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 50D" decoder_version="1">
+ <ID make="Canon" model="EOS 50D">Canon EOS 50D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="64" y="52" width="4752" height="3158"/>
+ <Sensor black="1020" white="13653"/>
+ <BlackAreas>
+ <Vertical x="0" width="60"/>
+ <Horizontal y="2" height="46"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 50D" mode="sRaw1">
+ <ID make="Canon" model="EOS 50D">Canon EOS 50D</ID>
+ <Crop x="0" y="0" width="3272" height="2178"/>
+ <Sensor black="0" white="53000"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 50D" mode="sRaw2">
+ <ID make="Canon" model="EOS 50D">Canon EOS 50D</ID>
+ <Crop x="0" y="0" width="2376" height="1584"/>
+ <Sensor black="0" white="53000"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 60D" decoder_version="1">
+ <ID make="Canon" model="EOS 60D">Canon EOS 60D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="146" y="54" width="0" height="0"/>
+ <Sensor black="2026" white="14200"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="4" height="46"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 60D" mode="sRaw1">
+ <ID make="Canon" model="EOS 60D">Canon EOS 60D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 60D" mode="sRaw2">
+ <ID make="Canon" model="EOS 60D">Canon EOS 60D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 70D" decoder_version="1">
+ <ID make="Canon" model="EOS 70D">Canon EOS 70D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="38" width="0" height="0"/>
+ <Sensor black="2026" white="13653" iso_min="0" iso_max="199"/>
+ <Sensor black="2026" white="16383" iso_min="6400" iso_max="25600"/>
+ <Sensor black="2026" white="15387"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="0" height="38"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 70D" mode="sRaw1">
+ <ID make="Canon" model="EOS 70D">Canon EOS 70D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="53000"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 70D" mode="sRaw2">
+ <ID make="Canon" model="EOS 70D">Canon EOS 70D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="53000"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 80D">
+ <ID make="Canon" model="EOS 80D">Canon EOS 80D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-8" height="-4"/>
+ <Sensor black="512" white="11892" iso_list="100 125"/>
+ <Sensor black="512" white="10749" iso_list="160"/>
+ <Sensor black="511" white="14338" iso_list="200 250"/>
+ <Sensor black="2048" white="10749" iso_list="320 640 1250 2500 5000"/>
+ <Sensor black="2048" white="14338" iso_list="400 500 800 1000 1600 2000 3200 4000
6400 16000"/>
+ <Sensor black="2046" white="14338" iso_list="8000 25600"/>
+ <Sensor black="2045" white="14338" iso_list="10000"/>
+ <Sensor black="2049" white="14338" iso_list="12800"/>
+ <BlackAreas>
+ <Vertical x="0" width="263"/>
+ <Horizontal y="0" height="33"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 80D" mode="sRaw1" decoder_version="8">
+ <ID make="Canon" model="EOS 80D">Canon EOS 80D</ID>
+ <Crop x="28" y="10" width="0" height="0"/>
+ <Sensor black="0" white="48816" iso_list="320 640 1250 2500 5000"/>
+ <Sensor black="0" white="51936" iso_list="160"/>
+ <Sensor black="0" white="55680" iso_list="400 500 800 1000 1600 2000 3200 4000 6400
12800 16000 25600"/>
+ <Sensor black="0" white="55688" iso_list="8000 10000"/>
+ <Sensor black="0" white="58832" iso_list="100 125 200 250"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 80D" mode="sRaw2" decoder_version="7">
+ <ID make="Canon" model="EOS 80D">Canon EOS 80D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="48468" iso_list="2500"/>
+ <Sensor black="0" white="48488" iso_list="1250"/>
+ <Sensor black="0" white="48492" iso_list="320"/>
+ <Sensor black="0" white="48496" iso_list="640"/>
+ <Sensor black="0" white="48588" iso_list="5000"/>
+ <Sensor black="0" white="51932" iso_list="160"/>
+ <Sensor black="0" white="55680" iso_list="400 500 800 1000 1600 2000 3200 4000 6400
12800 16000 25600"/>
+ <Sensor black="0" white="55688" iso_list="8000 10000"/>
+ <Sensor black="0" white="58832" iso_list="100 125 200 250"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 700D">
+ <ID make="Canon" model="EOS 700D">Canon EOS 700D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="76" y="60" width="-6" height="-6"/>
+ <Sensor black="2052" white="15000"/>
+ <Sensor black="2048" white="12277" iso_list="100"/>
+ <Sensor black="2048" white="11222" iso_list="160 320 640 1250 2500 5000"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="4" height="50"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T5i">Canon EOS REBEL T5i</Alias>
+ <Alias id="EOS Kiss X7i">Canon EOS Kiss X7i</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 750D">
+ <ID make="Canon" model="EOS 750D">Canon EOS 750D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="34" width="0" height="0"/>
+ <Sensor black="2047" white="11765" iso_min="0" iso_max = "199"/>
+ <Sensor black="2047" white="14580"/>
+ <Aliases>
+ <Alias id="EOS Rebel T6i">Canon EOS Rebel T6i</Alias>
+ <Alias id="EOS Kiss X8i">Canon EOS Kiss X8i</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 800D">
+ <ID make="Canon" model="EOS 800D">Canon EOS 800D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2049" white="14338"/>
+ <Sensor black="511" white="11892" iso_list="100"/>
+ <Sensor black="511" white="14338" iso_list="200"/>
+ <BlackAreas>
+ <Vertical x="0" width="260"/>
+ <Horizontal y="2" height="36"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T7i">Canon EOS Rebel T7i</Alias>
+ <Alias id="EOS Kiss X9i">Canon EOS Kiss X9i</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 760D">
+ <ID make="Canon" model="EOS 760D">Canon EOS 760D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="34" width="0" height="0"/>
+ <Sensor black="2047" white="11765" iso_min="0" iso_max = "199"/>
+ <Sensor black="2047" white="14580"/>
+ <Aliases>
+ <Alias id="EOS Rebel T6s">Canon EOS Rebel T6s</Alias>
+ <Alias id="EOS 8000D">Canon EOS 8000D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 77D">
+ <ID make="Canon" model="EOS 77D">Canon EOS 77D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2048" white="14338"/>
+ <Sensor black="511" white="11892" iso_list="100"/>
+ <Sensor black="511" white="14338" iso_list="200"/>
+ <Sensor black="2037" white="14338" iso_list="51200"/>
+ <BlackAreas>
+ <Vertical x="0" width="260"/>
+ <Horizontal y="2" height="32"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS 9000D">Canon EOS 9000D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 500D">
+ <ID make="Canon" model="EOS 500D">Canon EOS 500D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="62" y="30" width="4722" height="3142"/>
+ <Sensor black="1020" white="16000"/>
+ <BlackAreas>
+ <Vertical x="0" width="56"/>
+ <Horizontal y="2" height="22"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T1i">Canon EOS REBEL T1i</Alias>
+ <Alias id="EOS Kiss X3">Canon EOS Kiss X3</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 550D">
+ <ID make="Canon" model="EOS 550D">Canon EOS 550D</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="1">GREEN</Color>
+ <Color x="0" y="1">RED</Color>
+ </CFA>
+ <Crop x="148" y="54" width="0" height="0"/>
+ <Sensor black="2048" white="15831"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T2i">Canon EOS REBEL T2i</Alias>
+ <Alias id="EOS Kiss X4">Canon EOS Kiss X4</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 600D">
+ <ID make="Canon" model="EOS 600D">Canon EOS 600D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="146" y="54" width="0" height="0"/>
+ <Sensor black="2026" white="13584" iso_min="0" iso_max = "199"/>
+ <Sensor black="2026" white="15304"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T3i">Canon EOS REBEL T3i</Alias>
+ <Alias id="EOS Kiss X5">Canon EOS Kiss X5</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 650D">
+ <ID make="Canon" model="EOS 650D">Canon EOS 650D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="76" y="56" width="0" height="0"/>
+ <Sensor black="2026" white="13584" iso_min="0" iso_max = "199"/>
+ <Sensor black="2026" white="15304"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T4i">Canon EOS REBEL T4i</Alias>
+ <Alias id="EOS Kiss X6i">Canon EOS Kiss X6i</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D">
+ <ID make="Canon" model="EOS 5D">Canon EOS 5D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="90" y="34" width="4386" height="2920"/>
+ <Sensor black="127" white="3692"/>
+ <BlackAreas>
+ <Vertical x="0" width="88"/>
+ <Horizontal y="2" height="30"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark II" decoder_version="1">
+ <ID make="Canon" model="EOS 5D Mark II">Canon EOS 5D Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="158" y="51" width="5634" height="3753"/>
+ <Sensor black="1024" white="12995" iso_list="160 320 640 1250"/>
+ <Sensor black="1024" white="15950"/>
+ <BlackAreas>
+ <Vertical x="0" width="156"/>
+ <Horizontal y="2" height="48"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark II" mode="sRaw1">
+ <ID make="Canon" model="EOS 5D Mark II">Canon EOS 5D Mark II</ID>
+ <Crop x="0" y="0" width="3872" height="2574"/>
+ <Sensor black="0" white="57200" iso_list="160 320 640 1250"/>
+ <Sensor black="0" white="64948"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark II" mode="sRaw2">
+ <ID make="Canon" model="EOS 5D Mark II">Canon EOS 5D Mark II</ID>
+ <Crop x="0" y="0" width="2808" height="1872"/>
+ <Sensor black="0" white="57200" iso_list="160 320 640 1250"/>
+ <Sensor black="0" white="64948"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark III" decoder_version="2">
+ <ID make="Canon" model="EOS 5D Mark III">Canon EOS 5D Mark III</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="124" y="82" width="-2" height="0"/>
+ <Sensor black="2060" white="16383"/>
+ <Sensor black="2060" white="15700" iso_list="400 500 1600"/>
+ <Sensor black="2060" white="15200" iso_list="100 125 200 250 800 2000"/>
+ <Sensor black="2060" white="15100" iso_list="2500 10000"/>
+ <Sensor black="2060" white="13700" iso_list="160 320 1250"/>
+ <Sensor black="2060" white="14200" iso_list="640 5000"/>
+ <BlackAreas>
+ <Vertical x="0" width="120"/>
+ <Horizontal y="2" height="78"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark III" mode="sRaw1">
+ <ID make="Canon" model="EOS 5D Mark III">Canon EOS 5D Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="44112" iso_list="160 320 640 1250 2500 5000 10000"/>
+ <Sensor black="0" white="50300" iso_list="100 20000"/>
+ <Sensor black="0" white="55128" iso_list="25600 51200 65535"/>
+ <Sensor black="0" white="53220"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark III" mode="sRaw2">
+ <ID make="Canon" model="EOS 5D Mark III">Canon EOS 5D Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="44112" iso_list="160 320 640 1250 2500 5000 10000"/>
+ <Sensor black="0" white="51300" iso_list="250"/>
+ <Sensor black="0" white="55028" iso_list="25600 51200 65535"/>
+ <Sensor black="0" white="53000"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark IV">
+ <ID make="Canon" model="EOS 5D Mark IV">Canon EOS 5D Mark IV</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="136" y="44" width="0" height="0"/>
+ <Sensor black="2049" white="14448"/>
+ <Sensor black="513" white="14008" iso_list="50 100 125"/>
+ <Sensor black="512" white="10838" iso_list="160"/>
+ <Sensor black="512" white="14448" iso_list="200 250"/>
+ <Sensor black="2048" white="10838" iso_list="320 640 1250 2500 5000 10000 20000"/>
+ <Sensor black="2049" white="13533" iso_list="102400"/>
+ <BlackAreas>
+ <Vertical x="0" width="134"/>
+ <Horizontal y="0" height="40"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark IV" mode="sRaw1">
+ <ID make="Canon" model="EOS 5D Mark IV">Canon EOS 5D Mark IV</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="56032"/>
+ <!-- FIXME: iso50? -->
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5D Mark IV" mode="sRaw2">
+ <ID make="Canon" model="EOS 5D Mark IV">Canon EOS 5D Mark IV</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="56032"/>
+ <!-- FIXME: iso50? -->
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS">Canon EOS 5DS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="164" y="72" width="-6" height="-8"/>
+ <Sensor black="2048" white="15181"/>
+ <Sensor black="2048" white="14466" iso_list="50 100"/>
+ <BlackAreas>
+ <Vertical x="0" width="150"/>
+ <Horizontal y="40" height="60"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS" mode="sRaw1" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS">Canon EOS 5DS</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="52000"/>
+ <Sensor black="0" white="46000" iso_list="50"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS" mode="sRaw2" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS">Canon EOS 5DS</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="52000"/>
+ <Sensor black="0" white="46000" iso_list="50"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS R" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS R">Canon EOS 5DS R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="164" y="72" width="-6" height="-8"/>
+ <Sensor black="2048" white="15181"/>
+ <Sensor black="2048" white="14466" iso_list="50 100"/>
+ <BlackAreas>
+ <Vertical x="0" width="150"/>
+ <Horizontal y="40" height="60"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS R" mode="sRaw1" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS R">Canon EOS 5DS R</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="52000"/>
+ <Sensor black="0" white="46000" iso_list="50"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 5DS R" mode="sRaw2" decoder_version="6">
+ <ID make="Canon" model="EOS 5DS R">Canon EOS 5DS R</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="52000"/>
+ <Sensor black="0" white="46000" iso_list="50"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 6D" decoder_version="2">
+ <ID make="Canon" model="EOS 6D">Canon EOS 6D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="40" width="0" height="-2"/>
+ <Sensor black="2048" white="15000"/>
+ <Sensor black="2047" white="15000" iso_list="50 100 125 200 250 16000"/>
+ <Sensor black="2047" white="13100" iso_list="160"/>
+ <Sensor black="2047" white="13000" iso_list="320"/>
+ <Sensor black="2048" white="13000" iso_list="640 1250 2500 5000 10000 20000"/>
+ <Sensor black="2048" white="16000" iso_list="51200 102400"/>
+ <BlackAreas>
+ <Vertical x="0" width="68"/>
+ <Horizontal y="2" height="36"/>
+ </BlackAreas>
+ </Camera>
+ <!---Guess -->
+ <Camera make="Canon" model="Canon EOS 6D" mode="sRaw1" decoder_version="3">
+ <ID make="Canon" model="EOS 6D">Canon EOS 6D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="48664"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <!---Guess -->
+ <Camera make="Canon" model="Canon EOS 6D" mode="sRaw2">
+ <ID make="Canon" model="EOS 6D">Canon EOS 6D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="48664"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 6D Mark II" decoder_version="9">
+ <ID make="Canon" model="EOS 6D Mark II">Canon EOS 6D Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="128" y="52" width="-4" height="-4"/>
+ <Sensor black="2049" white="14558"/>
+ <Sensor black="513" white="14558" iso_list="50 100 125 200 250"/>
+ <Sensor black="513" white="11301" iso_list="160"/>
+ <Sensor black="2049" white="11301" iso_list="320 640 1250 2500 5000 10000"/>
+ <BlackAreas>
+ <Vertical x="2" width="116"/>
+ <Horizontal y="8" height="32"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 6D Mark II" mode="sRaw1" decoder_version="9">
+ <ID make="Canon" model="EOS 6D Mark II">Canon EOS 6D Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="16383"/> <!-- don't know -->
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 6D Mark II" mode="sRaw2" decoder_version="9">
+ <ID make="Canon" model="EOS 6D Mark II">Canon EOS 6D Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="16383"/> <!-- don't know -->
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D" decoder_version="1">
+ <ID make="Canon" model="EOS 7D">Canon EOS 7D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="158" y="52" width="5202" height="3464"/>
+ <Sensor black="1025" white="8150" iso_min="12800" iso_max="12800"/>
+ <Sensor black="2050" white="16300"/>
+ <BlackAreas>
+ <Vertical x="8" width="156"/>
+ <Horizontal y="32" height="18"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D" mode="sRaw1">
+ <ID make="Canon" model="EOS 7D">Canon EOS 7D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="44200" iso_list="100 125 160 320 640 1250"/>
+ <Sensor black="0" white="54000"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D" mode="sRaw2">
+ <ID make="Canon" model="EOS 7D">Canon EOS 7D</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="44200" iso_list="100 125 160 320 640 1250"/>
+ <Sensor black="0" white="54000"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D Mark II">
+ <ID make="Canon" model="EOS 7D Mark II">Canon EOS 7D Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="38" width="0" height="0"/>
+ <Sensor black="2047" white="13400" iso_list="100 125 160 200 250 320"/>
+ <Sensor black="2047" white="15100"/>
+ <BlackAreas>
+ <Vertical x="4" width="64"/>
+ <Horizontal y="24" height="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D Mark II" mode="sRaw1">
+ <ID make="Canon" model="EOS 7D Mark II">Canon EOS 7D Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 7D Mark II" mode="sRaw2">
+ <ID make="Canon" model="EOS 7D Mark II">Canon EOS 7D Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 1000D">
+ <ID make="Canon" model="EOS 1000D">Canon EOS 1000D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="18" width="3906" height="2602"/>
+ <Sensor black="255" white="3650" iso_min="0" iso_max="199"/>
+ <Sensor black="255" white="4036"/>
+ <BlackAreas>
+ <Vertical x="0" width="41"/>
+ <Horizontal y="2" height="14"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Digital Rebel XS">Canon EOS DIGITAL REBEL XS</Alias>
+ <Alias id="EOS Kiss Digital F">Canon EOS Kiss Digital F</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 1100D">
+ <ID make="Canon" model="EOS 1100D">Canon EOS 1100D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="64" y="20" width="0" height="0"/>
+ <Sensor black="2036" white="15500"/>
+ <BlackAreas>
+ <Vertical x="0" width="58"/>
+ <Horizontal y="4" height="12"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T3">Canon EOS REBEL T3</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 1200D">
+ <ID make="Canon" model="EOS 1200D">Canon EOS 1200D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="146" y="54" width="0" height="0"/>
+ <Sensor black="2046" white="12279" iso_min="0" iso_max = "199"/>
+ <Sensor black="2046" white="15000"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T5">Canon EOS REBEL T5</Alias>
+ <Alias id="EOS Kiss X70">Canon EOS Kiss X70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 1300D">
+ <ID make="Canon" model="EOS 1300D">Canon EOS 1300D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="146" y="54" width="0" height="0"/>
+ <Sensor black="2048" white="15000"/>
+ <Sensor black="2046" white="12279" iso_list="100"/>
+ <Sensor black="2046" white="15000" iso_list="200"/>
+ <Sensor black="2047" white="15000" iso_list="3200"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T6">Canon EOS Rebel T6</Alias>
+ <Alias id="EOS Kiss X80">Canon EOS Kiss X80</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 2000D">
+ <ID make="Canon" model="EOS 2000D">Canon EOS 2000D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="78" y="42" width="-4" height="-6"/>
+ <Sensor black="2048" white="15092"/>
+ <Sensor black="2048" white="12277" iso_list="100"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="4" height="30"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T7">Canon EOS Rebel T7</Alias>
+ <Alias id="EOS 1500D">Canon EOS 1500D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 4000D">
+ <ID make="Canon" model="EOS 4000D">Canon EOS 4000D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="146" y="56" width="0" height="0"/>
+ <Sensor black="2051" white="15000"/>
+ <Sensor black="2046" white="12279" iso_list="100"/>
+ <BlackAreas>
+ <Vertical x="0" width="130"/>
+ <Horizontal y="4" height="40"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Rebel T100">Canon EOS Rebel T100</Alias>
+ <Alias id="EOS 3000D">Canon EOS 3000D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS 400D DIGITAL">
+ <ID make="Canon" model="EOS 400D">Canon EOS 400D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="18" width="3906" height="2602"/>
+ <Sensor black="255" white="3726"/>
+ <BlackAreas>
+ <Vertical x="0" width="40"/>
+ <Horizontal y="4" height="12"/>
+ </BlackAreas>
+ <Aliases>
+ <Alias id="EOS Digital Rebel XTi">Canon EOS DIGITAL REBEL XTi</Alias>
+ <Alias id="EOS Kiss Digital X">Canon EOS Kiss Digital X</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M">
+ <ID make="Canon" model="EOS M">Canon EOS M</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="76" y="56" width="0" height="0"/>
+ <Sensor black="2026" white="13584" iso_min="0" iso_max = "199"/>
+ <Sensor black="2026" white="15304"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M2">
+ <ID make="Canon" model="EOS M2">Canon EOS M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="52" width="0" height="0"/>
+ <Sensor black="2026" white="13584" iso_min="0" iso_max = "199"/>
+ <Sensor black="2026" white="15304"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="4" height="44"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M3">
+ <ID make="Canon" model="EOS M3">Canon EOS M3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="34" width="0" height="0"/>
+ <Sensor black="2048" white="16000"/>
+ <BlackAreas>
+ <Vertical x="2" width="68"/>
+ <Horizontal y="2" height="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M5">
+ <ID make="Canon" model="EOS M5">Canon EOS M5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="38" width="0" height="0"/>
+ <Sensor black="2048" white="16000"/>
+ <Sensor black="512" white="16000" iso_list="100 125 200 250"/>
+ <Sensor black="512" white="13200" iso_list="160"/>
+ <Sensor black="2048" white="13200" iso_list="320 640 1250 2500 5000"/>
+ <BlackAreas>
+ <Vertical x="4" width="260"/>
+ <Horizontal y="4" height="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M6">
+ <ID make="Canon" model="EOS M6">Canon EOS M6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2048" white="16000"/>
+ <Sensor black="512" white="16000" iso_list="100 125 200 250"/>
+ <Sensor black="512" white="13200" iso_list="160"/>
+ <Sensor black="2048" white="13200" iso_list="320 640 1250 2500 5000"/>
+ <BlackAreas>
+ <Vertical x="4" width="260"/>
+ <Horizontal y="4" height="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M10">
+ <ID make="Canon" model="EOS M10">Canon EOS M10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="72" y="52" width="0" height="-2"/>
+ <Sensor black="2048" white="16000"/>
+ <BlackAreas>
+ <Vertical x="2" width="68"/>
+ <Horizontal y="2" height="40"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS M100">
+ <ID make="Canon" model="EOS M100">Canon EOS M100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2048" white="16000"/>
+ <Sensor black="512" white="16000" iso_list="100 125 200 250"/>
+ <Sensor black="512" white="13200" iso_list="160"/>
+ <Sensor black="2048" white="13200" iso_list="320 640 1250 2500 5000"/>
+ <BlackAreas>
+ <Vertical x="4" width="260"/>
+ <Horizontal y="4" height="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D" decoder_version="8">
+ <ID make="Canon" model="EOS-1D">Canon EOS-1D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3588"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1DS" decoder_version="8">
+ <ID make="Canon" model="EOS-1Ds">Canon EOS-1Ds</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3500"/>
+ </Camera>
+ <Camera make="Canon" model="EOS D2000C" decoder_version="8">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="96" white="4095"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark II">
+ <ID make="Canon" model="EOS-1D Mark II">Canon EOS-1D Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="12" width="3522" height="2348"/>
+ <Sensor black="127" white="3700"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="2" height="8"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="68"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark II N">
+ <ID make="Canon" model="EOS-1D Mark II N">Canon EOS-1D Mark II N</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="12" width="3522" height="2348"/>
+ <Sensor black="127" white="3700"/>
+ <BlackAreas>
+ <Vertical x="0" width="72"/>
+ <Horizontal y="2" height="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark III" decoder_version="1">
+ <ID make="Canon" model="EOS-1D Mark III">Canon EOS-1D Mark III</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="78" y="20" width="3906" height="2600"/>
+ <Sensor black="1023" white="15100"/>
+ <BlackAreas>
+ <Vertical x="2" width="74"/>
+ <Horizontal y="4" height="14"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark III" mode="sRaw1">
+ <ID make="Canon" model="EOS-1D Mark III">Canon EOS-1D Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark III" mode="sRaw2">
+ <ID make="Canon" model="EOS-1D Mark III">Canon EOS-1D Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark IV" decoder_version="1">
+ <ID make="Canon" model="EOS-1D Mark IV">Canon EOS-1D Mark IV</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="144" y="46" width="-64" height="-2"/>
+ <Sensor black="2000" white="13000"/>
+ <BlackAreas>
+ <Vertical x="0" width="140"/>
+ <Horizontal y="26" height="16"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark IV" mode="sRaw1">
+ <ID make="Canon" model="EOS-1D Mark IV">Canon EOS-1D Mark IV</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D Mark IV" mode="sRaw2">
+ <ID make="Canon" model="EOS-1D Mark IV">Canon EOS-1D Mark IV</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1Ds Mark II">
+ <ID make="Canon" model="EOS-1Ds Mark II">Canon EOS-1Ds Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="98" y="13" width="5010" height="3336"/>
+ <Sensor black="126" white="4060"/>
+ <BlackAreas>
+ <Vertical x="0" width="96"/>
+ <Horizontal y="2" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="68"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1Ds Mark III" decoder_version="1">
+ <ID make="Canon" model="EOS-1Ds Mark III">Canon EOS-1Ds Mark III</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="62" y="20" width="5640" height="3752"/>
+ <Sensor black="1021" white="15100"/>
+ <BlackAreas>
+ <Vertical x="0" width="60"/>
+ <Horizontal y="4" height="14"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1Ds Mark III" mode="sRaw1">
+ <ID make="Canon" model="EOS-1Ds Mark III">Canon EOS-1Ds Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1Ds Mark III" mode="sRaw2">
+ <ID make="Canon" model="EOS-1Ds Mark III">Canon EOS-1Ds Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X" decoder_version="1">
+ <ID make="Canon" model="EOS-1D X">Canon EOS-1D X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="128" y="102" width="0" height="-2"/>
+ <Sensor black="2050" white="15100"/>
+ <BlackAreas>
+ <Vertical x="0" width="120"/>
+ <Horizontal y="0" height="98"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X" mode="sRaw1">
+ <ID make="Canon" model="EOS-1D X">Canon EOS-1D X</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X" mode="sRaw2">
+ <ID make="Canon" model="EOS-1D X">Canon EOS-1D X</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X Mark II">
+ <ID make="Canon" model="EOS-1D X Mark II">Canon EOS-1D X Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="74" y="40" width="-2" height="0"/>
+ <Sensor black="2048" white="14888"/>
+ <Sensor black="512" white="13898" iso_list="50 100"/>
+ <Sensor black="512" white="14888" iso_list="125 160 200 250"/>
+ <Sensor black="2049" white="14888" iso_list="1600 2500 51200"/>
+ <Sensor black="2050" white="13533" iso_list="102400"/>
+ <Sensor black="2048" white="13533" iso_list="204800"/>
+ <Sensor black="2049" white="13533" iso_list="409600"/>
+ <BlackAreas>
+ <Vertical x="0" width="68"/>
+ <Horizontal y="0" height="36"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X Mark II" mode="sRaw1">
+ <ID make="Canon" model="EOS-1D X Mark II">Canon EOS-1D X Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon EOS-1D X Mark II" mode="sRaw2">
+ <ID make="Canon" model="EOS-1D X Mark II">Canon EOS-1D X Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="sraw_new" value=""/>
+ <Hint name="invert_sraw_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot Pro1">
+ <ID make="Canon" model="PowerShot Pro1">Canon PowerShot Pro1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="4" y="6" width="-52" height="-6"/>
+ <Sensor black="129" white="4095"/>
+ <BlackAreas>
+ <Vertical x="3294" width="50"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="96"/>
+ <Hint name="wb_mangle" value="true"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot Pro70" supported="no">
+ <ID make="Canon" model="PowerShot Pro70">Canon PowerShot Pro70</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G1">
+ <ID make="Canon" model="PowerShot G1">Canon PowerShot G1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">FUJI_GREEN</Color>
+ <Color x="1" y="0">MAGENTA</Color>
+ <Color x="0" y="1">YELLOW</Color>
+ <Color x="1" y="1">CYAN</Color>
+ </CFA>
+ <Crop x="4" y="8" width="-52" height="-2"/>
+ <Sensor black="31" white="1023"/>
+ <BlackAreas>
+ <Vertical x="2094" width="50"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="no_decompressed_lowbits" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G2">
+ <ID make="Canon" model="PowerShot G2">Canon PowerShot G2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-52" height="-2"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="2326" width="50"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="no_decompressed_lowbits" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G3">
+ <ID make="Canon" model="PowerShot G3">Canon PowerShot G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-52" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="2326" width="50"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G3 X">
+ <ID make="Canon" model="PowerShot G3 X">Canon PowerShot G3 X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="17" width="0" height="0"/>
+ <Sensor black="2047" white="16000"/>
+ <BlackAreas>
+ <Vertical x="0" width="90"/>
+ <Horizontal y="0" height="16"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G5">
+ <ID make="Canon" model="PowerShot G5">Canon PowerShot G5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-44" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="2632" width="40"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G5 X">
+ <ID make="Canon" model="PowerShot G5 X">Canon PowerShot G5 X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="17" width="0" height="0"/>
+ <Sensor black="2047" white="16000"/>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G6">
+ <ID make="Canon" model="PowerShot G6">Canon PowerShot G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="44" y="12" width="-4" height="-4"/>
+ <Sensor black="128" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="40"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="96"/>
+ <Hint name="wb_mangle" value="true"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G7 X">
+ <ID make="Canon" model="PowerShot G7 X">Canon PowerShot G7 X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="18" width="0" height="0"/>
+ <Sensor black="511" white="4000"/>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G7 X Mark II">
+ <ID make="Canon" model="PowerShot G7 X Mark II">Canon PowerShot G7 X Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="22" width="0" height="0"/>
+ <Sensor black="2047" white="16000"/>
+ <Sensor black="2046" white="16000" iso_list="1600 2000 2500"/>
+ <Sensor black="2048" white="16000" iso_list="3200 4000"/>
+ <Sensor black="2049" white="16000" iso_list="5000 6400 8000"/>
+ <Sensor black="2050" white="16000" iso_list="10000 12800"/>
+ <BlackAreas>
+ <Vertical x="0" width="76"/>
+ <Horizontal y="0" height="14"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G1 X">
+ <ID make="Canon" model="PowerShot G1 X">Canon PowerShot G1 X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="82" y="52" width="-14" height="0"/>
+ <Sensor black="0" white="16383"/>
+ <BlackAreas>
+ <Vertical x="0" width="68"/>
+ <Horizontal y="0" height="46"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G1 X Mark II">
+ <ID make="Canon" model="PowerShot G1 X Mark II">Canon PowerShot G1 X Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="82" y="52" width="-14" height="0"/>
+ <Sensor black="0" white="16000"/>
+ <BlackAreas>
+ <Vertical x="0" width="68"/>
+ <Horizontal y="0" height="46"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G1 X Mark III">
+ <ID make="Canon" model="PowerShot G1 X Mark III">Canon PowerShot G1 X Mark III</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="266" y="42" width="-6" height="-4"/>
+ <Sensor black="2048" white="16000"/>
+ <Sensor black="512" white="16000" iso_list="100 125 200 250"/>
+ <Sensor black="512" white="13200" iso_list="160"/>
+ <Sensor black="2048" white="13200" iso_list="320 640 1250 2500 5000"/>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G12">
+ <ID make="Canon" model="PowerShot G12">Canon PowerShot G12</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="54" y="14" width="-12" height="-18"/>
+ <Sensor black="120" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="50"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G11">
+ <ID make="Canon" model="PowerShot G11">Canon PowerShot G11</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="10" y="18" width="-56" height="-14"/>
+ <Sensor black="120" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="10"/>
+ <Vertical x="3696" width="48"/>
+ <Horizontal y="0" height="14"/>
+ <Horizontal y="2778" height="6"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G10">
+ <ID make="Canon" model="PowerShot G10">Canon PowerShot G10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="12" y="13" width="4432" height="3323"/>
+ <Sensor black="128" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="10"/>
+ <Horizontal y="0" height="8"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G9">
+ <ID make="Canon" model="PowerShot G9">Canon PowerShot G9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="48" y="12" width="4032" height="3024"/>
+ <Sensor black="125" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="46"/>
+ <Horizontal y="0" height="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G9 X">
+ <ID make="Canon" model="PowerShot G9 X">Canon PowerShot G9 X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="18" width="0" height="0"/>
+ <Sensor black="2047" white="16000"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G9 X Mark II">
+ <ID make="Canon" model="PowerShot G9 X Mark II">Canon PowerShot G9 X Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="104" y="22" width="-6" height="-6"/>
+ <Sensor black="2048" white="16000"/>
+ <BlackAreas>
+ <Vertical x="0" width="76"/>
+ <Horizontal y="0" height="14"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G15">
+ <ID make="Canon" model="PowerShot G15">Canon PowerShot G15</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="106" y="12" width="-10" height="-66"/>
+ <Sensor black="500" white="2800" iso_min="12800" iso_max="12800"/>
+ <Sensor black="128" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="100"/>
+ <Horizontal y="3062" height="60"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot G16" decoder_version="4">
+ <ID make="Canon" model="PowerShot G16">Canon PowerShot G16</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="96" y="18" width="-24" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="80"/>
+ <Horizontal y="0" height="16"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX1 IS">
+ <ID make="Canon" model="PowerShot SX1 IS">Canon PowerShot SX1 IS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="192" y="12" width="3958" height="2760"/>
+ <Sensor black="125" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="188"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S30">
+ <ID make="Canon" model="PowerShot S30">Canon PowerShot S30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="4" y="8" width="-52" height="-2"/>
+ <Sensor black="31" white="1023"/>
+ <BlackAreas>
+ <Vertical x="2094" width="50"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="no_decompressed_lowbits" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S40">
+ <ID make="Canon" model="PowerShot S40">Canon PowerShot S40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-52" height="-2"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="2326" width="50"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="no_decompressed_lowbits" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S45">
+ <ID make="Canon" model="PowerShot S45">Canon PowerShot S45</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-52" height="-2"/>
+ <Sensor black="129" white="4095"/>
+ <BlackAreas>
+ <Vertical x="2326" width="50"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S50">
+ <ID make="Canon" model="PowerShot S50">Canon PowerShot S50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-44" height="-2"/>
+ <Sensor black="129" white="4095"/>
+ <BlackAreas>
+ <Vertical x="2632" width="40"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S60">
+ <ID make="Canon" model="PowerShot S60">Canon PowerShot S60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="12" y="6" width="-44" height="-2"/>
+ <Sensor black="129" white="4095"/>
+ <BlackAreas>
+ <Vertical x="2632" width="40"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="96"/>
+ <Hint name="wb_mangle" value="true"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S70">
+ <ID make="Canon" model="PowerShot S70">Canon PowerShot S70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="44" y="12" width="-4" height="-4"/>
+ <Sensor black="129" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="40"/>
+ <Horizontal y="0" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="96"/>
+ <Hint name="wb_mangle" value="true"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S90">
+ <ID make="Canon" model="PowerShot S90">Canon PowerShot S90</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="10" y="18" width="-54" height="-10"/>
+ <Sensor black="125" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="6"/>
+ <Horizontal y="0" height="14"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S95">
+ <ID make="Canon" model="PowerShot S95">Canon PowerShot S95</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="66" y="24" width="-20" height="-24"/>
+ <Sensor black="125" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="50"/>
+ <Vertical x="3738" width="6"/>
+ <Horizontal y="0" height="10"/>
+ <Horizontal y="2774" height="10"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S100">
+ <ID make="Canon" model="PowerShot S100">Canon PowerShot S100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="104" y="12" width="-10" height="-66"/>
+ <Sensor black="125" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="100"/>
+ <Horizontal y="0" height="10"/>
+ <Horizontal y="3062" height="60"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S110">
+ <ID make="Canon" model="PowerShot S110">Canon PowerShot S110</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="106" y="12" width="-10" height="-66"/>
+ <Sensor black="500" white="3072" iso_min="12800" iso_max="12800"/>
+ <Sensor black="128" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="100"/>
+ <Horizontal y="3062" height="60"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot S120">
+ <ID make="Canon" model="PowerShot S120">Canon PowerShot S120</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="121" y="30" width="-47" height="-14"/>
+ <Sensor black="0" white="4000"/>
+ <BlackAreas>
+ <Vertical x="0" width="74"/>
+ <Horizontal y="0" height="12"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX50 HS">
+ <ID make="Canon" model="PowerShot SX50 HS">Canon PowerShot SX50 HS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="100" y="20" width="-10" height="0"/>
+ <Sensor black="127" white="4095"/>
+ <BlackAreas>
+ <Vertical x="6" width="70"/>
+ <Horizontal y="0" height="16"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX60 HS">
+ <ID make="Canon" model="PowerShot SX60 HS">Canon PowerShot SX60 HS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="112" y="26" width="-24" height="-10"/>
+ <Sensor black="128" white="4000"/>
+ <BlackAreas>
+ <Vertical x="0" width="70"/>
+ <Horizontal y="0" height="16"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="wb_offset" value="142"/>
+ </Hints>
+ </Camera>
+ <Camera make="GoPro" model="FUSION" mode="dng">
+ <ID make="GoPro" model="FUSION">GoPro FUSION</ID>
+ </Camera>
+ <Camera make="GoPro" model="HERO5 Black" mode="dng">
+ <ID make="GoPro" model="HERO5 Black">GoPro HERO5 Black</ID>
+ </Camera>
+ <Camera make="GoPro" model="HERO6 Black" mode="dng">
+ <ID make="GoPro" model="HERO6 Black">GoPro HERO6 Black</ID>
+ </Camera>
+ <Camera make="GoPro" model="HERO7 Black" mode="dng">
+ <ID make="GoPro" model="HERO7 Black">GoPro HERO7 Black</ID>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D100" mode="12bit-compressed">
+ <ID make="Nikon" model="D100">Nikon D100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="2" y="0" width="3030" height="2024"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D100" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D100">Nikon D100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="2" y="0" width="3030" height="2024"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D1" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D1">Nikon D1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="2000" height="1312"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="nikon_wb_adjustment" value="true"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D1H" mode="12bit-compressed">
+ <ID make="Nikon" model="D1H">Nikon D1H</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="2012" height="1324"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D1H" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D1H">Nikon D1H</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="2012" height="1324"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D1X" mode="12bit-compressed">
+ <ID make="Nikon" model="D1X">Nikon D1X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4024" height="1324"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="pixel_aspect_ratio" value="0.5"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D1X" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D1X">Nikon D1X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4024" height="1324"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="pixel_aspect_ratio" value="0.5"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D200" mode="12bit-compressed">
+ <ID make="Nikon" model="D200">Nikon D200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="2" y="0" width="3898" height="2616"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D200" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D200">Nikon D200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="2" y="0" width="3898" height="2616"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2H" mode="12bit-compressed">
+ <ID make="Nikon" model="D2H">Nikon D2H</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-8" height="1648"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2H" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D2H">Nikon D2H</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-8" height="1648"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2Hs" mode="12bit-compressed">
+ <ID make="Nikon" model="D2Hs">Nikon D2Hs</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-8" height="1648"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2Hs" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D2Hs">Nikon D2Hs</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-8" height="1648"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2X" mode="12bit-compressed">
+ <ID make="Nikon" model="D2X">Nikon D2X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="-8"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2X" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D2X">Nikon D2X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="-8"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2Xs" mode="12bit-compressed">
+ <ID make="Nikon" model="D2Xs">Nikon D2Xs</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D2Xs" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D2Xs">Nikon D2Xs</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3" mode="14bit-compressed">
+ <ID make="Nikon" model="D3">Nikon D3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D3">Nikon D3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3" mode="12bit-compressed">
+ <ID make="Nikon" model="D3">Nikon D3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D3">Nikon D3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3S" mode="14bit-compressed">
+ <ID make="Nikon" model="D3S">Nikon D3S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3S" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D3S">Nikon D3S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3S" mode="12bit-compressed">
+ <ID make="Nikon" model="D3S">Nikon D3S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3S" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D3S">Nikon D3S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3X" mode="14bit-compressed">
+ <ID make="Nikon" model="D3X">Nikon D3X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4044"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3X" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D3X">Nikon D3X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4044"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3X" mode="12bit-compressed">
+ <ID make="Nikon" model="D3X">Nikon D3X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4044"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3X" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D3X">Nikon D3X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4044"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300" mode="14bit-compressed">
+ <ID make="Nikon" model="D300">Nikon D300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="15236"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D300">Nikon D300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="15236"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300" mode="12bit-compressed">
+ <ID make="Nikon" model="D300">Nikon D300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="3808"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D300">Nikon D300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="3808"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300S" mode="14bit-compressed">
+ <ID make="Nikon" model="D300S">Nikon D300S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="15236"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300S" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D300S">Nikon D300S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="15236"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300S" mode="12bit-compressed">
+ <ID make="Nikon" model="D300S">Nikon D300S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="3808"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D300S" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D300S">Nikon D300S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4320" height="2868"/>
+ <Sensor black="0" white="3808"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3000" mode="12bit-compressed">
+ <ID make="Nikon" model="D3000">Nikon D3000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3898" height="2610"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3200" mode="12bit-compressed">
+ <ID make="Nikon" model="D3200">Nikon D3200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3300" mode="12bit-compressed">
+ <ID make="Nikon" model="D3300">Nikon D3300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-8" height="-8"/>
+ <Sensor black="150" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3300" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D3300">Nikon D3300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-8" height="-8"/>
+ <Sensor black="150" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3400" mode="12bit-compressed">
+ <ID make="Nikon" model="D3400">Nikon D3400</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3500" mode="12bit-compressed">
+ <ID make="Nikon" model="D3500">Nikon D3500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4" mode="14bit-compressed">
+ <ID make="Nikon" model="D4">Nikon D4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D4">Nikon D4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4" mode="12bit-compressed">
+ <ID make="Nikon" model="D4">Nikon D4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D4">Nikon D4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5" mode="14bit-compressed">
+ <ID make="Nikon" model="D5">Nikon D5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="400" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D5">Nikon D5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="400" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5" mode="12bit-compressed">
+ <ID make="Nikon" model="D5">Nikon D5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="100" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D5">Nikon D5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="100" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 6" mode="14bit-compressed">
+ <ID make="Nikon" model="Z 6">Nikon Z 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1008" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 6" mode="14bit-uncompressed">
+ <ID make="Nikon" model="Z 6">Nikon Z 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1008" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 6" mode="12bit-compressed">
+ <ID make="Nikon" model="Z 6">Nikon Z 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="251" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 6" mode="12bit-uncompressed">
+ <ID make="Nikon" model="Z 6">Nikon Z 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="251" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 7" mode="14bit-compressed">
+ <ID make="Nikon" model="Z 7">Nikon Z 7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1008" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 7" mode="14bit-uncompressed">
+ <ID make="Nikon" model="Z 7">Nikon Z 7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1008" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 7" mode="12bit-compressed">
+ <ID make="Nikon" model="Z 7">Nikon Z 7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="252" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Z 7" mode="12bit-uncompressed">
+ <ID make="Nikon" model="Z 7">Nikon Z 7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="252" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Df" mode="14bit-compressed">
+ <ID make="Nikon" model="Df">Nikon Df</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ <BlackAreas>
+ <Vertical x="4984" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Df" mode="14bit-uncompressed">
+ <ID make="Nikon" model="Df">Nikon Df</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ <BlackAreas>
+ <Vertical x="4984" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Df" mode="12bit-compressed">
+ <ID make="Nikon" model="Df">Nikon Df</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ <BlackAreas>
+ <Vertical x="4984" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON Df" mode="12bit-uncompressed">
+ <ID make="Nikon" model="Df">Nikon Df</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ <BlackAreas>
+ <Vertical x="4984" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5100" mode="14bit-compressed" decoder_version="2">
+ <ID make="Nikon" model="D5100">Nikon D5100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-46" height="0"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5100" mode="14bit-uncompressed" decoder_version="2">
<!-- the camera lies, see https://redmine.darktable.org/issues/11268 -->
+ <ID make="Nikon" model="D5100">Nikon D5100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-46" height="0"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D3100" mode="12bit-compressed" decoder_version="2">
+ <ID make="Nikon" model="D3100">Nikon D3100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="12" y="2" width="-26" height="-6"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D40" mode="12bit-compressed">
+ <ID make="Nikon" model="D40">Nikon D40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3038" height="2014"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D40" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D40">Nikon D40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3038" height="2014"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D40X" mode="12bit-compressed">
+ <ID make="Nikon" model="D40X">Nikon D40X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="4" y="0" width="3896" height="2613"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D40X" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D40X">Nikon D40X</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="4" y="0" width="3896" height="2613"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D50" mode="12bit-compressed">
+ <ID make="Nikon" model="D50">Nikon D50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-2" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D500" mode="14bit-compressed">
+ <ID make="Nikon" model="D500">Nikon D500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="2" width="-2" height="-2"/>
+ <Sensor black="400" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D500" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D500">Nikon D500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="2" width="-2" height="-2"/>
+ <Sensor black="400" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D500" mode="12bit-compressed">
+ <ID make="Nikon" model="D500">Nikon D500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="2" width="-2" height="-2"/>
+ <Sensor black="100" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D500" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D500">Nikon D500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="2" width="-2" height="-2"/>
+ <Sensor black="100" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5000" mode="12bit-compressed">
+ <ID make="Nikon" model="D5000">Nikon D5000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4310" height="2868"/>
+ <Sensor black="0" white="3767"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5000" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D5000">Nikon D5000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4310" height="2868"/>
+ <Sensor black="0" white="3767"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5200" mode="14bit-compressed">
+ <ID make="Nikon" model="D5200">Nikon D5200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5300" mode="12bit-compressed">
+ <ID make="Nikon" model="D5300">Nikon D5300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5300" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D5300">Nikon D5300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5300" mode="14bit-compressed">
+ <ID make="Nikon" model="D5300">Nikon D5300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5300" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D5300">Nikon D5300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5500" mode="12bit-compressed">
+ <ID make="Nikon" model="D5500">Nikon D5500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5500" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D5500">Nikon D5500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5500" mode="14bit-compressed">
+ <ID make="Nikon" model="D5500">Nikon D5500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5500" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D5500">Nikon D5500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5600" mode="12bit-compressed">
+ <ID make="Nikon" model="D5600">Nikon D5600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5600" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D5600">Nikon D5600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5600" mode="14bit-compressed">
+ <ID make="Nikon" model="D5600">Nikon D5600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D5600" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D5600">Nikon D5600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D60" mode="12bit-compressed">
+ <ID make="Nikon" model="D60">Nikon D60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2613"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D60" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D60">Nikon D60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2613"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D600" mode="14bit-compressed">
+ <ID make="Nikon" model="D600">Nikon D600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D600" mode="12bit-compressed">
+ <ID make="Nikon" model="D600">Nikon D600</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D610" mode="14bit-compressed">
+ <ID make="Nikon" model="D610">Nikon D610</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D610" mode="12bit-compressed">
+ <ID make="Nikon" model="D610">Nikon D610</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-50" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D70" mode="12bit-compressed">
+ <ID make="Nikon" model="D70">Nikon D70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3038" height="2014"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D700" mode="12bit-compressed">
+ <ID make="Nikon" model="D700">Nikon D700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D700" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D700">Nikon D700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D700" mode="14bit-compressed">
+ <ID make="Nikon" model="D700">Nikon D700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D700" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D700">Nikon D700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="4282" height="2844"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D750" mode="12bit-compressed">
+ <ID make="Nikon" model="D750">Nikon D750</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D750" mode="14bit-compressed">
+ <ID make="Nikon" model="D750">Nikon D750</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7000" mode="14bit-compressed">
+ <ID make="Nikon" model="D7000">Nikon D7000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-46" height="-2"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7000" mode="12bit-compressed">
+ <ID make="Nikon" model="D7000">Nikon D7000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-46" height="-2"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7100" mode="14bit-compressed">
+ <ID make="Nikon" model="D7100">Nikon D7100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7100" mode="12bit-compressed">
+ <ID make="Nikon" model="D7100">Nikon D7100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7200" mode="14bit-compressed">
+ <ID make="Nikon" model="D7200">Nikon D7200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7200" mode="14bit-uncompressed" supported="no">
+ <ID make="Nikon" model="D7200">Nikon D7200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15892"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7200" mode="12bit-compressed">
+ <ID make="Nikon" model="D7200">Nikon D7200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7200" mode="12bit-uncompressed" supported="no">
+ <ID make="Nikon" model="D7200">Nikon D7200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3972"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7500" mode="12bit-compressed">
+ <ID make="Nikon" model="D7500">Nikon D7500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="100" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D7500" mode="14bit-compressed">
+ <ID make="Nikon" model="D7500">Nikon D7500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="400" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D70s" mode="12bit-compressed">
+ <ID make="Nikon" model="D70s">Nikon D70s</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3040" height="2014"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D80" mode="12bit-compressed">
+ <ID make="Nikon" model="D80">Nikon D80</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2611"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D80" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D80">Nikon D80</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2611"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800" mode="7424x4924-14bit-uncompressed">
+ <ID make="Nikon" model="D800">Nikon D800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800" mode="14bit-compressed">
+ <ID make="Nikon" model="D800">Nikon D800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D800">Nikon D800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800" mode="12bit-compressed">
+ <ID make="Nikon" model="D800">Nikon D800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D800">Nikon D800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800E" mode="7424x4924-14bit-uncompressed">
+ <ID make="Nikon" model="D800E">Nikon D800E</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800E" mode="14bit-compressed">
+ <ID make="Nikon" model="D800E">Nikon D800E</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800E" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D800E">Nikon D800E</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800E" mode="12bit-compressed">
+ <ID make="Nikon" model="D800E">Nikon D800E</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D800E" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D800E">Nikon D800E</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="2" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D810" mode="sNEF-uncompressed">
+ <ID make="Nikon" model="D810">Nikon D810</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D810" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D810">Nikon D810</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D810" mode="12bit-compressed">
+ <ID make="Nikon" model="D810">Nikon D810</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D810" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D810">Nikon D810</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D810" mode="14bit-compressed">
+ <ID make="Nikon" model="D810">Nikon D810</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="600" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D850" mode="12bit-compressed">
+ <ID make="Nikon" model="D850">Nikon D850</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="100" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D850" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D850">Nikon D850</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="100" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D850" mode="14bit-compressed">
+ <ID make="Nikon" model="D850">Nikon D850</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="400" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D850" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D850">Nikon D850</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="400" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4S" mode="sNEF-uncompressed">
+ <ID make="Nikon" model="D4S">Nikon D4S</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4S" mode="12bit-compressed">
+ <ID make="Nikon" model="D4S">Nikon D4S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="192" white="3880"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4S" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D4S">Nikon D4S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="192" white="3880"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4S" mode="14bit-uncompressed">
+ <ID make="Nikon" model="D4S">Nikon D4S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="768" white="15520"/>
+ <Hints>
+ <Hint name="msb_override" value=""/>
+
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D4S" mode="14bit-compressed">
+ <ID make="Nikon" model="D4S">Nikon D4S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="768" white="15520"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D90" mode="12bit-compressed">
+ <ID make="Nikon" model="D90">Nikon D90</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4310" height="2868"/>
+ <Sensor black="0" white="3767"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON D90" mode="12bit-uncompressed">
+ <ID make="Nikon" model="D90">Nikon D90</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4310" height="2868"/>
+ <Sensor black="0" white="3767"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J1" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 J1">Nikon 1 J1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="0" white="3300"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J2" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 J2">Nikon 1 J2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J3" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 J3">Nikon 1 J3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J4" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 J4">Nikon 1 J4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J5" mode="12bit-compressed">
+ <ID make="Nikon" model="1 J5">Nikon 1 J5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="3800"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 J5" mode="12bit-uncompressed">
+ <ID make="Nikon" model="1 J5">Nikon 1 J5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="3800"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 S1" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 S1">Nikon 1 S1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="0" white="3300"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 S2" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 S2">Nikon 1 S2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 V1" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 V1">Nikon 1 V1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="0" white="3300"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 V2" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 V2">Nikon 1 V2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 V3" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 V3">Nikon 1 V3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 V3" mode="12bit-uncompressed" decoder_version="4">
+ <ID make="Nikon" model="1 V3">Nikon 1 V3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="NIKON 1 AW1" mode="12bit-compressed" decoder_version="4">
+ <ID make="Nikon" model="1 AW1">Nikon 1 AW1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Sensor black="0" white="3300" iso_list="160"/>
+ </Camera>
+ <Camera make="NIKON" model="E5400" mode="12bit-uncompressed" decoder_version="3">
+ <ID make="Nikon" model="E5400">Nikon E5400</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="2608" height="1950"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="E5700" mode="12bit-uncompressed" decoder_version="4">
+ <ID make="Nikon" model="E5700">Nikon E5700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">FUJI_GREEN</Color>
+ <Color x="1" y="0">MAGENTA</Color>
+ <Color x="0" y="1">YELLOW</Color>
+ <Color x="1" y="1">CYAN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="2576" height="1924"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="E8400" mode="12bit-compressed" decoder_version="3" supported="no"> <!--
need raw sample for whitelevel -->
+ <ID make="Nikon" model="E8400">Nikon E8400</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3280" height="2454"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="E8400" mode="12bit-uncompressed" decoder_version="3" supported="no"> <!--
need raw sample for whitelevel -->
+ <ID make="Nikon" model="E8400">Nikon E8400</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3280" height="2454"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P330" mode="12bit-compressed" decoder_version="5">
+ <ID make="Nikon" model="Coolpix P330">Nikon Coolpix P330</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="3200" white="65000"/>
+ <Hints>
+ <Hint name="force_uncompressed" value=""/>
+ <Hint name="real_bpp" value="16"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P340" mode="12bit-compressed" decoder_version="5">
+ <ID make="Nikon" model="Coolpix P340">Nikon Coolpix P340</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="3800"/>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P340" mode="12bit-uncompressed" decoder_version="5">
+ <ID make="Nikon" model="Coolpix P340">Nikon Coolpix P340</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="3800"/>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="COOLPIX A" mode="14bit-compressed">
+ <ID make="Nikon" model="Coolpix A">Nikon Coolpix A</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="0" white="15892"/>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX B700">
+ <ID make="Nikon" model="COOLPIX B700">Nikon Coolpix B700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX B700" mode="12bit-uncompressed">
+ <ID make="Nikon" model="COOLPIX B700">Nikon Coolpix B700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P6000" mode="12bit-uncompressed" decoder_version="1">
+ <ID make="Nikon" model="Coolpix P6000">Nikon Coolpix P6000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P7000" mode="12bit-uncompressed" decoder_version="1">
+ <ID make="Nikon" model="Coolpix P7000">Nikon Coolpix P7000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Sensor black="255" white="4095" iso_min="400" iso_max="3200"/>
+ <!-- In usual Nikon style, ISO 6400 is marked as ISO 0 -->
+ <Sensor black="255" white="4095" iso_min="0" iso_max="1"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P7100" mode="12bit-uncompressed" decoder_version="1">
+ <ID make="Nikon" model="Coolpix P7100">Nikon Coolpix P7100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3800"/>
+ <Sensor black="250" white="3800" iso_min="400" iso_max="0"/>
+ <!-- In usual Nikon style, ISO 6400 is marked as ISO 0 -->
+ <Sensor black="260" white="3800" iso_min="0" iso_max="1"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P7700" mode="12bit-compressed" decoder_version="5">
+ <ID make="Nikon" model="Coolpix P7700">Nikon Coolpix P7700</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="3200" white="65000"/>
+ <Hints>
+ <Hint name="force_uncompressed" value=""/>
+ <Hint name="real_bpp" value="16"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="COOLPIX P7800" mode="12bit-compressed" decoder_version="5">
+ <ID make="Nikon" model="Coolpix P7800">Nikon Coolpix P7800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="3200" white="65000"/>
+ <Hints>
+ <Hint name="force_uncompressed" value=""/>
+ <Hint name="real_bpp" value="16"/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON CORPORATION" model="COOLPIX P1000" mode="12bit-uncompressed">
+ <ID make="Nikon" model="COOLPIX P1000">Nikon Coolpix P1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4095"/>
+ <Hints>
+ <Hint name="coolpixmangled" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="E8800" mode="12bit-compressed" decoder_version="3" supported="no"> <!--
need raw sample for whitelevel -->
+ <ID make="Nikon" model="Coolpix E8800">Nikon Coolpix E8800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3280" height="2454"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="NIKON" model="E8800" mode="12bit-uncompressed" decoder_version="3" supported="no"> <!--
need raw sample for whitelevel -->
+ <ID make="Nikon" model="Coolpix E8800">Nikon Coolpix E8800</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3280" height="2454"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="coolpixsplit" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="OLYMPUS OPTICAL CO.,LTD" model="C5050Z">
+ <ID make="Olympus" model="C5050Z">Olympus C5050Z</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="C5060WZ">
+ <ID make="Olympus" model="C5060WZ">Olympus C5060WZ</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="C8080WZ">
+ <ID make="Olympus" model="C8080WZ">Olympus C8080WZ</ID>
+ <Crop x="0" y="0" width="3280" height="2453"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-1">
+ <ID make="Olympus" model="E-1">Olympus E-1</ID>
+ <Crop x="0" y="0" width="2624" height="1966"/>
+ <Sensor black="65" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="TG-4">
+ <ID make="Olympus" model="TG-4">Olympus TG-4</ID>
+ <Crop x="16" y="8" width="-16" height="-8"/>
+ <Sensor black="0" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="TG-5">
+ <ID make="Olympus" model="TG-5">Olympus TG-5</ID>
+ <Crop x="0" y="0" width="-24" height="0"/>
+ <Sensor black="256" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="C7070WZ">
+ <ID make="Olympus" model="C7070WZ">Olympus C7070WZ</ID>
+ <Crop x="0" y="0" width="3088" height="2309"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-3">
+ <ID make="Olympus" model="E-3">Olympus E-3</ID>
+ <Crop x="0" y="0" width="3720" height="2800"/>
+ <Sensor black="65" white="4015"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-30">
+ <ID make="Olympus" model="E-30">Olympus E-30</ID>
+ <Crop x="0" y="0" width="-6" height="-2"/>
+ <Sensor black="65" white="4015"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-300" decoder_version="3">
+ <ID make="Olympus" model="E-300">Olympus E-300</ID>
+ <Crop x="0" y="0" width="3340" height="2504"/>
+ <Sensor black="63" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-330" decoder_version="3">
+ <ID make="Olympus" model="E-330">Olympus E-330</ID>
+ <Crop x="0" y="0" width="3250" height="2450"/>
+ <Sensor black="77" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-400">
+ <ID make="Olympus" model="E-400">Olympus E-400</ID>
+ <Crop x="0" y="0" width="3768" height="2840"/>
+ <Sensor black="96" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-410">
+ <ID make="Olympus" model="E-410">Olympus E-410</ID>
+ <Crop x="0" y="0" width="3720" height="2800"/>
+ <Sensor black="72" white="3500"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-420">
+ <ID make="Olympus" model="E-420">Olympus E-420</ID>
+ <Crop x="0" y="0" width="3720" height="2800"/>
+ <Sensor black="68" white="4015"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-450">
+ <ID make="Olympus" model="E-450">Olympus E-450</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="69" white="4015"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-500" decoder_version="3">
+ <ID make="Olympus" model="E-500">Olympus E-500</ID>
+ <Crop x="0" y="0" width="3340" height="2504"/>
+ <Sensor black="63" white="3967"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-510">
+ <ID make="Olympus" model="E-510">Olympus E-510</ID>
+ <Crop x="0" y="0" width="3720" height="2800"/>
+ <Sensor black="72" white="3500"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-520">
+ <ID make="Olympus" model="E-520">Olympus E-520</ID>
+ <Crop x="0" y="0" width="3720" height="2800"/>
+ <Sensor black="69" white="4015"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-600">
+ <ID make="Olympus" model="E-600">Olympus E-600</ID>
+ <Crop x="0" y="0" width="4096" height="3072"/>
+ <Sensor black="64" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-620">
+ <ID make="Olympus" model="E-620">Olympus E-620</ID>
+ <Crop x="0" y="0" width="4096" height="3072"/>
+ <Sensor black="64" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="SH-2">
+ <ID make="Olympus" model="SH-2">Olympus SH-2</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="SP320">
+ <ID make="Olympus" model="SP320">Olympus SP320</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="SP350">
+ <ID make="Olympus" model="SP350">Olympus SP350</ID>
+ <Crop x="0" y="0" width="3280" height="2453"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="SP500UZ">
+ <ID make="Olympus" model="SP500UZ">Olympus SP500UZ</ID>
+ <Crop x="0" y="0" width="2832" height="2117"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-5">
+ <ID make="Olympus" model="E-5">Olympus E-5</ID>
+ <Crop x="0" y="0" width="-4" height="0"/>
+ <Sensor black="80" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS OPTICAL CO.,LTD" model="E-10">
+ <ID make="Olympus" model="E-10">Olympus E-10</ID>
+ <Crop x="0" y="0" width="2256" height="1684"/>
+ <Sensor black="32" white="1023"/>
+ </Camera>
+ <Camera make="OLYMPUS OPTICAL CO.,LTD" model="E-20,E-20N,E-20P">
+ <ID make="Olympus" model="E-20">Olympus E-20</ID>
+ <Crop x="0" y="0" width="2576" height="1924"/>
+ <Sensor black="0" white="4092"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-M1">
+ <ID make="Olympus" model="E-M1">Olympus E-M1</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="255" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-M1X">
+ <ID make="Olympus" model="E-M1X">Olympus E-M1X</ID>
+ <Crop x="0" y="0" width="-22" height="0"/>
+ <Sensor black="252" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-M1MarkII">
+ <ID make="Olympus" model="E-M1MarkII">Olympus E-M1 Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-M10">
+ <ID make="Olympus" model="E-M10">Olympus E-M10</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-M10MarkII">
+ <ID make="Olympus" model="E-M10 Mark II">Olympus E-M10 Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-M10 Mark III">
+ <ID make="Olympus" model="E-M10 Mark III">Olympus E-M10 Mark III</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-M5">
+ <ID make="Olympus" model="E-M5">Olympus E-M5</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="255" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-M5MarkII">
+ <ID make="Olympus" model="E-M5 Mark II">Olympus E-M5 Mark II</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="254" white="4000"/>
+ <Sensor black="249" white="4000" iso_min="800" iso_max="3199"/>
+ <Sensor black="235" white="4000" iso_min="3200" iso_max="6399"/>
+ <Sensor black="230" white="4000" iso_min="6400" iso_max="12799"/>
+ <Sensor black="220" white="4000" iso_min="12800" iso_max="25599"/>
+ <Sensor black="187" white="4000" iso_min="25600" iso_max="25600"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-P1">
+ <ID make="Olympus" model="E-P1">Olympus E-P1</ID>
+ <Crop x="0" y="0" width="4094" height="3082"/>
+ <Sensor black="55" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL1">
+ <ID make="Olympus" model="E-PL1">Olympus E-PL1</ID>
+ <Crop x="0" y="0" width="4094" height="3082"/>
+ <Sensor black="55" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL2">
+ <ID make="Olympus" model="E-PL2">Olympus E-PL2</ID>
+ <Crop x="0" y="0" width="-4" height="0"/>
+ <Sensor black="45" white="4095"/>
+ <BlackAreas>
+ <Vertical x="4097" width="2"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL3">
+ <ID make="Olympus" model="E-PL3">Olympus E-PL3</ID>
+ <Crop x="0" y="0" width="-30" height="0"/>
+ <Sensor black="45" white="4095"/>
+ <BlackAreas>
+ <Vertical x="4064" width="6"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL5">
+ <ID make="Olympus" model="E-PL5">Olympus E-PL5</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL6">
+ <ID make="Olympus" model="E-PL6">Olympus E-PL6</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PL7">
+ <ID make="Olympus" model="E-PL7">Olympus E-PL7</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-PL8">
+ <ID make="Olympus" model="E-PL8">Olympus E-PL8</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="254" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="E-PL9">
+ <ID make="Olympus" model="E-PL9">Olympus E-PL9</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="248" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-P5">
+ <ID make="Olympus" model="E-P5">Olympus E-P5</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="250" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PM1">
+ <ID make="Olympus" model="E-PM1">Olympus E-PM1</ID>
+ <Crop x="0" y="0" width="-24" height="0"/>
+ <Sensor black="45" white="4095"/>
+ <BlackAreas>
+ <Vertical x="4064" width="6"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-PM2">
+ <ID make="Olympus" model="E-PM2">Olympus E-PM2</ID>
+ <Crop x="8" y="8" width="-24" height="-8"/>
+ <Sensor black="250" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-P2">
+ <ID make="Olympus" model="E-P2">Olympus E-P2</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="55" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="E-P3">
+ <ID make="Olympus" model="E-P3">Olympus E-P3</ID>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS CORPORATION" model="PEN-F">
+ <ID make="Olympus" model="PEN-F">Olympus PEN-F</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="XZ-1">
+ <ID make="Olympus" model="XZ-1">Olympus XZ-1</ID>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="55" white="3972"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="XZ-2" decoder_version="3">
+ <ID make="Olympus" model="XZ-2">Olympus XZ-2</ID>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="200" white="4092"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="XZ-10" decoder_version="3">
+ <ID make="Olympus" model="XZ-10">Olympus XZ-10</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="200" white="3900"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="SP570UZ">
+ <ID make="Olympus" model="SP570UZ">Olympus SP570UZ</ID>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="OLYMPUS IMAGING CORP." model="STYLUS1">
+ <ID make="Olympus" model="Stylus1">Olympus Stylus1</ID>
+ <Crop x="0" y="0" width="-14" height="0"/>
+ <Sensor black="200" white="3900"/>
+ <Aliases>
+ <Alias id="Stylus1">STYLUS1,1s</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-CM1">
+ <ID make="Panasonic" model="DMC-CM1">Panasonic DMC-CM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-CM1" mode="3:2">
+ <ID make="Panasonic" model="DMC-CM1">Panasonic DMC-CM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FX150">
+ <ID make="Panasonic" model="DMC-FX150">Panasonic DMC-FX150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4429" height="3324"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FX150" mode = "4:3">
+ <ID make="Panasonic" model="DMC-FX150">Panasonic DMC-FX150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4429" height="3324"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ28" mode = "4:3">
+ <ID make="Panasonic" model="DMC-FZ28">Panasonic DMC-FZ28</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3668" height="2754"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ28" mode = "16:9">
+ <ID make="Panasonic" model="DMC-FZ28">Panasonic DMC-FZ28</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3668" height="2754"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ28">
+ <ID make="Panasonic" model="DMC-FZ28">Panasonic DMC-FZ28</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-56" height="-4"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ150">
+ <ID make="Panasonic" model="DMC-FZ150">Panasonic DMC-FZ150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="145" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ150" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ150">Panasonic DMC-FZ150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="145" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ150" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ150">Panasonic DMC-FZ150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ150" mode="1:1">
+ <ID make="Panasonic" model="DMC-FZ150">Panasonic DMC-FZ150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="142" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ150" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ150">Panasonic DMC-FZ150</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ200">
+ <ID make="Panasonic" model="DMC-FZ200">Panasonic DMC-FZ200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="-4"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ200" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ200">Panasonic DMC-FZ200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="-4"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ200" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ200">Panasonic DMC-FZ200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ200" mode="1:1">
+ <ID make="Panasonic" model="DMC-FZ200">Panasonic DMC-FZ200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ200" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ200">Panasonic DMC-FZ200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ2000">
+ <ID make="Panasonic" model="DMC-FZ2000">Panasonic DMC-FZ2000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="2095"/>
+ <Aliases>
+ <Alias>DMC-FZ2500</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ2000" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ2000">Panasonic DMC-FZ2000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="2095"/>
+ <Aliases>
+ <Alias>DMC-FZ2500</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ300">
+ <ID make="Panasonic" model="DMC-FZ300">Panasonic DMC-FZ300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ300" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ300">Panasonic DMC-FZ300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ330">
+ <ID make="Panasonic" model="DMC-FZ330">Panasonic DMC-FZ300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ330" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ330">Panasonic DMC-FZ300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+
+ <Camera make="Panasonic" model = "DMC-G1">
+ <ID make="Panasonic" model="DMC-G1">Panasonic DMC-G1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4018" height="3016" />
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G1" mode="4:3">
+ <ID make="Panasonic" model="DMC-G1">Panasonic DMC-G1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4018" height="3016" />
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G1" mode="16:9">
+ <ID make="Panasonic" model="DMC-G1">Panasonic DMC-G1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0" />
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G1" mode="3:2">
+ <ID make="Panasonic" model="DMC-G1">Panasonic DMC-G1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G2" mode="4:3">
+ <ID make="Panasonic" model="DMC-G2">Panasonic DMC-G2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G2" mode="3:2">
+ <ID make="Panasonic" model="DMC-G2">Panasonic DMC-G2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G2" mode="16:9">
+ <ID make="Panasonic" model="DMC-G2">Panasonic DMC-G2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G2" mode="1:1">
+ <ID make="Panasonic" model="DMC-G2">Panasonic DMC-G2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G2">
+ <ID make="Panasonic" model="DMC-G2">Panasonic DMC-G2</ID>
+ <!-- Default Guess -->
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G10">
+ <ID make="Panasonic" model="DMC-G10">Panasonic DMC-G10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G10" mode="4:3">
+ <ID make="Panasonic" model="DMC-G10">Panasonic DMC-G10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0" />
+ <Sensor black="0" white="3900"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH1" mode="4:3">
+ <ID make="Panasonic" model="DMC-GH1">Panasonic DMC-GH1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH1" mode="3:2">
+ <ID make="Panasonic" model="DMC-GH1">Panasonic DMC-GH1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH1">
+ <ID make="Panasonic" model="DMC-GH1">Panasonic DMC-GH1</ID>
+ <!-- Default Guess -->
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH3" mode="4:3">
+ <ID make="Panasonic" model="DMC-GH3">Panasonic DMC-GH3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="155" white="3956"/>
+ <Sensor black="165" white="3941" iso_min="6400"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH3" mode="3:2">
+ <ID make="Panasonic" model="DMC-GH3">Panasonic DMC-GH3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="155" white="3956"/>
+ <Sensor black="165" white="3941" iso_min="6400"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH3" mode="16:9">
+ <ID make="Panasonic" model="DMC-GH3">Panasonic DMC-GH3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="155" white="3956"/>
+ <Sensor black="165" white="3941" iso_min="6400"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH3" mode="1:1">
+ <ID make="Panasonic" model="DMC-GH3">Panasonic DMC-GH3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="155" white="3956"/>
+ <Sensor black="165" white="3941" iso_min="6400"/>
+ </Camera>
+ <!-- Default Guess -->
+ <Camera make="Panasonic" model = "DMC-GH3">
+ <ID make="Panasonic" model="DMC-GH3">Panasonic DMC-GH3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="155" white="3956"/>
+ <Sensor black="165" white="3941" iso_min="6400"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH4" mode="4:3">
+ <ID make="Panasonic" model="DMC-GH4">Panasonic DMC-GH4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-200" height="-8"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH4" mode="3:2">
+ <ID make="Panasonic" model="DMC-GH4">Panasonic DMC-GH4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-200" height="-8"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH4" mode="16:9">
+ <ID make="Panasonic" model="DMC-GH4">Panasonic DMC-GH4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-200" height="-8"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH4" mode="1:1">
+ <ID make="Panasonic" model="DMC-GH4">Panasonic DMC-GH4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <!-- Default Guess -->
+ <Camera make="Panasonic" model = "DMC-GH4">
+ <ID make="Panasonic" model="DMC-GH4">Panasonic DMC-GH4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="-200" height="-8"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF1">
+ <ID make="Panasonic" model="DMC-GF1">Panasonic DMC-GF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF1" mode="4:3">
+ <ID make="Panasonic" model="DMC-GF1">Panasonic DMC-GF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF1" mode="16:9">
+ <ID make="Panasonic" model="DMC-GF1">Panasonic DMC-GF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF1" mode="3:2">
+ <ID make="Panasonic" model="DMC-GF1">Panasonic DMC-GF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF1" mode="1:1">
+ <ID make="Panasonic" model="DMC-GF1">Panasonic DMC-GF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF2">
+ <ID make="Panasonic" model="DMC-GF2">Panasonic DMC-GF2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF2" mode="4:3">
+ <ID make="Panasonic" model="DMC-GF2">Panasonic DMC-GF2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF2" mode="16:9">
+ <ID make="Panasonic" model="DMC-GF2">Panasonic DMC-GF2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF2" mode="3:2">
+ <ID make="Panasonic" model="DMC-GF2">Panasonic DMC-GF2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF2" mode="1:1">
+ <ID make="Panasonic" model="DMC-GF2">Panasonic DMC-GF2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GM1" mode="4:3">
+ <ID make="Panasonic" model="DMC-GM1">Panasonic DMC-GM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GM1" mode="3:2">
+ <ID make="Panasonic" model="DMC-GM1">Panasonic DMC-GM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GM1" mode="16:9">
+ <ID make="Panasonic" model="DMC-GM1">Panasonic DMC-GM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GM1" mode="1:1">
+ <ID make="Panasonic" model="DMC-GM1">Panasonic DMC-GM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <!-- Default Guess -->
+ <Camera make="Panasonic" model = "DMC-GM1">
+ <ID make="Panasonic" model="DMC-GM1">Panasonic DMC-GM1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GM5">
+ <ID make="Panasonic" model="DMC-GM5">Panasonic DMC-GM5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GM5" mode="4:3">
+ <ID make="Panasonic" model="DMC-GM5">Panasonic DMC-GM5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="4592" height="3448"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GM5" mode="3:2">
+ <ID make="Panasonic" model="DMC-GM5">Panasonic DMC-GM5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="4592" height="3064"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GM5" mode="16:9">
+ <ID make="Panasonic" model="DMC-GM5">Panasonic DMC-GM5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="4592" height="2584"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GM5" mode="1:1">
+ <ID make="Panasonic" model="DMC-GM5">Panasonic DMC-GM5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="8" y="8" width="3424" height="3424"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G3" mode="4:3">
+ <ID make="Panasonic" model="DMC-G3">Panasonic DMC-G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-152" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G3" mode="1:1">
+ <ID make="Panasonic" model="DMC-G3">Panasonic DMC-G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G3" mode="16:9">
+ <ID make="Panasonic" model="DMC-G3">Panasonic DMC-G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G3" mode="3:2">
+ <ID make="Panasonic" model="DMC-G3">Panasonic DMC-G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G3">
+ <ID make="Panasonic" model="DMC-G3">Panasonic DMC-G3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G5" mode="4:3">
+ <ID make="Panasonic" model="DMC-G5">Panasonic DMC-G5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-192" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G5" mode="3:2">
+ <ID make="Panasonic" model="DMC-G5">Panasonic DMC-G5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-192" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G5" mode="16:9">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-192" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G5" mode="1:1">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G5">
+ <ID make="Panasonic" model="DMC-G5">Panasonic DMC-G5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-192" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G6">
+ <ID make="Panasonic" model="DMC-G6">Panasonic DMC-G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="142" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G6" mode="4:3">
+ <ID make="Panasonic" model="DMC-G6">Panasonic DMC-G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="142" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G6" mode="3:2">
+ <ID make="Panasonic" model="DMC-G6">Panasonic DMC-G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="142" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G6" mode="16:9">
+ <ID make="Panasonic" model="DMC-G6">Panasonic DMC-G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-194" height="0"/>
+ <Sensor black="142" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-G6" mode="1:1">
+ <ID make="Panasonic" model="DMC-G6">Panasonic DMC-G6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G7">
+ <ID make="Panasonic" model="DMC-G7">Panasonic DMC-G7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G7" mode="4:3">
+ <ID make="Panasonic" model="DMC-G7">Panasonic DMC-G7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G7" mode="3:2">
+ <ID make="Panasonic" model="DMC-G7">Panasonic DMC-G7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G7" mode="1:1">
+ <ID make="Panasonic" model="DMC-G7">Panasonic DMC-G7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G7" mode="16:9">
+ <ID make="Panasonic" model="DMC-G7">Panasonic DMC-G7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G8">
+ <ID make="Panasonic" model="DMC-G8">Panasonic DMC-G8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G80</Alias>
+ <Alias>DMC-G81</Alias>
+ <Alias>DMC-G85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-G8" mode="4:3">
+ <ID make="Panasonic" model="DMC-G8">Panasonic DMC-G8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-G80</Alias>
+ <Alias>DMC-G81</Alias>
+ <Alias>DMC-G85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF3">
+ <ID make="Panasonic" model="DMC-GF3">Panasonic DMC-GF3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-72" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF3" mode= "4:3">
+ <ID make="Panasonic" model="DMC-GF3">Panasonic DMC-GF3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-72" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF3" mode= "3:2">
+ <ID make="Panasonic" model="DMC-GF3">Panasonic DMC-GF3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-72" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF3" mode= "16:9">
+ <ID make="Panasonic" model="DMC-GF3">Panasonic DMC-GF3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-72" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF3" mode= "1:1">
+ <ID make="Panasonic" model="DMC-GF3">Panasonic DMC-GF3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-184" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF5">
+ <ID make="Panasonic" model="DMC-GF5">Panasonic DMC-GF5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF5" mode= "4:3">
+ <ID make="Panasonic" model="DMC-GF5">Panasonic DMC-GF5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF5" mode= "16:9">
+ <ID make="Panasonic" model="DMC-GF5">Panasonic DMC-GF5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF5" mode= "3:2">
+ <ID make="Panasonic" model="DMC-GF5">Panasonic DMC-GF5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF5" mode= "1:1">
+ <ID make="Panasonic" model="DMC-GF5">Panasonic DMC-GF5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF6">
+ <ID make="Panasonic" model="DMC-GF6">Panasonic DMC-GF6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF6" mode= "4:3">
+ <ID make="Panasonic" model="DMC-GF6">Panasonic DMC-GF6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF6" mode= "16:9">
+ <ID make="Panasonic" model="DMC-GF6">Panasonic DMC-GF6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF6" mode= "3:2">
+ <ID make="Panasonic" model="DMC-GF6">Panasonic DMC-GF6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GF6" mode= "1:1">
+ <ID make="Panasonic" model="DMC-GF6">Panasonic DMC-GF6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GF7">
+ <ID make="Panasonic" model="DMC-GF7">Panasonic DMC-GF7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GF7" mode="4:3">
+ <ID make="Panasonic" model="DMC-GF7">Panasonic DMC-GF7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GF7" mode="1:1">
+ <ID make="Panasonic" model="DMC-GF7">Panasonic DMC-GF7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GF7" mode="3:2">
+ <ID make="Panasonic" model="DMC-GF7">Panasonic DMC-GF7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GF7" mode="16:9">
+ <ID make="Panasonic" model="DMC-GF7">Panasonic DMC-GF7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH2">
+ <ID make="Panasonic" model="DMC-GH2">Panasonic DMC-GH2</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="1">RED</Color>
+ <Color x="0" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-216" height="0"/>
+ <Sensor black="15" white="3800"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH2" mode="4:3">
+ <ID make="Panasonic" model="DMC-GH2">Panasonic DMC-GH2</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="1">RED</Color>
+ <Color x="0" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-136" height="0"/>
+ <Sensor black="15" white="3800"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH2" mode="16:9">
+ <ID make="Panasonic" model="DMC-GH2">Panasonic DMC-GH2</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="1">RED</Color>
+ <Color x="0" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-216" height="0"/>
+ <Sensor black="15" white="3800"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH2" mode="3:2">
+ <ID make="Panasonic" model="DMC-GH2">Panasonic DMC-GH2</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="1">RED</Color>
+ <Color x="0" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-216" height="0"/>
+ <Sensor black="15" white="3800"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH2" mode="1:1">
+ <ID make="Panasonic" model="DMC-GH2">Panasonic DMC-GH2</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="1">RED</Color>
+ <Color x="0" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="15" white="3800"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GH1" mode="16:9">
+ <ID make="Panasonic" model="DMC-GH1">Panasonic DMC-GH1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ35" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ35">Panasonic DMC-FZ35</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="150" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ38</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ35" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ35">Panasonic DMC-FZ35</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-42" height="-4"/>
+ <Sensor black="143" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ38</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ35" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ35">Panasonic DMC-FZ35</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-42" height="-4"/>
+ <Sensor black="143" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ38</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ35">
+ <ID make="Panasonic" model="DMC-FZ35">Panasonic DMC-FZ35</ID>
+ <!-- Default Guess -->
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="150" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ38</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ45">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-58" height="-10"/>
+ <Sensor black="150" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ45" mode = "4:3">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-58" height="-10"/>
+ <Sensor black="150" white="3986"/>
+ <Aliases>
+ <Alias>DMC-FZ40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ70">
+ <ID make="Panasonic" model="DMC-FZ70">Panasonic DMC-FZ70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-182" height="0"/>
+ <Sensor black="120" white="3971"/>
+ <Aliases>
+ <Alias>DMC-FZ72</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ70" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ70">Panasonic DMC-FZ70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-182" height="0"/>
+ <Sensor black="120" white="3971"/>
+ <Aliases>
+ <Alias>DMC-FZ72</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ70" mode="1:1">
+ <ID make="Panasonic" model="DMC-FZ70">Panasonic DMC-FZ70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="144" white="3956"/>
+ <Aliases>
+ <Alias>DMC-FZ72</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ70" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ70">Panasonic DMC-FZ70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-182" height="0"/>
+ <Sensor black="144" white="3956"/>
+ <Aliases>
+ <Alias>DMC-FZ72</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ70" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ70">Panasonic DMC-FZ70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-182" height="0"/>
+ <Sensor black="144" white="3956"/>
+ <Aliases>
+ <Alias>DMC-FZ72</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ100">
+ <ID make="Panasonic" model="DMC-FZ100">Panasonic DMC-FZ100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="0"/>
+ <Sensor black="120" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ100" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ100">Panasonic DMC-FZ100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="0"/>
+ <Sensor black="120" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ1000">
+ <ID make="Panasonic" model="DMC-FZ1000">Panasonic DMC-FZ1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ1000" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ1000">Panasonic DMC-FZ1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ1000" mode="1:1">
+ <ID make="Panasonic" model="DMC-FZ1000">Panasonic DMC-FZ1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ1000" mode="4:3">
+ <ID make="Panasonic" model="DMC-FZ1000">Panasonic DMC-FZ1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-160" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ1000" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ1000">Panasonic DMC-FZ1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX1">
+ <ID make="Panasonic" model="DMC-GX1">Panasonic DMC-GX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-154" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX1" mode = "4:3">
+ <ID make="Panasonic" model="DMC-GX1">Panasonic DMC-GX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-154" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX1" mode = "3:2">
+ <ID make="Panasonic" model="DMC-GX1">Panasonic DMC-GX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-170" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX1" mode = "16:9">
+ <ID make="Panasonic" model="DMC-GX1">Panasonic DMC-GX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-170" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX1" mode = "1:1">
+ <ID make="Panasonic" model="DMC-GX1">Panasonic DMC-GX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-198" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX7">
+ <ID make="Panasonic" model="DMC-GX7">Panasonic DMC-GX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX7" mode = "4:3">
+ <ID make="Panasonic" model="DMC-GX7">Panasonic DMC-GX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX7" mode = "3:2">
+ <ID make="Panasonic" model="DMC-GX7">Panasonic DMC-GX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX7" mode = "16:9">
+ <ID make="Panasonic" model="DMC-GX7">Panasonic DMC-GX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-GX7" mode = "1:1">
+ <ID make="Panasonic" model="DMC-GX7">Panasonic DMC-GX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="150" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GX8">
+ <ID make="Panasonic" model="DMC-GX8">Panasonic DMC-GX8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GX8" mode="4:3">
+ <ID make="Panasonic" model="DMC-GX8">Panasonic DMC-GX8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GX85">
+ <ID make="Panasonic" model="DMC-GX85">Panasonic DMC-GX85</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-GX80</Alias>
+ <Alias>DMC-GX7MK2</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-GX85" mode="4:3">
+ <ID make="Panasonic" model="DMC-GX85">Panasonic DMC-GX85</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-206" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DMC-GX80</Alias>
+ <Alias>DMC-GX7MK2</Alias>
+ </Aliases>
+ </Camera>
+ <!-- Default guess -->
+ <Camera make="Panasonic" model = "DMC-LF1">
+ <ID make="Panasonic" model="DMC-LF1">Panasonic DMC-LF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-130" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LF1" mode="4:3">
+ <ID make="Panasonic" model="DMC-LF1">Panasonic DMC-LF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LF1" mode="3:2">
+ <ID make="Panasonic" model="DMC-LF1">Panasonic DMC-LF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="143" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LF1" mode="16:9">
+ <ID make="Panasonic" model="DMC-LF1">Panasonic DMC-LF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="143" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LF1" mode="1:1">
+ <ID make="Panasonic" model="DMC-LF1">Panasonic DMC-LF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="142" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX3">
+ <ID make="Panasonic" model="DMC-LX3">Panasonic DMC-LX3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-84" height="0"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX3" mode="16:9">
+ <ID make="Panasonic" model="DMC-LX3">Panasonic DMC-LX3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3990" height="2250"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX3" mode="4:3">
+ <ID make="Panasonic" model="DMC-LX3">Panasonic DMC-LX3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3666" height="2754"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX3" mode="3:2">
+ <ID make="Panasonic" model="DMC-LX3">Panasonic DMC-LX3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX3" mode="1:1">
+ <ID make="Panasonic" model="DMC-LX3">Panasonic DMC-LX3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="0"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX15">
+ <ID make="Panasonic" model="DMC-LX15">Panasonic DMC-LX9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Sensor black="142" white="2095" iso_list="80"/>
+ <Sensor black="142" white="3400" iso_list="100"/>
+ <Sensor black="142" white="4095" iso_list="125"/>
+ <Sensor black="144" white="4095" iso_list="640 800"/>
+ <Sensor black="145" white="4095" iso_list="1250 1600"/>
+ <Sensor black="149" white="4095" iso_list="3200"/>
+ <Sensor black="154" white="4095" iso_list="6400"/>
+ <Sensor black="166" white="4095" iso_list="12800"/>
+ <Sensor black="167" white="4095" iso_list="25600"/>
+ <Aliases>
+ <Alias>DMC-LX9</Alias>
+ <Alias>DMC-LX10</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX15" mode="3:2">
+ <ID make="Panasonic" model="DMC-LX15">Panasonic DMC-LX9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Sensor black="142" white="2095" iso_list="80"/>
+ <Sensor black="142" white="3400" iso_list="100"/>
+ <Sensor black="142" white="4095" iso_list="125"/>
+ <Sensor black="144" white="4095" iso_list="640 800"/>
+ <Sensor black="145" white="4095" iso_list="1250 1600"/>
+ <Sensor black="149" white="4095" iso_list="3200"/>
+ <Sensor black="154" white="4095" iso_list="6400"/>
+ <Sensor black="166" white="4095" iso_list="12800"/>
+ <Sensor black="167" white="4095" iso_list="25600"/>
+ <Aliases>
+ <Alias>DMC-LX9</Alias>
+ <Alias>DMC-LX10</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="LEICA" model="C (Typ 112)">
+ <ID make="Leica" model="C (Typ 112)">Leica C (Typ 112)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-130" height="0"/>
+ <Sensor black="146" white="3956"/>
+ </Camera>
+ <Camera make="LEICA" model="C (Typ 112)" mode="4:3">
+ <ID make="Leica" model="C (Typ 112)">Leica C (Typ 112)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-130" height="0"/>
+ <Sensor black="146" white="3956"/>
+ </Camera>
+ <Camera make="LEICA" model="DIGILUX 2" decoder_version="2">
+ <ID make="Leica" model="Digilux 2">Leica Digilux 2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="DIGILUX 2" mode="4:3" decoder_version="2">
+ <ID make="Leica" model="Digilux 2">Leica Digilux 2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX 3" decoder_version="2">
+ <ID make="Leica" model="D-LUX 3">Leica D-LUX 3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX 3" mode="16:9" decoder_version="2">
+ <ID make="Leica" model="D-LUX 3">Leica D-LUX 3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="17" y="17" width="-65" height="-24"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX (Typ 114)">
+ <ID make="Leica" model="V-LUX (Typ 114)">Leica V-LUX (Typ 114)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX (Typ 114)" mode="3:2">
+ <ID make="Leica" model="V-LUX (Typ 114)">Leica V-LUX (Typ 114)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX (Typ 114)" mode="4:3">
+ <ID make="Leica" model="V-LUX (Typ 114)">Leica V-LUX (Typ 114)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-162" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX (Typ 114)" mode="16:9">
+ <ID make="Leica" model="V-LUX (Typ 114)">Leica V-LUX (Typ 114)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX (Typ 114)" mode="1:1">
+ <ID make="Leica" model="V-LUX (Typ 114)">Leica V-LUX (Typ 114)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="142" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX 1" decoder_version="2">
+ <ID make="Leica" model="V-LUX 1">Leica V-LUX 1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX 1" mode="3:2" decoder_version="2">
+ <ID make="Leica" model="V-LUX 1">Leica V-LUX 1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-14" height="-3"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model="V-LUX 1" mode="4:3" decoder_version="2">
+ <ID make="Leica" model="V-LUX 1">Leica V-LUX 1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-14" height="-3"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-L10" decoder_version="2">
+ <ID make="Panasonic" model="DMC-L10">Panasonic DMC-L10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-L10" mode="4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-L10">Panasonic DMC-L10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ30" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ30">Panasonic DMC-FZ30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ30" mode="4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ30">Panasonic DMC-FZ30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="0" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ50" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ50">Panasonic DMC-FZ50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-FZ50" mode = "4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ50">Panasonic DMC-FZ50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="20" y="18" width="-16" height="-5"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ50" mode="16:9" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ50">Panasonic DMC-FZ50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="3" y="0" width="-15" height="-5"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ50" mode="3:2" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ50">Panasonic DMC-FZ50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="3" y="0" width="-17" height="-5"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ8" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ8">Panasonic DMC-FZ8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ8" mode="4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ8">Panasonic DMC-FZ8</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="5" y="0" width="-31" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ18" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ18">Panasonic DMC-FZ18</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ18" mode="4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-FZ18">Panasonic DMC-FZ18</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="10" y="0" width="-30" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ18" mode="16:9">
+ <ID make="Panasonic" model="DMC-FZ18">Panasonic DMC-FZ18</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="9" y="0" width="-31" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-FZ18" mode="3:2">
+ <ID make="Panasonic" model="DMC-FZ18">Panasonic DMC-FZ18</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="9" y="0" width="-31" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-L1" decoder_version="2">
+ <ID make="Panasonic" model="DMC-L1">Panasonic DMC-L1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-11" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-L1" mode="4:3" decoder_version="2">
+ <ID make="Panasonic" model="DMC-L1">Panasonic DMC-L1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-11" height="-1"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX2" decoder_version="2">
+ <ID make="Panasonic" model="DMC-LX2">Panasonic DMC-LX2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX2" mode="16:9" decoder_version="2">
+ <ID make="Panasonic" model="DMC-LX2">Panasonic DMC-LX2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-38" height="-3"/>
+ <Sensor black="0" white="3986"/>
+ </Camera>
+ <!-- Leica D-Lux 4 is the same camera as LX-3 -->
+ <Camera make="LEICA" model = "D-LUX 4">
+ <ID make="Leica" model="D-LUX 4">Leica D-LUX 4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-84" height="0"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 4" mode="16:9">
+ <ID make="Leica" model="D-LUX 4">Leica D-LUX 4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3990" height="2250"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 4" mode="4:3">
+ <ID make="Leica" model="D-LUX 4">Leica D-LUX 4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3666" height="2754"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 4" mode="3:2">
+ <ID make="Leica" model="D-LUX 4">Leica D-LUX 4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="0"/>
+ <Sensor black="15" white="3971"/>
+ </Camera>
+
+ <Camera make="Panasonic" model = "DMC-LX5">
+ <ID make="Panasonic" model="DMC-LX5">Panasonic DMC-LX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX5" mode="4:3">
+ <ID make="Panasonic" model="DMC-LX5">Panasonic DMC-LX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX5" mode="3:2">
+ <ID make="Panasonic" model="DMC-LX5">Panasonic DMC-LX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX5" mode="16:9">
+ <ID make="Panasonic" model="DMC-LX5">Panasonic DMC-LX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-104" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX5" mode="1:1">
+ <ID make="Panasonic" model="DMC-LX5">Panasonic DMC-LX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-220" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <!-- Leica D-Lux 5 is the same camera as LX-5 -->
+ <Camera make="LEICA" model = "D-LUX 5">
+ <ID make="Leica" model="D-LUX 5">Leica D-LUX 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 5" mode="4:3">
+ <ID make="Leica" model="D-LUX 5">Leica D-LUX 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-200" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 5" mode="3:2">
+ <ID make="Leica" model="D-LUX 5">Leica D-LUX 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-74" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 5" mode="16:9">
+ <ID make="Leica" model="D-LUX 5">Leica D-LUX 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-104" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 5" mode="1:1">
+ <ID make="Leica" model="D-LUX 5">Leica D-LUX 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-220" height="-4"/>
+ <Sensor black="150" white="3986"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX7">
+ <ID make="Panasonic" model="DMC-LX7">Panasonic DMC-LX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-60" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX7" mode="4:3">
+ <ID make="Panasonic" model="DMC-LX7">Panasonic DMC-LX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-60" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX7" mode="3:2">
+ <ID make="Panasonic" model="DMC-LX7">Panasonic DMC-LX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX7" mode="16:9">
+ <ID make="Panasonic" model="DMC-LX7">Panasonic DMC-LX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-160" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model = "DMC-LX7" mode="1:1">
+ <ID make="Panasonic" model="DMC-LX7">Panasonic DMC-LX7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-48" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <!-- LEICA D-LUX 6 is the same camera as Panasonic DMC-LX7 -->
+ <Camera make="LEICA" model = "D-LUX 6">
+ <ID make="Leica" model="D-LUX 6">Leica D-LUX 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 6" mode="4:3">
+ <ID make="Leica" model="D-LUX 6">Leica D-LUX 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 6" mode="3:2">
+ <ID make="Leica" model="D-LUX 6">Leica D-LUX 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 6" mode="16:9">
+ <ID make="Leica" model="D-LUX 6">Leica D-LUX 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-160" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model = "D-LUX 6" mode="1:1">
+ <ID make="Leica" model="D-LUX 6">Leica D-LUX 6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-48" height="0"/>
+ <Sensor black="150" white="3971"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX (Typ 109)">
+ <ID make="Leica" model="D-LUX (Typ 109)">Leica D-LUX (Typ 109)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX (Typ 109)" mode="4:3">
+ <ID make="Leica" model="D-LUX (Typ 109)">Leica D-LUX (Typ 109)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX (Typ 109)" mode="3:2">
+ <ID make="Leica" model="D-LUX (Typ 109)">Leica D-LUX (Typ 109)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-80" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX (Typ 109)" mode="16:9">
+ <ID make="Leica" model="D-LUX (Typ 109)">Leica D-LUX (Typ 109)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-98" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="LEICA" model="D-LUX (Typ 109)" mode="1:1">
+ <ID make="Leica" model="D-LUX (Typ 109)">Leica D-LUX (Typ 109)</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-144" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX1" mode="16:9">
+ <ID make="Panasonic" model="DMC-LX1">Panasonic DMC-LX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-16" height="0"/>
+ <Sensor black="0" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX1">
+ <ID make="Panasonic" model="DMC-LX1">Panasonic DMC-LX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX100">
+ <ID make="Panasonic" model="DMC-LX100">Panasonic DMC-LX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX100" mode="4:3">
+ <ID make="Panasonic" model="DMC-LX100">Panasonic DMC-LX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX100" mode="16:9">
+ <ID make="Panasonic" model="DMC-LX100">Panasonic DMC-LX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-96" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX100" mode="3:2">
+ <ID make="Panasonic" model="DMC-LX100">Panasonic DMC-LX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-80" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-LX100" mode="1:1">
+ <ID make="Panasonic" model="DMC-LX100">Panasonic DMC-LX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-144" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-LX100M2">
+ <ID make="Panasonic" model="DC-LX100M2">Panasonic DC-LX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-LX100M2" mode="4:3">
+ <ID make="Panasonic" model="DC-LX100M2">Panasonic DC-LX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="10" y="6" width="-60" height="-4"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-LX100M2" mode="1:1">
+ <ID make="Panasonic" model="DC-LX100M2">Panasonic DC-LX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="6" width="-130" height="-4"/>
+ <Sensor black="144" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-LX100M2" mode="16:9">
+ <ID make="Panasonic" model="DC-LX100M2">Panasonic DC-LX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="10" y="0" width="-90" height="0"/>
+ <Sensor black="144" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-LX100M2" mode="3:2">
+ <ID make="Panasonic" model="DC-LX100M2">Panasonic DC-LX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-90" height="0"/>
+ <Sensor black="144" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ60">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="145" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ60" mode="3:2">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="145" white="3971"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ60" mode="16:9">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="145" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ60" mode="1:1">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="145" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ60" mode="4:3">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="145" white="3956"/>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ61">
+ <ID make="Panasonic" model="DMC-TZ61">Panasonic DMC-ZS40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="145" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ61" mode="4:3">
+ <ID make="Panasonic" model="DMC-TZ61">Panasonic DMC-ZS40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="146" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ61" mode="3:2">
+ <ID make="Panasonic" model="DMC-TZ61">Panasonic DMC-ZS40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="146" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ61" mode="1:1">
+ <ID make="Panasonic" model="DMC-TZ61">Panasonic DMC-ZS40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-6" height="0"/>
+ <Sensor black="146" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ61" mode="16:9">
+ <ID make="Panasonic" model="DMC-TZ61">Panasonic DMC-ZS40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="146" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS40</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ71">
+ <ID make="Panasonic" model="DMC-TZ71">Panasonic DMC-ZS50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="143" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS50</Alias>
+ <Alias>DMC-TZ70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ71" mode="4:3">
+ <ID make="Panasonic" model="DMC-TZ71">Panasonic DMC-ZS50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS50</Alias>
+ <Alias>DMC-TZ70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ71" mode="3:2">
+ <ID make="Panasonic" model="DMC-TZ71">Panasonic DMC-ZS50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS50</Alias>
+ <Alias>DMC-TZ70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ71" mode="16:9">
+ <ID make="Panasonic" model="DMC-TZ71">Panasonic DMC-ZS50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS50</Alias>
+ <Alias>DMC-TZ70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ71" mode="1:1">
+ <ID make="Panasonic" model="DMC-TZ71">Panasonic DMC-ZS50</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="143" white="3971"/>
+ <Aliases>
+ <Alias>DMC-ZS50</Alias>
+ <Alias>DMC-TZ70</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ81">
+ <ID make="Panasonic" model="DMC-TZ81">Panasonic DMC-ZS60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DMC-ZS60</Alias>
+ <Alias>DMC-TZ80</Alias>
+ <Alias>DMC-TZ85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ81" mode="4:3">
+ <ID make="Panasonic" model="DMC-TZ81">Panasonic DMC-ZS60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DMC-ZS60</Alias>
+ <Alias>DMC-TZ80</Alias>
+ <Alias>DMC-TZ85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ100">
+ <ID make="Panasonic" model="DMC-TZ100">Panasonic DMC-ZS100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DMC-ZS100</Alias>
+ <Alias>DMC-ZS110</Alias>
+ <Alias>DMC-TZ101</Alias>
+ <Alias>DMC-TZ110</Alias>
+ <Alias>DMC-TX1</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DMC-TZ100" mode="3:2">
+ <ID make="Panasonic" model="DMC-TZ100">Panasonic DMC-ZS100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DMC-ZS100</Alias>
+ <Alias>DMC-ZS110</Alias>
+ <Alias>DMC-TZ101</Alias>
+ <Alias>DMC-TZ110</Alias>
+ <Alias>DMC-TX1</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-FZ82">
+ <ID make="Panasonic" model="DC-FZ82">Panasonic DC-FZ82</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DC-FZ80</Alias>
+ <Alias>DMC-FZ80</Alias>
+ <Alias>DMC-FZ85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-FZ82" mode="4:3">
+ <ID make="Panasonic" model="DC-FZ82">Panasonic DC-FZ82</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DC-FZ80</Alias>
+ <Alias>DMC-FZ80</Alias>
+ <Alias>DMC-FZ85</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5">
+ <ID make="Panasonic" model="DC-GH5">Panasonic DC-GH5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-56" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5" mode="4:3">
+ <ID make="Panasonic" model="DC-GH5">Panasonic DC-GH5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-56" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5S" decoder_version="3">
+ <ID make="Panasonic" model="DC-GH5S">Panasonic DC-GH5S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="510" white="8000"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5S" decoder_version="3" mode="4:3">
+ <ID make="Panasonic" model="DC-GH5S">Panasonic DC-GH5S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-40" height="0"/>
+ <Sensor black="510" white="8000"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5S" decoder_version="3" mode="3:2">
+ <ID make="Panasonic" model="DC-GH5S">Panasonic DC-GH5S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-24" height="0"/>
+ <Sensor black="510" white="8000"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5S" decoder_version="3" mode="16:9">
+ <ID make="Panasonic" model="DC-GH5S">Panasonic DC-GH5S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-68" height="0"/>
+ <Sensor black="510" white="8000"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GH5S" decoder_version="3" mode="1:1">
+ <ID make="Panasonic" model="DC-GH5S">Panasonic DC-GH5S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="510" white="8000"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-G9">
+ <ID make="Panasonic" model="DC-G9">Panasonic DC-G9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-58" height="0"/>
+ <Sensor black="148" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-G9" mode="4:3">
+ <ID make="Panasonic" model="DC-G9">Panasonic DC-G9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-58" height="0"/>
+ <Sensor black="148" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GF9">
+ <ID make="Panasonic" model="DC-GF9">Panasonic DC-GF9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DC-GX800</Alias>
+ <Alias>DC-GX850</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GF9" mode="4:3">
+ <ID make="Panasonic" model="DC-GF9">Panasonic DC-GF9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-208" height="0"/>
+ <Sensor black="143" white="4095"/>
+ <Aliases>
+ <Alias>DC-GX800</Alias>
+ <Alias>DC-GX850</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GX9">
+ <ID make="Panasonic" model="DC-GX9">Panasonic DC-GX9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-66" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-GX9" mode="4:3">
+ <ID make="Panasonic" model="DC-GX9">Panasonic DC-GX9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-66" height="0"/>
+ <Sensor black="143" white="4095"/>
+ </Camera>
+ <Camera make="Panasonic" model="DC-TZ90">
+ <ID make="Panasonic" model="DC-TZ90">Panasonic DC-ZS70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="139" white="4095"/>
+ <Aliases>
+ <Alias>DC-ZS70</Alias>
+ <Alias>DC-FZ91</Alias>
+ <Alias>DC-FZ92</Alias>
+ <Alias>DC-FZ93</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-TZ90" mode="4:3">
+ <ID make="Panasonic" model="DC-TZ90">Panasonic DC-ZS70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="139" white="4095"/>
+ <Aliases>
+ <Alias>DC-ZS70</Alias>
+ <Alias>DC-FZ91</Alias>
+ <Alias>DC-FZ92</Alias>
+ <Alias>DC-FZ93</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-TZ202">
+ <ID make="Panasonic" model="DC-TZ202">Panasonic DC-ZS200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DC-TZ200</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Panasonic" model="DC-TZ202" mode="3:2">
+ <ID make="Panasonic" model="DC-TZ202">Panasonic DC-ZS200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="142" white="4095"/>
+ <Aliases>
+ <Alias>DC-TZ200</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K100D">
+ <ID make="Pentax" model="K100D">Pentax K100D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3040" height="2024"/>
+ <Sensor black="127" white="3950"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K110D">
+ <ID make="Pentax" model="K110D">Pentax K110D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="127" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K100D Super">
+ <ID make="Pentax" model="K100D Super">Pentax K100D Super</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="127" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K100D">
+ <ID make="Pentax" model="K100D">Pentax K100D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3040" height="2024"/>
+ <Sensor black="127" white="3950"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K110D">
+ <ID make="Pentax" model="K110D">Pentax K110D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="127" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX *ist D">
+ <ID make="Pentax" model="*ist D">Pentax *ist D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="128" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX *ist DL">
+ <ID make="Pentax" model="*ist DL">Pentax *ist DL</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="128" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX *ist DL2">
+ <ID make="Pentax" model="*ist DL2">Pentax *ist DL2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="127" white="3950"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX *ist DS">
+ <ID make="Pentax" model="*ist DS">Pentax *ist DS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="-2"/>
+ <Sensor black="128" white="3950"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K10D">
+ <ID make="Pentax" model="K10D">Pentax K10D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3888" height="2608"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K20D">
+ <ID make="Pentax" model="K20D">Pentax K20D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4688" height="3124"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K20D">
+ <ID make="Pentax" model="K20D">Pentax K20D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4688" height="3124"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K200D">
+ <ID make="Pentax" model="K200D">Pentax K200D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3880" height="2604"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-S1">
+ <ID make="Pentax" model="K-S1">Pentax K-S1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="32" white="4062"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-S2">
+ <ID make="Pentax" model="K-S2">Pentax K-S2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="0" white="3839"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-70">
+ <ID make="Pentax" model="K-70">PENTAX K-70</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="58" y="30" width="0" height="-14"/>
+ <Sensor black="64" white="16319"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-1">
+ <ID make="Pentax" model="K-1">Pentax K-1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="4" y="18" width="-10" height="-2"/>
+ <Sensor black="64" white="16316"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-1 Mark II">
+ <ID make="Pentax" model="K-1 Mark II">Pentax K-1 Mark II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="6" y="28" width="-8" height="0"/>
+ <Sensor black="64" white="16316"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-3">
+ <ID make="Pentax" model="K-3">Pentax K-3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-36" height="0"/>
+ <Sensor black="1" white="15868"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX K-3 II">
+ <ID make="Pentax" model="K-3 II">PENTAX K-3 II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="4" y="0" width="-36" height="0"/>
+ <Sensor black="1" white="15865"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-5" decoder_version="2">
+ <ID make="Pentax" model="K-5">PENTAX K-5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="12" y="0" width="-38" height="-60"/>
+ <Sensor black="512" white="16383"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-5" mode="dng">
+ <ID make="Pentax" model="K-5">PENTAX K-5</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-5 II" decoder_version="2">
+ <ID make="Pentax" model="K-5 II">PENTAX K-5 II</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="12" y="0" width="-34" height="0"/>
+ <Sensor black="512" white="16383"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-5 II s" decoder_version="2">
+ <ID make="Pentax" model="K-5 II s">PENTAX K-5 II s</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="12" y="0" width="-34" height="0"/>
+ <Sensor black="512" white="15863"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-7">
+ <ID make="Pentax" model="K-7">PENTAX K-7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4672" height="3104"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-m">
+ <ID make="Pentax" model="K-m">Pentax K-m</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2616"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-x">
+ <ID make="Pentax" model="K-x">PENTAX K-x</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-44" height="-2"/>
+ <Sensor black="41" white="4095"/>
+ <BlackAreas>
+ <Vertical x="4310" width="40"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-r" decoder_version="3">
+ <ID make="Pentax" model="K-r">PENTAX K-r</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="2" y="2" width="-44" height="0"/>
+ <Sensor black="64" white="4000"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX KP">
+ <ID make="Pentax" model="KP">Pentax KP</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="60" y="32" width="-28" height="-4"/>
+ <Sensor black="128" white="16254"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K200D">
+ <ID make="Pentax" model="K200D">Pentax K200D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-42" height="-10"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K2000">
+ <ID make="Pentax" model="K2000">Pentax K2000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3900" height="2616"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX 645D">
+ <ID make="Pentax" model="645D">Pentax 645D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="54" y="32" width="-50" height="-22"/>
+ <Sensor black="0" white="15767"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX 645Z">
+ <ID make="Pentax" model="645Z">Pentax 645Z</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="48" y="0" width="-60" height="0"/>
+ <Sensor black="1" white="16316"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="EX2F">
+ <ID make="Samsung" model="EX2F">Samsung EX2F</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="22" y="12" width="-148" height="-18"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="EX1">
+ <ID make="Samsung" model="EX1">Samsung EX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="2" width="-8" height="-22"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX1">
+ <ID make="Samsung" model="NX1">Samsung NX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1024" white="16100" iso_min="51200"/>
+ <Sensor black="512" white="16100" iso_min="8000" iso_max="25600"/>
+ <Sensor black="128" white="16100"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX1" mode="12bit">
+ <ID make="Samsung" model="NX1">Samsung NX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="128" white="4000" iso_min="8000"/>
+ <Sensor black="32" white="4000"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX5">
+ <ID make="Samsung" model="NX5">Samsung NX5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="17" y="8" width="4602" height="3068"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX10">
+ <ID make="Samsung" model="NX10">Samsung NX10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="17" y="8" width="4602" height="3068"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ <Horizontal y="0" height="2"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX11">
+ <ID make="Samsung" model="NX11">Samsung NX11</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="17" y="8" width="4602" height="3068"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX100">
+ <ID make="Samsung" model="NX100">Samsung NX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="17" y="8" width="4602" height="3068"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="0" width="8"/>
+ <Horizontal y="0" height="2"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX1000" decoder_version="2">
+ <ID make="Samsung" model="NX1000">Samsung NX1000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="89" y="17" width="-71" height="-55"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="msb_override" value="false"/>
+ </Hints>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX1100" decoder_version="2">
+ <ID make="Samsung" model="NX1100">Samsung NX1100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="89" y="17" width="-71" height="-55"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="msb_override" value="false"/>
+ </Hints>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX20" decoder_version="2">
+ <ID make="Samsung" model="NX20">Samsung NX20</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="89" y="17" width="-71" height="-55"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="msb_override" value="false"/>
+ </Hints>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX200" decoder_version="2">
+ <ID make="Samsung" model="NX200">Samsung NX200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="89" y="17" width="-71" height="-55"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="msb_override" value="false"/>
+ </Hints>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX210" decoder_version="2">
+ <ID make="Samsung" model="NX210">Samsung NX210</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="89" y="17" width="-71" height="-55"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="msb_override" value="false"/>
+ </Hints>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX2000" decoder_version="3">
+ <ID make="Samsung" model="NX2000">Samsung NX2000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="45" y="25" width="-11" height="-29"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX30" decoder_version="3">
+ <ID make="Samsung" model="NX30">Samsung NX30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="45" y="25" width="-11" height="-29"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX300" decoder_version="3">
+ <ID make="Samsung" model="NX300">Samsung NX300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="45" y="25" width="-11" height="-29"/>
+ <Sensor black="0" white="4095"/>
+ <Aliases>
+ <Alias>NX300M</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX3000">
+ <ID make="Samsung" model="NX3000">Samsung NX3000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="96" y="42" width="-32" height="-24"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX500">
+ <ID make="Samsung" model="NX500">Samsung NX500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="-16"/>
+ <Sensor black="1024" white="16100" iso_min="51200"/>
+ <Sensor black="512" white="16100" iso_min="8000" iso_max="25600"/>
+ <Sensor black="128" white="16100"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="NX500" mode="12bit">
+ <ID make="Samsung" model="NX500">Samsung NX500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="-16"/>
+ <Sensor black="128" white="4000" iso_min="8000"/>
+ <Sensor black="32" white="4000"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="EK-GN120" decoder_version="3">
+ <ID make="Samsung" model="EK-GN120">Samsung EK-GN120</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="45" y="25" width="-11" height="-29"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SAMSUNG" model="WB2000">
+ <ID make="Samsung" model="WB2000">Samsung WB2000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="2" width="-10" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX0">
+ <ID make="Sony" model="DSC-RX0">Sony DSC-RX0</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16620"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX10">
+ <ID make="Sony" model="DSC-RX10">Sony DSC-RX10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX10M2">
+ <ID make="Sony" model="DSC-RX10M2">Sony DSC-RX10M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX10M3">
+ <ID make="Sony" model="DSC-RX10M3">Sony DSC-RX10M3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX10M4">
+ <ID make="Sony" model="DSC-RX10M4">Sony DSC-RX10M4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16380"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100">
+ <ID make="Sony" model="DSC-RX100">Sony DSC-RX100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="800" white="16620"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M2">
+ <ID make="Sony" model="DSC-RX100M2">Sony DSC-RX100M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M3">
+ <ID make="Sony" model="DSC-RX100M3">Sony DSC-RX100M3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M4">
+ <ID make="Sony" model="DSC-RX100M4">Sony DSC-RX100M4</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M5">
+ <ID make="Sony" model="DSC-RX100M5">Sony DSC-RX100M5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M5A">
+ <ID make="Sony" model="DSC-RX100M5A">Sony DSC-RX100M5A</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-8" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX100M6">
+ <ID make="Sony" model="DSC-RX100M6">Sony DSC-RX100M6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX1R">
+ <ID make="Sony" model="DSC-RX1R">Sony DSC-RX1R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="512" white="33192" iso_list="50"/>
+ <Sensor black="512" white="16596"/>
+ <BlackAreas>
+ <Vertical x="6032" width="14"/>
+ </BlackAreas>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX1RM2">
+ <ID make="Sony" model="DSC-RX1RM2">Sony DSC-RX1RM2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-40" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A100" decoder_version="1">
+ <ID make="Sony" model="DSLR-A100">Sony DSLR-A100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-1" height="-2"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A200">
+ <ID make="Sony" model="DSLR-A200">Sony DSLR-A200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3878" height="2600"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A230">
+ <ID make="Sony" model="DSLR-A230">Sony DSLR-A230</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A290">
+ <ID make="Sony" model="DSLR-A290">Sony DSLR-A290</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A300">
+ <ID make="Sony" model="DSLR-A300">Sony DSLR-A300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3878" height="2600"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A330">
+ <ID make="Sony" model="DSLR-A330">Sony DSLR-A330</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="3878" height="2600"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A350">
+ <ID make="Sony" model="DSLR-A350">Sony DSLR-A350</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4599" height="3064"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A380">
+ <ID make="Sony" model="DSLR-A380">Sony DSLR-A380</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A390">
+ <ID make="Sony" model="DSLR-A390">Sony DSLR-A390</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A450">
+ <ID make="Sony" model="DSLR-A450">Sony DSLR-A450</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="500" white="16000"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A500">
+ <ID make="Sony" model="DSLR-A500">Sony DSLR-A500</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="500" white="16600"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A550">
+ <ID make="Sony" model="DSLR-A550">Sony DSLR-A550</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="512" white="16372"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A560">
+ <ID make="Sony" model="DSLR-A560">Sony DSLR-A560</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="476" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A580">
+ <ID make="Sony" model="DSLR-A580">Sony DSLR-A580</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-2" height="-2"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A700">
+ <ID make="Sony" model="DSLR-A700">Sony DSLR-A700</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="4288" height="2856"/>
+ <Sensor black="520" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A850">
+ <ID make="Sony" model="DSLR-A850">Sony DSLR-A850</ID>
+ <CFA width="2" height="2">
+ <Color x="1" y="1">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="0" y="0">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4048"/>
+ <Sensor black="500" white="15000"/>
+ </Camera>
+ <Camera make="SONY" model="DSLR-A900">
+ <ID make="Sony" model="DSLR-A900">Sony DSLR-A900</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="6080" height="4048"/>
+ <Sensor black="520" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-3">
+ <ID make="Sony" model="NEX-3">Sony NEX-3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16360"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-3N">
+ <ID make="Sony" model="NEX-3N">Sony NEX-3N</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-10" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-5">
+ <ID make="Sony" model="NEX-5">Sony NEX-5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-5N">
+ <ID make="Sony" model="NEX-5N">Sony NEX-5N</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-5R">
+ <ID make="Sony" model="NEX-5R">Sony NEX-5R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-5T">
+ <ID make="Sony" model="NEX-5T">Sony NEX-5T</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-12" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-6">
+ <ID make="Sony" model="NEX-6">Sony NEX-6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-16" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <!-- Measured on images from https://github.com/klauspost/rawspeed/issues/78 -->
+ <Camera make="SONY" model="NEX-7">
+ <ID make="Sony" model="NEX-7">Sony NEX-7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-C3">
+ <ID make="Sony" model="NEX-C3">Sony NEX-C3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="NEX-F3">
+ <ID make="Sony" model="NEX-F3">Sony NEX-F3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16360"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-3000">
+ <ID make="Sony" model="ILCE-3000">Sony ILCE-3000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-3500">
+ <ID make="Sony" model="ILCE-3500">Sony ILCE-3500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-5000">
+ <ID make="Sony" model="ILCE-5000">Sony ILCE-5000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-34" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-5100">
+ <ID make="Sony" model="ILCE-5100">Sony ILCE-5100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-6000">
+ <ID make="Sony" model="ILCE-6000">Sony ILCE-6000</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-6300">
+ <ID make="Sony" model="ILCE-6300">Sony ILCE-6300</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-6400">
+ <ID make="Sony" model="ILCE-6400">Sony ILCE-6400</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-22" height="0"/>
+ <Sensor black="512" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-6500">
+ <ID make="Sony" model="ILCE-6500">Sony ILCE-6500</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7">
+ <ID make="Sony" model="ILCE-7">Sony ILCE-7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7M2">
+ <ID make="Sony" model="ILCE-7M2">Sony ILCE-7M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7M3">
+ <ID make="Sony" model="ILCE-7M3">Sony ILCE-7M3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7R">
+ <ID make="Sony" model="ILCE-7R">Sony ILCE-7R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7RM2">
+ <ID make="Sony" model="ILCE-7RM2">Sony ILCE-7RM2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7RM3">
+ <ID make="Sony" model="ILCE-7RM3">Sony ILCE-7RM3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-20" height="0"/>
+ <Sensor black="512" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7S">
+ <ID make="Sony" model="ILCE-7S">Sony ILCE-7S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-7SM2">
+ <ID make="Sony" model="ILCE-7SM2">Sony ILCE-7SM2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCE-9">
+ <ID make="Sony" model="ILCE-9">Sony ILCE-9</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="512" white="16383"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-RX1">
+ <ID make="Sony" model="DSC-RX1">Sony DSC-RX1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-18" height="0"/>
+ <Sensor black="520" white="16628"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A33">
+ <ID make="Sony" model="SLT-A33">Sony SLT-A33</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A35">
+ <ID make="Sony" model="SLT-A35">Sony SLT-A35</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="545" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A37">
+ <ID make="Sony" model="SLT-A37">Sony SLT-A37</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-10" height="0"/>
+ <Sensor black="520" white="16500"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A55">
+ <ID make="Sony" model="SLT-A55">Sony SLT-A55</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="520" white="16596"/>
+ <Aliases>
+ <Alias id="SLT-A55">SLT-A55V</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="SONY" model="SLT-A57">
+ <ID make="Sony" model="SLT-A57">Sony SLT-A57</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="512" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A58">
+ <ID make="Sony" model="SLT-A58">Sony SLT-A58</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-36" height="0"/>
+ <Sensor black="520" white="16596"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A65">
+ <ID make="Sony" model="SLT-A65">Sony SLT-A65</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-30" height="0"/>
+ <Sensor black="520" white="16596"/>
+ <Aliases>
+ <Alias id="SLT-A65">SLT-A65V</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="SONY" model="SLT-A77">
+ <ID make="Sony" model="SLT-A77">Sony SLT-A77</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="520" white="16596"/>
+ <Aliases>
+ <Alias id="SLT-A77">SLT-A77V</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="SONY" model="ILCA-77M2">
+ <ID make="Sony" model="ILCA-77M2">Sony ILCA-77M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-26" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCA-68">
+ <ID make="Sony" model="ILCA-68">Sony ILCA-68</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="ILCA-99M2">
+ <ID make="Sony" model="ILCA-99M2">Sony ILCA-99M2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="512" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="SLT-A99">
+ <ID make="Sony" model="SLT-A99">Sony SLT-A99</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-28" height="0"/>
+ <Sensor black="520" white="16596"/>
+ <Aliases>
+ <Alias id="SLT-A99">SLT-A99V</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Sinar Photography AG" model="Sinar Hy6/ Sinarback eXact" mode="dng">
+ <ID make="Sinar" model="Hy6">Sinar Hy6</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="0"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S6000fd">
+ <ID make="Fujifilm" model="FinePix S6000fd">Fujifilm FinePix S6000fd</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S100FS">
+ <ID make="Fujifilm" model="FinePix S100FS">Fujifilm FinePix S100FS</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="64" y="0" width="-64" height="0"/>
+ <Sensor black="512" white="16383"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S200EXR">
+ <ID make="Fujifilm" model="FinePix S200EXR">Fujifilm FinePix S200EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="28" y="0" width="-28" height="0"/>
+ <Sensor black="519" white="16250"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix F600EXR">
+ <ID make="Fujifilm" model="FinePix F600EXR">Fujifilm FinePix F600EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="34" y="0" width="-32" height="0"/>
+ <Sensor black="256" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix F700">
+ <ID make="Fujifilm" model="FinePix F700">Fujifilm FinePix F700</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="16" y="0" width="-16" height="0"/>
+ <Sensor black="0" white="16383"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ <Hint name="double_width_unpacked" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix F770EXR">
+ <ID make="Fujifilm" model="FinePix F770EXR">Fujifilm FinePix F770EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="38" y="0" width="-34" height="0"/>
+ <Sensor black="256" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix F900EXR">
+ <ID make="Fujifilm" model="FinePix F900EXR">Fujifilm FinePix F900EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="256" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix E550">
+ <ID make="Fujifilm" model="FinePix E550">Fujifilm FinePix E550</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15875"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="IS-1">
+ <ID make="Fujifilm" model="IS-1">Fujifilm IS-1</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S3Pro">
+ <ID make="Fujifilm" model="FinePix S3Pro">Fujifilm FinePix S3Pro</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="2" width="-32" height="-2"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S5Pro">
+ <ID make="Fujifilm" model="FinePix S5Pro">Fujifilm FinePix S5Pro</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="2" width="-32" height="-2"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S5600">
+ <ID make="Fujifilm" model="FinePix S5600">Fujifilm FinePix S5600</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="30" y="0" width="-30" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix E900">
+ <ID make="Fujifilm" model="FinePix E900">Fujifilm FinePix E900</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePixS2Pro">
+ <ID make="Fujifilm" model="FinePix S2Pro">Fujifilm FinePix S2Pro</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="32" width="-32" height="-32"/>
+ <Sensor black="128" white="4095"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S5000">
+ <ID make="Fujifilm" model="FinePix S5000">Fujifilm FinePix S5000</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="24" y="4" width="-24" height="-4"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S5200">
+ <ID make="Fujifilm" model="FinePix S5200">Fujifilm FinePix S5200</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="30" y="4" width="-30" height="-4"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S5500">
+ <ID make="Fujifilm" model="FinePix S5500">Fujifilm FinePix S5500</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15872"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S6500fd">
+ <ID make="Fujifilm" model="FinePix S6500fd">Fujifilm FinePix S6500fd</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="64" y="0" width="-64" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S9500">
+ <ID make="Fujifilm" model="FinePix S9500">Fujifilm FinePix S9500</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Aliases>
+ <Alias>FinePix S9000</Alias>
+ </Aliases>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S9600">
+ <ID make="Fujifilm" model="FinePix S9600">Fujifilm FinePix S9600</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="32" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix S9600fd">
+ <ID make="Fujifilm" model="FinePix S9600fd">Fujifilm FinePix S9600fd</ID>
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="64" y="0" width="-64" height="0"/>
+ <Sensor black="0" white="15872"/>
+ <Hints>
+ <Hint name="fuji_rotate" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix HS10 HS11">
+ <ID make="Fujifilm" model="FinePix HS10 HS11">Fujifilm FinePix HS10 HS11</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="50" white="3900"/>
+ <Hints>
+ <Hint name="jpeg32_bitorder" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix HS20EXR">
+ <ID make="Fujifilm" model="FinePix HS20EXR">Fujifilm FinePix HS20EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="34" y="0" width="-32" height="0"/>
+ <Sensor black="256" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix HS30EXR">
+ <ID make="Fujifilm" model="FinePix HS30EXR">Fujifilm FinePix HS30EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="34" y="0" width="-32" height="0"/>
+ <Sensor black="258" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix HS50EXR">
+ <ID make="Fujifilm" model="FinePix HS50EXR">Fujifilm FinePix HS50EXR</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="256" white="3900"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="FinePix X100">
+ <ID make="Fujifilm" model="FinePix X100">Fujifilm FinePix X100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="73" y="1" width="-73" height="-1"/>
+ <Sensor black="254" white="4000"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X10">
+ <ID make="Fujifilm" model="FinePix X10">Fujifilm FinePix X10</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="48" y="0" width="-48" height="0"/>
+ <Sensor black="256" white="4000"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-S1">
+ <ID make="Fujifilm" model="X-S1">Fujifilm X-S1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="48" y="0" width="-48" height="0"/>
+ <Sensor black="260" white="4000"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="GFX 50S">
+ <ID make="Fujifilm" model="GFX 50S">Fujifilm GFX 50S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-938" height="-4"/>
+ <Sensor black="65" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="GFX 50S" mode="compressed">
+ <ID make="Fujifilm" model="GFX 50S">Fujifilm GFX 50S</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-938" height="-4"/>
+ <Sensor black="65" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="GFX 50R">
+ <ID make="Fujifilm" model="GFX 50R">Fujifilm GFX 50R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-938" height="-4"/>
+ <Sensor black="65" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="GFX 50R" mode="compressed">
+ <ID make="Fujifilm" model="GFX 50R">Fujifilm GFX 50R</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-938" height="-4"/>
+ <Sensor black="65" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-Pro1">
+ <ID make="Fujifilm" model="X-Pro1">Fujifilm X-Pro1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="256" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-Pro2">
+ <ID make="Fujifilm" model="X-Pro2">Fujifilm X-Pro2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-Pro2" mode="compressed">
+ <ID make="Fujifilm" model="X-Pro2">Fujifilm X-Pro2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-16" height="-12"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X70">
+ <ID make="Fujifilm" model="X70">Fujifilm X70</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="XF1">
+ <ID make="Fujifilm" model="XF1">Fujifilm XF1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="48" y="0" width="-48" height="0"/>
+ <Sensor black="257" white="4000"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-E1">
+ <ID make="Fujifilm" model="X-E1">Fujifilm X-E1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="255" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X20">
+ <ID make="Fujifilm" model="X20">Fujifilm X20</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GBGGRG</ColorRow>
+ <ColorRow y="1">RGRBGB</ColorRow>
+ <ColorRow y="2">GBGGRG</ColorRow>
+ <ColorRow y="3">GRGGBG</ColorRow>
+ <ColorRow y="4">BGBRGR</ColorRow>
+ <ColorRow y="5">GRGGBG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="257" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X30">
+ <ID make="Fujifilm" model="X30">Fujifilm X30</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GBGGRG</ColorRow>
+ <ColorRow y="1">RGRBGB</ColorRow>
+ <ColorRow y="2">GBGGRG</ColorRow>
+ <ColorRow y="3">GRGGBG</ColorRow>
+ <ColorRow y="4">BGBRGR</ColorRow>
+ <ColorRow y="5">GRGGBG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="257" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X100F">
+ <ID make="Fujifilm" model="X100F">Fujifilm X100F</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X100F" mode="compressed">
+ <ID make="Fujifilm" model="X100F">Fujifilm X100F</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-16" height="-12"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X100S">
+ <ID make="Fujifilm" model="X100S">Fujifilm X100S</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X100T">
+ <ID make="Fujifilm" model="X100T">Fujifilm X100T</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-H1">
+ <ID make="Fujifilm" model="X-H1">Fujifilm X-H1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-132" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-H1" mode="compressed">
+ <ID make="Fujifilm" model="X-H1">Fujifilm X-H1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="18" width="-18" height="-6"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-M1">
+ <ID make="Fujifilm" model="X-M1">Fujifilm X-M1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="256" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-A1">
+ <ID make="Fujifilm" model="X-A1">Fujifilm X-A1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="256" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-A2">
+ <ID make="Fujifilm" model="X-A2">Fujifilm X-A2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-168" height="0"/>
+ <Sensor black="256" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-A3">
+ <ID make="Fujifilm" model="X-A3">Fujifilm X-A3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ <Hints>
+ <Hint name="jpeg32_bitorder" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-A5">
+ <ID make="Fujifilm" model="X-A5">Fujifilm X-A5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ <Hints>
+ <Hint name="jpeg32_bitorder" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="FUJIFILM" model="XQ1">
+ <ID make="Fujifilm" model="XQ1">Fujifilm XQ1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GBGGRG</ColorRow>
+ <ColorRow y="1">RGRBGB</ColorRow>
+ <ColorRow y="2">GBGGRG</ColorRow>
+ <ColorRow y="3">GRGGBG</ColorRow>
+ <ColorRow y="4">BGBRGR</ColorRow>
+ <ColorRow y="5">GRGGBG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="257" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="XQ2">
+ <ID make="Fujifilm" model="XQ2">Fujifilm XQ2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GBGGRG</ColorRow>
+ <ColorRow y="1">RGRBGB</ColorRow>
+ <ColorRow y="2">GBGGRG</ColorRow>
+ <ColorRow y="3">GRGGBG</ColorRow>
+ <ColorRow y="4">BGBRGR</ColorRow>
+ <ColorRow y="5">GRGGBG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-64" height="0"/>
+ <Sensor black="257" white="4094"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-E2">
+ <ID make="Fujifilm" model="X-E2">Fujifilm X-E2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-E3">
+ <ID make="Fujifilm" model="X-E3">Fujifilm X-E3</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-128" height="0"/>
+ <Sensor black="1023" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-E3" mode="compressed">
+ <ID make="Fujifilm" model="X-E3">Fujifilm X-E3</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="18" width="-24" height="-12"/>
+ <Sensor black="1023" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-E2S">
+ <ID make="Fujifilm" model="X-E2S">Fujifilm X-E2S</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T1">
+ <ID make="Fujifilm" model="X-T1">Fujifilm X-T1</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T2">
+ <ID make="Fujifilm" model="X-T2">Fujifilm X-T2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-132" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T2" mode="compressed">
+ <ID make="Fujifilm" model="X-T2">Fujifilm X-T2</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-16" height="-12"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T3">
+ <ID make="Fujifilm" model="X-T3">Fujifilm X-T3</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="8" width="-134" height="-6"/>
+ <Sensor black="1022" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T3" mode="compressed">
+ <ID make="Fujifilm" model="X-T3">Fujifilm X-T3</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="8" width="-134" height="-6"/>
+ <Sensor black="1022" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T10">
+ <ID make="Fujifilm" model="X-T10">Fujifilm X-T10</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="0" width="-52" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T20">
+ <ID make="Fujifilm" model="X-T20">Fujifilm X-T20</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">RBGBRG</ColorRow>
+ <ColorRow y="1">GGRGGB</ColorRow>
+ <ColorRow y="2">GGBGGR</ColorRow>
+ <ColorRow y="3">BRGRBG</ColorRow>
+ <ColorRow y="4">GGBGGR</ColorRow>
+ <ColorRow y="5">GGRGGB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-126" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T20" mode="compressed">
+ <ID make="Fujifilm" model="X-T20">Fujifilm X-T20</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-16" height="-12"/>
+ <Sensor black="1024" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T30">
+ <ID make="Fujifilm" model="X-T30">Fujifilm X-T30</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-132" height="-6"/>
+ <Sensor black="1022" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T30" mode="compressed">
+ <ID make="Fujifilm" model="X-T30">Fujifilm X-T30</ID>
+ <CFA2 width="6" height="6">
+ <ColorRow y="0">GGRGGB</ColorRow>
+ <ColorRow y="1">GGBGGR</ColorRow>
+ <ColorRow y="2">BRGRBG</ColorRow>
+ <ColorRow y="3">GGBGGR</ColorRow>
+ <ColorRow y="4">GGRGGB</ColorRow>
+ <ColorRow y="5">RBGBRG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="12" width="-132" height="-6"/>
+ <Sensor black="1022" white="16383"/>
+ </Camera>
+ <Camera make="FUJIFILM" model="X-T100">
+ <ID make="Fujifilm" model="X-T100">Fujifilm X-T100</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ <Hints>
+ <Hint name="jpeg32_bitorder" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="KONICA MINOLTA" model="DYNAX 5D">
+ <ID make="Minolta" model="Dynax 5D">Konica Minolta Maxxum 5D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Aliases>
+ <Alias id="Maxxum 5D">MAXXUM 5D</Alias>
+ <Alias id="Alpha 5D">ALPHA 5D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="KONICA MINOLTA" model="DYNAX 7D">
+ <ID make="Minolta" model="Dynax 7D">Konica Minolta Maxxum 7D</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Aliases>
+ <Alias id="Maxxum 7D">MAXXUM 7D</Alias>
+ <Alias id="Alpha 7D">ALPHA 7D</Alias>
+ </Aliases>
+ </Camera>
+ <Camera make="Minolta Co., Ltd." model="DiMAGE A1">
+ <ID make="Minolta" model="DiMAGE A1">Minolta DiMAGE A1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="Konica Minolta Camera, Inc." model="DiMAGE A2">
+ <ID make="Minolta" model="DiMAGE A2">Konica Minolta DiMAGE A2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="KONICA MINOLTA" model="DiMAGE A200">
+ <ID make="Minolta" model="DiMAGE A200">Konica Minolta DiMAGE A200</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ <Hints>
+ <Hint name="swapped_wb" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Minolta Co., Ltd." model="DiMAGE 5">
+ <ID make="Minolta" model="DiMAGE 5">Minolta DiMAGE 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="Minolta Co., Ltd." model="DiMAGE 7">
+ <ID make="Minolta" model="DiMAGE 7">Minolta DiMAGE 7</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="Minolta Co., Ltd." model="DiMAGE 7i">
+ <ID make="Minolta" model="DiMAGE 7i">Minolta DiMAGE 7i</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="Minolta Co., Ltd." model="DiMAGE 7Hi">
+ <ID make="Minolta" model="DiMAGE 7Hi">Minolta DiMAGE 7Hi</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3965"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-F828">
+ <ID make="Sony" model="DSC-F828">Sony DSC-F828</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">YELLOW</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="5" y="0" width="-67" height="0"/>
+ <Sensor black="495" white="16383"/>
+ <Hints>
+ <Hint name="srf_format" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="SONY" model="DSC-HX99">
+ <ID make="Sony" model="DSC-HX99">Sony DSC-HX99</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="10" y="0" width="-20" height="-10"/>
+ <Sensor black="800" white="16300"/>
+ </Camera>
+ <Camera make="SONY" model="DSC-R1">
+ <ID make="Sony" model="DSC-R1">Sony DSC-R1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="-60" height="0"/>
+ <Sensor black="511" white="16383"/>
+ <Hints>
+ <Hint name="sr2_format" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="Mamiya-OP Co.,Ltd." model="MAMIYA ZD">
+ <ID make="Mamiya" model="ZD">Mamiya ZD</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4000"/>
+ </Camera>
+ <Camera make="Creo/Leaf" model="Leaf Aptus 22(LF3779 )/Hasselblad H1">
+ <ID make="Leaf" model="Aptus 22">Leaf Aptus 22</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="16191"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus 75(LI400146 )/Large Format" supported="no">
+ <ID make="Leaf" model="Aptus 75">Leaf Aptus 75</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="16191"/>
+ </Camera>
+ <Camera make="Leaf" model="Credo 40">
+ <ID make="Leaf" model="Credo 40">Leaf Credo 40</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="8" width="-20" height="-48"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="Leaf" model="Credo 60">
+ <ID make="Leaf" model="Credo 60">Leaf Credo 60</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="36" y="6" width="-16" height="-46"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="Leaf" model="Credo 80">
+ <ID make="Leaf" model="Credo 80">Leaf Credo 80</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="36" y="8" width="-16" height="-48"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus-II 5(LI300059 )/Mamiya 645 AFD">
+ <ID make="Leaf" model="Aptus-II 5">Leaf Aptus-II 5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus-II 8(LI300247 )/Mamiya 645 AFD" supported="no">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">BLUE</Color>
+ <Color x="0" y="1">RED</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf AFi-II 7(BT12701 )/Leaf AFi" supported="no">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus-II 10(LI300019 )/Phase One 645DF" supported="no">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus-II 10R( )/Large Format" supported="no">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Leaf" model="Leaf Aptus-II 12(LI301306 )/Phase One 645DF/645AF" supported="no">
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="15000"/>
+ </Camera>
+ <Camera make="Phase One A/S" model="P30">
+ <ID make="Phase One" model="P30">Phase One P30</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="50" y="28" width="-24" height="-32"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="Phase One A/S" model="P65+">
+ <ID make="Phase One" model="P65+">Phase One P65+</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="12" width="-18" height="-48"/>
+ <Sensor black="0" white="16383"/>
+ </Camera>
+ <Camera make="Phase One A/S" model="IQ140">
+ <ID make="Phase One" model="IQ140">Phase One IQ140</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="42" y="8" width="-20" height="-54"/>
+ <Sensor black="0" white="65535"/>
+ </Camera>
+ <Camera make="Phase One A/S" model="IQ250">
+ <ID make="Phase One" model="IQ250">Phase One IQ250</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="92" y="108" width="0" height="0"/>
+ <Sensor black="1024" white="65535"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS Pro SLR/n">
+ <ID make="Kodak" model="DCS Pro SLR/n">Kodak DCS Pro SLR/n</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS Pro 14N">
+ <ID make="Kodak" model="DCS Pro 14N">Kodak DCS Pro 14n</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3700"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS Pro 14nx">
+ <ID make="Kodak" model="DCS Pro 14nx">Kodak DCS Pro 14nx</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3700"/>
+ </Camera>
+ <Camera make="KODAK" model="DCS460D FILE VERSION 3">
+ <ID make="Kodak" model="DCS460D">Kodak DCS460</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="3" y="0" width="-3" height="0"/>
+ <Sensor black="7" white="6664"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS520C">
+ <ID make="Kodak" model="DCS520C">Kodak DCS520C</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="176" white="4095"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS560C">
+ <ID make="Kodak" model="DCS560C">Kodak DCS560C</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="208" white="4095"/>
+ </Camera>
+ <Camera make="KODAK" model="EOSDCS1B FILE VERSION 3">
+ <ID make="Kodak" model="EOS DCS 1">Kodak EOSDCS1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="3" y="0" width="-3" height="0"/>
+ <Sensor black="19" white="7638"/>
+ </Camera>
+ <Camera make="KODAK" model="EOSDCS3C FILE VERSION 3">
+ <ID make="Kodak" model="EOS DCS 3">Kodak EOSDCS3</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="6" y="0" width="-6" height="0"/>
+ <Sensor black="0" white="7855"/>
+ </Camera>
+ <Camera make="Kodak" model="DCS760C">
+ <ID make="Kodak" model="DCS760C">Kodak DCS760C</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">GREEN</Color>
+ <Color x="1" y="0">RED</Color>
+ <Color x="0" y="1">BLUE</Color>
+ <Color x="1" y="1">GREEN</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3500"/>
+ </Camera>
+ <Camera make="EASTMAN KODAK COMPANY" model="KODAK P880 ZOOM DIGITAL CAMERA">
+ <ID make="Kodak" model="P880">Kodak P880</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ </Camera>
+ <Camera make="Eastman Kodak Company" model="Kodak Digital Science DC50 Zoom Camera" supported="no">
+ <ID make="Kodak" model="DC50">Kodak Digital Science DC50 Zoom Camera</ID>
+ </Camera>
+ <Camera make="Eastman Kodak Company" model="Kodak DC120 ZOOM Digital Camera" supported="no">
+ <ID make="Kodak" model="DC120">Kodak DC120 ZOOM Digital Camera</ID>
+ </Camera>
+ <Camera make="EASTMAN KODAK COMPANY" model="KODAK EasyShare Z980 Digital Camera">
+ <ID make="Kodak" model="EasyShare Z980">Kodak Z980</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="easyshare_offset_hack" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="EASTMAN KODAK COMPANY" model="KODAK EASYSHARE Z1015 IS DIGITAL CAMERA">
+ <ID make="Kodak" model="Z1015 IS">Kodak Z1015 IS</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">BLUE</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">RED</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="easyshare_offset_hack" value=""/>
+ </Hints>
+ </Camera>
+ <Camera make="SEIKO EPSON CORP." model="R-D1">
+ <ID make="Epson" model="R-D1">Epson R-D1</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="63" white="4095"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Hasselblad 500 mech.">
+ <ID make="Hasselblad" model="CFV">Hasselblad 16-Uncoated</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="32767"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Hasselblad CFV-50">
+ <ID make="Hasselblad" model="CFV-50">Hasselblad 50-Coated</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="48" y="6" width="-42" height="-82"/>
+ <Sensor black="256" white="65535"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Hasselblad H3D">
+ <ID make="Hasselblad" model="H3D">Hasselblad 39-Coated</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="10" y="6" width="-8" height="-10"/>
+ <Sensor black="0" white="31456"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Hasselblad H5D-40">
+ <ID make="Hasselblad" model="H5D-40">Hasselblad 40-Coated5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="44" y="6" width="-42" height="-82"/>
+ <Sensor black="256" white="62914"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Hasselblad H5D-50c">
+ <ID make="Hasselblad" model="H5D-50c">Hasselblad 50-15-Coated5</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="50" y="104" width="-48" height="0"/>
+ <Sensor black="256" white="62914"/>
+ </Camera>
+ <Camera make="Hasselblad" model="Flash Sync">
+ <ID make="Hasselblad" model="CF132">Hasselblad 22-Uncoated</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="62914"/>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="PENTAX 645Z" mode="dng">
+ <ID make="Pentax" model="645Z">PENTAX 645Z</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX 645D" mode="dng">
+ <ID make="Pentax" model="645D">PENTAX 645D</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX 645Z" mode="dng">
+ <ID make="Pentax" model="645Z">PENTAX 645Z</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-01" mode="dng">
+ <ID make="Pentax" model="K-01">PENTAX K-01</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-30" mode="dng">
+ <ID make="Pentax" model="K-30">PENTAX K-30</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-50" mode="dng">
+ <ID make="Pentax" model="K-50">PENTAX K-50</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-500" mode="dng">
+ <ID make="Pentax" model="K-500">PENTAX K-500</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX Q" mode="dng">
+ <ID make="Pentax" model="Q">PENTAX Q</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX Q7" mode="dng">
+ <ID make="Pentax" model="Q7">PENTAX Q7</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX Q10" mode="dng">
+ <ID make="Pentax" model="Q10">PENTAX Q10</ID>
+ </Camera>
+ <Camera make="PENTAX RICOH IMAGING" model="PENTAX MX-1" mode="dng">
+ <ID make="Pentax" model="MX-1">PENTAX MX-1</ID>
+ </Camera>
+ <Camera make="Leica Camera AG" model="M8 Digital Camera" mode="dng">
+ <ID make="Leica" model="M8">M8 Digital Camera</ID>
+ </Camera>
+ <Camera make="Leica Camera AG" model="LEICA M (Typ 240)" mode="dng">
+ <ID make="Leica" model="M (Typ 240)">LEICA M (Typ 240)</ID>
+ </Camera>
+ <Camera make="LEICA CAMERA AG" model="LEICA X2" mode="dng">
+ <ID make="Leica" model="X2">LEICA X2</ID>
+ </Camera>
+ <Camera make="RICOH IMAGING COMPANY, LTD." model="GR" mode="dng">
+ <ID make="Ricoh" model="GR">GR</ID>
+ </Camera>
+ <Camera make="RICOH" model="GR DIGITAL 2" mode="dng">
+ <ID make="Ricoh" model="GR II">Ricoh GR DIGITAL 2</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot A720 IS" mode="dng">
+ <ID make="Canon" model="PowerShot A720 IS">Canon PowerShot A720 IS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX40 HS" mode="dng">
+ <ID make="Canon" model="PowerShot SX40 HS">Canon PowerShot SX40 HS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX100 IS" mode="dng">
+ <ID make="Canon" model="PowerShot SX100 IS">Canon PowerShot SX100 IS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX130 IS" mode="dng">
+ <ID make="Canon" model="PowerShot SX130 IS">Canon PowerShot SX130 IS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX260 HS" mode="dng">
+ <ID make="Canon" model="PowerShot SX260 HS">Canon PowerShot SX260 HS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SX510 HS" mode="dng">
+ <ID make="Canon" model="PowerShot SX510 HS">Canon PowerShot SX510 HS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot A3200 IS" mode="dng">
+ <ID make="Canon" model="PowerShot A3200 IS">Canon PowerShot A3200 IS</ID>
+ </Camera>
+ <Camera make="Canon" model="Canon PowerShot SD450" mode="dng">
+ <ID make="Canon" model="PowerShot SD450">Canon PowerShot SD450</ID>
+ </Camera>
+ <Camera make="OnePlus" model="One A0001" mode="dng">
+ <ID make="OnePlus" model="One">OnePlus One</ID>
+ </Camera>
+ <Camera make="SAMSUNG DIGITAL IMA" model="SAMSUNG GX20" mode="dng">
+ <ID make="Samsung" model="GX20">Samsung GX20</ID>
+ </Camera>
+ <Camera make="samsung" model="SM-G920F">
+ <ID make="Samsung" model="G920F">Samsung G920F</ID>
+ </Camera>
+ <Camera make="samsung" model="SM-G935F">
+ <ID make="Samsung" model="G935F">Samsung G935F</ID>
+ </Camera>
+ <Camera make="SAMSUNG TECHWIN Co." model="SAMSUNG GX10">
+ <ID make="Samsung" model="GX10">Samsung GX10</ID>
+ </Camera>
+ <Camera make="SAMSUNG TECHWIN Co." model="SAMSUNG GX20">
+ <ID make="Samsung" model="GX20">Samsung GX20</ID>
+ </Camera>
+ <Camera make="Nokia" model="Lumia 1020" mode="dng">
+ <ID make="Nokia" model="Lumia 1020">Nokia Lumia 1020</ID>
+ </Camera>
+ <Camera make="PENTAX" model="PENTAX K-r" mode="dng">
+ <ID make="Pentax" model="K-r">PENTAX K-r</ID>
+ </Camera>
+ <Camera make="PENTAX Corporation" model="PENTAX K10D" mode="dng">
+ <ID make="Pentax" model="K10D">PENTAX K10D</ID>
+ </Camera>
+ <Camera make="Nikon" model="LS-5000" mode="dng">
+ <ID make="Nikon" model="LS-5000">Nikon LS-5000</ID>
+ </Camera>
+ <Camera make="SIGMA" model="SIGMA DP1" mode="dng" supported="no">
+ <ID make="Sigma" model="DP1">Sigma DP1</ID>
+ </Camera>
+ <Camera make="SIGMA" model="SIGMA DP1 Merrill" mode="dng" supported="no">
+ <ID make="Sigma" model="DP1 Merrill">Sigma DP1 Merrill</ID>
+ </Camera>
+ <Camera make="LGE" model="Nexus 5X" mode="dng">
+ <ID make="LG" model="Nexus 5X">LG Nexus 5X</ID>
+ </Camera>
+ <Camera make="LGE" model="LG-D855" mode="dng">
+ <ID make="LG" model="D855">LG D855</ID>
+ </Camera>
+ <Camera make="LGE" model="LG-US996" mode="dng">
+ <ID make="LG" model="US996">LG US996</ID>
+ </Camera>
+ <Camera make="LG Mobile" model="LG-H815" mode="dng">
+ <ID make="LG" model="H815">LG H815</ID>
+ </Camera>
+ <Camera make="LG Mobile" model="VS995" mode="dng">
+ <ID make="LG" model="VS995">LG VS995</ID>
+ </Camera>
+ <!-- CHDK Cameras -->
+ <Camera make="AVT" model="F-080C" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="786432"/>
+ <Hint name="full_width" value="1024"/>
+ <Hint name="full_height" value="768"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-145C" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="1447680"/>
+ <Hint name="full_width" value="1392"/>
+ <Hint name="full_height" value="1040"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-201C" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="1920000"/>
+ <Hint name="full_width" value="1600"/>
+ <Hint name="full_height" value="1200"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-510C" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="5067304"/>
+ <Hint name="full_width" value="2588"/>
+ <Hint name="full_height" value="1958"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-510C" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="5067316"/>
+ <Hint name="full_width" value="2588"/>
+ <Hint name="full_height" value="1958"/>
+ <Hint name="offset" value="12"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-510C" supported="no" mode="chdk-c">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="10134608"/>
+ <Hint name="full_width" value="2588"/>
+ <Hint name="full_height" value="1958"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-510C" supported="no" mode="chdk-d">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="10134620"/>
+ <Hint name="full_width" value="2588"/>
+ <Hint name="full_height" value="1958"/>
+ <Hint name="offset" value="12"/>
+ </Hints>
+ </Camera>
+ <Camera make="AVT" model="F-810C" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="16157136"/>
+ <Hint name="full_width" value="3272"/>
+ <Hint name="full_height" value="2469"/>
+ </Hints>
+ </Camera>
+ <Camera make="AgfaPhoto" model="DC-833m" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="15980544"/>
+ <Hint name="full_width" value="3264"/>
+ <Hint name="full_height" value="2448"/>
+ </Hints>
+ </Camera>
+ <Camera make="Alcatel" model="5035D" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="9631728"/>
+ <Hint name="full_width" value="2532"/>
+ <Hint name="full_height" value="1902"/>
+ </Hints>
+ </Camera>
+ <Camera make="Baumer" model="TXG14" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="2868726"/>
+ <Hint name="full_width" value="1384"/>
+ <Hint name="full_height" value="1036"/>
+ <Hint name="offset" value="1078"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SD300" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="12" y="12" width="-44" height="-2"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="5298000"/>
+ <Hint name="full_width" value="2400"/>
+ <Hint name="full_height" value="1766"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A460" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="4" y="4" width="-44" height="-4"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="6553440"/>
+ <Hint name="full_width" value="2664"/>
+ <Hint name="full_height" value="1968"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A610" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="12" y="8" width="-44" height="0"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="2632" width="40"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="6573120"/>
+ <Hint name="full_width" value="2672"/>
+ <Hint name="full_height" value="1968"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A530" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="10" y="6" width="-42" height="-2"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="6653280"/>
+ <Hint name="full_width" value="2672"/>
+ <Hint name="full_height" value="1992"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot S3 IS" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="44" y="8" width="-4" height="0"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="0" width="40"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="7710960"/>
+ <Hint name="full_width" value="2888"/>
+ <Hint name="full_height" value="2136"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A620" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="36" y="12" width="-4" height="0"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="0" width="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="9219600"/>
+ <Hint name="full_width" value="3152"/>
+ <Hint name="full_height" value="2340"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A470" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="12" y="7" width="-44" height="-13"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="9243240"/>
+ <Hint name="full_width" value="3152"/>
+ <Hint name="full_height" value="2346"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A720 IS" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="6" y="5" width="-32" height="-3"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="3306" width="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="10341600"/>
+ <Hint name="full_width" value="3336"/>
+ <Hint name="full_height" value="2480"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A630" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="12" y="6" width="-44" height="-6"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="0" width="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="10383120"/>
+ <Hint name="full_width" value="3344"/>
+ <Hint name="full_height" value="2484"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A640" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="12" y="6" width="-52" height="-6"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="3686" width="50"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="12945240"/>
+ <Hint name="full_width" value="3736"/>
+ <Hint name="full_height" value="2772"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A650" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="48" y="12" width="-24" height="-12"/>
+ <Sensor black="0" white="1023"/>
+ <BlackAreas>
+ <Vertical x="0" width="45"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="15636240"/>
+ <Hint name="full_width" value="4104"/>
+ <Hint name="full_height" value="3048"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SX110 IS" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="6" y="12" width="-30" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <BlackAreas>
+ <Vertical x="3690" width="30"/>
+ </BlackAreas>
+ <Hints>
+ <Hint name="filesize" value="15467760"/>
+ <Hint name="full_width" value="3720"/>
+ <Hint name="full_height" value="2772"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SX120 IS" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="12" y="9" width="-44" height="-9"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="15534576"/>
+ <Hint name="full_width" value="3728"/>
+ <Hint name="full_height" value="2778"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SX20 IS" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="24" y="12" width="-24" height="-12"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="18653760"/>
+ <Hint name="full_width" value="4080"/>
+ <Hint name="full_height" value="3048"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SX220 HS" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="92" y="16" width="-4" height="-1"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="19131120"/>
+ <Hint name="full_width" value="4168"/>
+ <Hint name="full_height" value="3060"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot SX30 IS" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="25" y="10" width="-73" height="-12"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="21936096"/>
+ <Hint name="full_width" value="4464"/>
+ <Hint name="full_height" value="3276"/>
+ </Hints>
+ </Camera>
+ <Camera make="Canon" model="PowerShot A3300 IS" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="8" y="16" width="-56" height="-8"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="24724224"/>
+ <Hint name="full_width" value="4704"/>
+ <Hint name="full_height" value="3504"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-2000UX" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="2" width="0" height="-1"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="1976352"/>
+ <Hint name="full_width" value="1632"/>
+ <Hint name="full_height" value="1211"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-3*00EX" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-10" height="-1"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="3217760"/>
+ <Hint name="full_width" value="2080"/>
+ <Hint name="full_height" value="1547"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-5700" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-9" height="0"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="6218368"/>
+ <Hint name="full_width" value="2585"/>
+ <Hint name="full_height" value="1924"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z60" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-34" height="-36"/>
+ <Sensor black="0" white="1023"/>
+ <Hints>
+ <Hint name="filesize" value="7816704"/>
+ <Hint name="full_width" value="2867"/>
+ <Hint name="full_height" value="2181"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-S20" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-1" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="2937856"/>
+ <Hint name="full_width" value="1621"/>
+ <Hint name="full_height" value="1208"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-S100" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-32" height="-34"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="4948608"/>
+ <Hint name="full_width" value="2090"/>
+ <Hint name="full_height" value="1578"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-R41" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="2" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="6054400"/>
+ <Hint name="full_width" value="2346"/>
+ <Hint name="full_height" value="1720"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-P505" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="7426656"/>
+ <Hint name="full_width" value="2568"/>
+ <Hint name="full_height" value="1928"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-R51" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-22" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="7530816"/>
+ <Hint name="full_width" value="2602"/>
+ <Hint name="full_height" value="1929"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z50" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-32" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="7542528"/>
+ <Hint name="full_width" value="2602"/>
+ <Hint name="full_height" value="1932"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z500" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-25" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="7562048"/>
+ <Hint name="full_width" value="2602"/>
+ <Hint name="full_height" value="1937"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z55" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-32" height="-26"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="7753344"/>
+ <Hint name="full_width" value="2602"/>
+ <Hint name="full_height" value="1986"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-P600" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-14" height="-30"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="9313536"/>
+ <Hint name="full_width" value="2858"/>
+ <Hint name="full_height" value="2172"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z750" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-27" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="10834368"/>
+ <Hint name="full_width" value="3114"/>
+ <Hint name="full_height" value="2319"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z75" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-25" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="10843712"/>
+ <Hint name="full_width" value="3114"/>
+ <Hint name="full_height" value="2321"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-P700" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-32" height="-32"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="10979200"/>
+ <Hint name="full_width" value="3114"/>
+ <Hint name="full_height" value="2350"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z850" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-6" height="-30"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="12310144"/>
+ <Hint name="full_width" value="3285"/>
+ <Hint name="full_height" value="2498"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z8" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-47" height="-35"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="12489984"/>
+ <Hint name="full_width" value="3328"/>
+ <Hint name="full_height" value="2502"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-Z1050" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-82" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="15499264"/>
+ <Hint name="full_width" value="3754"/>
+ <Hint name="full_height" value="2752"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="EX-ZR100" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-24" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="18702336"/>
+ <Hint name="full_width" value="4096"/>
+ <Hint name="full_height" value="3044"/>
+ </Hints>
+ </Camera>
+ <Camera make="Casio" model="QV-4000" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="7684000"/>
+ <Hint name="full_width" value="2260"/>
+ <Hint name="full_height" value="1700"/>
+ </Hints>
+ </Camera>
+ <Camera make="Creative" model="PC-CAM 600" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="1" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="787456"/>
+ <Hint name="full_width" value="1024"/>
+ <Hint name="full_height" value="769"/>
+ </Hints>
+ </Camera>
+ <Camera make="DJI" model="" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="28829184"/>
+ <Hint name="full_width" value="4384"/>
+ <Hint name="full_height" value="3288"/>
+ </Hints>
+ </Camera>
+ <Camera make="Matrix" model="" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="15151104"/>
+ <Hint name="full_width" value="4608"/>
+ <Hint name="full_height" value="3288"/>
+ </Hints>
+ </Camera>
+ <Camera make="Foculus" model="531C" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="3840000"/>
+ <Hint name="full_width" value="1600"/>
+ <Hint name="full_height" value="1200"/>
+ </Hints>
+ </Camera>
+ <Camera make="Generic" model="" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="307200"/>
+ <Hint name="full_width" value="640"/>
+ <Hint name="full_height" value="480"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="DC20" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="1" y="1" width="-6" height="-1"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="62464"/>
+ <Hint name="full_width" value="256"/>
+ <Hint name="full_height" value="244"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="DC20" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="1" y="1" width="-10" height="-1"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="124928"/>
+ <Hint name="full_width" value="512"/>
+ <Hint name="full_height" value="244"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="DCS200" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="52" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="1652736"/>
+ <Hint name="full_width" value="1536"/>
+ <Hint name="full_height" value="1076"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C330" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="1" y="33" width="-1" height="-2"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="4159302"/>
+ <Hint name="full_width" value="2338"/>
+ <Hint name="full_height" value="1779"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C330" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="1" y="33" width="-1" height="-2"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="4162462"/>
+ <Hint name="full_width" value="2338"/>
+ <Hint name="full_height" value="1779"/>
+ <Hint name="offset" value="3160"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C603" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="6163328"/>
+ <Hint name="full_width" value="2864"/>
+ <Hint name="full_height" value="2152"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C603" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="6166488"/>
+ <Hint name="full_width" value="2864"/>
+ <Hint name="full_height" value="2152"/>
+ <Hint name="offset" value="3160"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C603" supported="no" mode="chdk-c">
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="460800"/>
+ <Hint name="full_width" value="640"/>
+ <Hint name="full_height" value="480"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="C603" supported="no" mode="chdk-d">
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="9116448"/>
+ <Hint name="full_width" value="2848"/>
+ <Hint name="full_height" value="2134"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="12MP" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="2" y="0" width="0" height="-13"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="12241200"/>
+ <Hint name="full_width" value="4040"/>
+ <Hint name="full_height" value="3030"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="12MP" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="2" y="0" width="0" height="-13"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="12272756"/>
+ <Hint name="full_width" value="4040"/>
+ <Hint name="full_height" value="3030"/>
+ <Hint name="offset" value="31556"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="12MP" supported="no" mode="chdk-c">
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="18000000"/>
+ <Hint name="full_width" value="4000"/>
+ <Hint name="full_height" value="3000"/>
+ </Hints>
+ </Camera>
+ <Camera make="Kodak" model="KAI-0340" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="3" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="614400"/>
+ <Hint name="full_width" value="640"/>
+ <Hint name="full_height" value="480"/>
+ </Hints>
+ </Camera>
+ <Camera make="Micron" model="2010" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="3884928"/>
+ <Hint name="full_width" value="1608"/>
+ <Hint name="full_height" value="1207"/>
+ <Hint name="offset" value="3212"/>
+ </Hints>
+ </Camera>
+ <Camera make="Minolta" model="RD175" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="63"/>
+ <Hints>
+ <Hint name="filesize" value="1138688"/>
+ <Hint name="full_width" value="1534"/>
+ <Hint name="full_height" value="986"/>
+ <Hint name="offset" value="513"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E900" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-18" height="-6"/>
+ <Sensor black="0" white="1008"/>
+ <Hints>
+ <Hint name="filesize" value="1581060"/>
+ <Hint name="full_width" value="1305"/>
+ <Hint name="full_height" value="969"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E950" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-22" height="-1"/>
+ <Sensor black="0" white="992"/>
+ <Hints>
+ <Hint name="filesize" value="2465792"/>
+ <Hint name="full_width" value="1638"/>
+ <Hint name="full_height" value="1204"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E2100" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="-7"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="2940928"/>
+ <Hint name="full_width" value="1616"/>
+ <Hint name="full_height" value="1213"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E990" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="-1"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="4771840"/>
+ <Hint name="full_width" value="2064"/>
+ <Hint name="full_height" value="1541"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E3700" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="4775936"/>
+ <Hint name="full_width" value="2064"/>
+ <Hint name="full_height" value="1542"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E4500" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="-1"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="5865472"/>
+ <Hint name="full_width" value="2288"/>
+ <Hint name="full_height" value="1709"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E4300" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="5869568"/>
+ <Hint name="full_width" value="2288"/>
+ <Hint name="full_height" value="1710"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="E5000" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="-1"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="7438336"/>
+ <Hint name="full_width" value="2576"/>
+ <Hint name="full_height" value="1925"/>
+ </Hints>
+ </Camera>
+ <Camera make="Nikon" model="COOLPIX S6" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="8998912"/>
+ <Hint name="full_width" value="2832"/>
+ <Hint name="full_height" value="2118"/>
+ </Hints>
+ </Camera>
+ <Camera make="Olympus" model="C770UZ" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="5939200"/>
+ <Hint name="full_width" value="2304"/>
+ <Hint name="full_height" value="1718"/>
+ </Hints>
+ </Camera>
+ <Camera make="Pentax" model="Optio S" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="3178560"/>
+ <Hint name="full_width" value="2064"/>
+ <Hint name="full_height" value="1540"/>
+ </Hints>
+ </Camera>
+ <Camera make="Pentax" model="Optio S" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-22" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="4841984"/>
+ <Hint name="full_width" value="2090"/>
+ <Hint name="full_height" value="1544"/>
+ </Hints>
+ </Camera>
+ <Camera make="Pentax" model="Optio S4" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-22" height="0"/>
+ <Sensor black="0" white="3968"/>
+ <Hints>
+ <Hint name="filesize" value="6114240"/>
+ <Hint name="full_width" value="2346"/>
+ <Hint name="full_height" value="1737"/>
+ </Hints>
+ </Camera>
+ <Camera make="Pentax" model="Optio 750Z" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="-21"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="10702848"/>
+ <Hint name="full_width" value="3072"/>
+ <Hint name="full_height" value="2322"/>
+ </Hints>
+ </Camera>
+ <Camera make="Pixelink" model="A782" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="13248000"/>
+ <Hint name="full_width" value="2208"/>
+ <Hint name="full_height" value="3000"/>
+ </Hints>
+ </Camera>
+ <Camera make="RoverShot" model="3320AF" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="6291456"/>
+ <Hint name="full_width" value="2048"/>
+ <Hint name="full_height" value="1536"/>
+ </Hints>
+ </Camera>
+ <Camera make="ST Micro" model="STV680 VGA" supported="no" mode="chdk">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">BG</ColorRow>
+ <ColorRow y="1">GR</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="311696"/>
+ <Hint name="full_width" value="644"/>
+ <Hint name="full_height" value="484"/>
+ </Hints>
+ </Camera>
+ <Camera make="Samsung" model="S85" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-24" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="16098048"/>
+ <Hint name="full_width" value="3288"/>
+ <Hint name="full_height" value="2448"/>
+ </Hints>
+ </Camera>
+ <Camera make="Samsung" model="S85" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-48" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="16215552"/>
+ <Hint name="full_width" value="3312"/>
+ <Hint name="full_height" value="2448"/>
+ </Hints>
+ </Camera>
+ <Camera make="Samsung" model="WB550" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65504"/>
+ <Hints>
+ <Hint name="filesize" value="20487168"/>
+ <Hint name="full_width" value="3648"/>
+ <Hint name="full_height" value="2808"/>
+ </Hints>
+ </Camera>
+ <Camera make="Samsung" model="WB550" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">RG</ColorRow>
+ <ColorRow y="1">GB</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65504"/>
+ <Hints>
+ <Hint name="filesize" value="24000000"/>
+ <Hint name="full_width" value="4000"/>
+ <Hint name="full_height" value="3000"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sinar" model="" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="12582980"/>
+ <Hint name="full_width" value="3072"/>
+ <Hint name="full_height" value="2048"/>
+ <Hint name="offset" value="68"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sinar" model="" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="33292868"/>
+ <Hint name="full_width" value="4080"/>
+ <Hint name="full_height" value="4080"/>
+ <Hint name="offset" value="68"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sinar" model="" supported="no" mode="chdk-c">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GB</ColorRow>
+ <ColorRow y="1">RG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="44390468"/>
+ <Hint name="full_width" value="4080"/>
+ <Hint name="full_height" value="5440"/>
+ <Hint name="offset" value="68"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sony" model="XCD-SX910CR" supported="no" mode="chdk-a">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-1" height="0"/>
+ <Sensor black="0" white="255"/>
+ <Hints>
+ <Hint name="filesize" value="1409024"/>
+ <Hint name="full_width" value="1376"/>
+ <Hint name="full_height" value="1024"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sony" model="XCD-SX910CR" supported="no" mode="chdk-b">
+ <CFA2 width="2" height="2">
+ <ColorRow y="0">GR</ColorRow>
+ <ColorRow y="1">BG</ColorRow>
+ </CFA2>
+ <Crop x="0" y="0" width="-1" height="0"/>
+ <Sensor black="0" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="2818048"/>
+ <Hint name="full_width" value="1376"/>
+ <Hint name="full_height" value="1024"/>
+ </Hints>
+ </Camera>
+ <Camera make="GITUP" model="GIT2" mode="chdk-a">
+ <ID make="GITUP" model="GIT2">GITUP GIT2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="3200" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="31850496"/>
+ <Hint name="full_width" value="4608"/>
+ <Hint name="full_height" value="3456"/>
+ </Hints>
+ </Camera>
+ <Camera make="GITUP" model="GIT2" mode="chdk-b">
+ <ID make="GITUP" model="GIT2">GITUP GIT2</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="3200" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="23887872"/>
+ <Hint name="full_width" value="4608"/>
+ <Hint name="full_height" value="2592"/>
+ </Hints>
+ </Camera>
+ <Camera make="GITUP" model="GIT2P" mode="chdk-a">
+ <ID make="GITUP" model="GIT2P">GITUP GIT2P</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="4160" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="32257024"/>
+ <Hint name="full_width" value="4624"/>
+ <Hint name="full_height" value="3488"/>
+ </Hints>
+ </Camera>
+ <Camera make="GITUP" model="GIT2P" mode="chdk-b">
+ <ID make="GITUP" model="GIT2P">GITUP GIT2P</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="4160" white="65535"/>
+ <Hints>
+ <Hint name="filesize" value="24192768"/>
+ <Hint name="full_width" value="4624"/>
+ <Hint name="full_height" value="2616"/>
+ </Hints>
+ </Camera>
+ <Camera make="Paralenz" model="Dive Camera" mode="chdk">
+ <ID make="Paralenz" model="Dive Camera">Paralenz Dive Camera</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="1024" white="16383"/>
+ <Hints>
+ <Hint name="filesize" value="16588800"/>
+ <Hint name="full_width" value="3840"/>
+ <Hint name="full_height" value="2160"/>
+ </Hints>
+ </Camera>
+ <!-- Clashes with GITUP GIT2P chdk-b.
+ I have idea how the problem can be resolved.
+ <Camera make="Sjcam" model="Sjcam SJ6 LEGEND" mode="chdk-a">
+ <ID make="Sjcam" model="SJ6 LEGEND">Sjcam SJ6 LEGEND</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="24192768"/>
+ <Hint name="full_width" value="4624"/>
+ <Hint name="full_height" value="3488"/>
+ <Hint name="order" value="plain"/>
+ </Hints>
+ </Camera>
+ -->
+ <Camera make="Sjcam" model="Sjcam SJ6 LEGEND" mode="chdk-b">
+ <ID make="Sjcam" model="SJ6 LEGEND">Sjcam SJ6 LEGEND</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="18325296"/>
+ <Hint name="full_width" value="4024"/>
+ <Hint name="full_height" value="3036"/>
+ <Hint name="order" value="plain"/>
+ </Hints>
+ </Camera>
+ <Camera make="Sjcam" model="Sjcam SJ6 LEGEND" mode="chdk-c">
+ <ID make="Sjcam" model="SJ6 LEGEND">Sjcam SJ6 LEGEND</ID>
+ <CFA width="2" height="2">
+ <Color x="0" y="0">RED</Color>
+ <Color x="1" y="0">GREEN</Color>
+ <Color x="0" y="1">GREEN</Color>
+ <Color x="1" y="1">BLUE</Color>
+ </CFA>
+ <Crop x="0" y="0" width="0" height="0"/>
+ <Sensor black="0" white="4095"/>
+ <Hints>
+ <Hint name="filesize" value="15972480"/>
+ <Hint name="full_width" value="3760"/>
+ <Hint name="full_height" value="2832"/>
+ <Hint name="order" value="plain"/>
+ </Hints>
+ </Camera>
+</Cameras>
+<!--
+vim: tabstop=8 shiftwidth=8 softtabstop=8
+kate: tab-width: 8; replace-tabs off; indent-width 8; tab-indents: on;
+kate: indent-mode xml; remove-trailing-spaces modified;
+-->
diff --git a/subprojects/rawspeed/data/meson.build b/subprojects/rawspeed/data/meson.build
new file mode 100644
index 00000000..9ef87aca
--- /dev/null
+++ b/subprojects/rawspeed/data/meson.build
@@ -0,0 +1 @@
+install_data('cameras.xml', install_dir: rawspeed_datadir)
diff --git a/subprojects/rawspeed/meson.build b/subprojects/rawspeed/meson.build
new file mode 100644
index 00000000..f7cce7c6
--- /dev/null
+++ b/subprojects/rawspeed/meson.build
@@ -0,0 +1,77 @@
+project(
+ 'rawspeed',
+ 'cpp',
+ license: 'LGPLv2+',
+ meson_version: '>= 0.50.0',
+)
+
+cxx = meson.get_compiler('cpp')
+
+if get_option('lto')
+ if cxx.get_id() == 'gcc'
+ add_project_arguments('-flto', language: 'cpp')
+ endif
+endif
+
+rawspeed_datadir = get_option('pkgdatadir')
+rawspeed_libdir = get_option('pkglibdir')
+
+config_h = configuration_data()
+
+cpp_thread_local_support_source = '''
+ static thread_local int tls;
+
+ int
+ main (void)
+ {
+ return 0;
+ }
+'''
+config_h.set(
+ 'HAVE_CXX_THREAD_LOCAL',
+ cxx.compiles(cpp_thread_local_support_source, name: 'C++ thread-local storage support')
+)
+
+gcc_thread_local_support_source = '''
+ static __thread int tls;
+
+ int
+ main (void)
+ {
+ return 0;
+ }
+'''
+config_h.set(
+ 'HAVE_GCC_THREAD_LOCAL',
+ cxx.compiles(gcc_thread_local_support_source, name: 'GCC thread-local storage support')
+)
+
+config_h.set('HAVE_POSIX_MEMALIGN', cxx.has_header_symbol('cstdlib', 'posix_memalign'))
+config_h.set('HAVE_ALIGNED_ALLOC', cxx.has_header_symbol('cstdlib', 'aligned_alloc'))
+config_h.set('HAVE_MM_MALLOC', cxx.has_header_symbol('xmmintrin.h', '_mm_malloc'))
+config_h.set('HAVE_ALIGNED_MALLOC', cxx.has_header_symbol('malloc.h', '_aligned_malloc'))
+
+libjpeg_dep = dependency('libjpeg', version: '>= 2.0.0')
+config_h.set('HAVE_JPEG', true)
+config_h.set('HAVE_JPEG_MEM_SRC', true)
+
+openmp_dep = dependency('openmp', version: '>= 4.0')
+config_h.set('HAVE_OPENMP', true)
+config_h.set('ompfirstprivateclause', 'firstprivate(__VA_ARGS__)')
+
+zlib_dep = dependency('zlib')
+config_h.set('HAVE_ZLIB', true)
+
+pugixml_dep = cxx.find_library('pugixml')
+config_h.set('HAVE_PUGIXML', true)
+
+configure_file(
+ input: 'config.h.in',
+ output: 'rawspeedconfig.h',
+ configuration: config_h,
+)
+
+rawspeed_include = include_directories('.')
+
+subdir('data')
+subdir('src')
diff --git a/subprojects/rawspeed/meson_options.txt b/subprojects/rawspeed/meson_options.txt
new file mode 100644
index 00000000..fb1efeb2
--- /dev/null
+++ b/subprojects/rawspeed/meson_options.txt
@@ -0,0 +1,20 @@
+option(
+ 'lto',
+ description: 'Use link-time optimization',
+ type: 'boolean',
+ value: false,
+)
+
+option(
+ 'pkgdatadir',
+ description: 'The private directory the cameras.xml file will be installed into',
+ type: 'string',
+ value: '',
+)
+
+option(
+ 'pkglibdir',
+ description: 'The private directory the shared library 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/MemorySanitizer.h
b/subprojects/rawspeed/src/external/MemorySanitizer.h
new file mode 100644
index 00000000..a0b7433e
--- /dev/null
+++ b/subprojects/rawspeed/src/external/MemorySanitizer.h
@@ -0,0 +1,67 @@
+/*
+ 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(memory_sanitizer) || defined(__SANITIZE_MEMORY__)
+#include <sanitizer/msan_interface.h>
+#endif
+
+namespace rawspeed {
+
+struct MSan final {
+ // Do not instantiate.
+ MSan() = delete;
+ MSan(const MSan&) = delete;
+ MSan(MSan&&) = delete;
+ MSan& operator=(const MSan&) = delete;
+ MSan& operator=(MSan&&) = delete;
+ ~MSan() = delete;
+
+ /* Checks that memory range is fully initialized, and reports an error if it
+ * is not. */
+ static void CheckMemIsInitialized(const volatile void* addr, size_t size);
+};
+
+#if __has_feature(memory_sanitizer) || defined(__SANITIZE_MEMORY__)
+inline void MSan::CheckMemIsInitialized(const volatile void* addr,
+ size_t size) {
+ __msan_check_mem_is_initialized(addr, size);
+}
+#else
+inline void MSan::CheckMemIsInitialized(const volatile void* addr,
+ size_t size) {
+ // If we are building without MSAN, 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.
+}
+#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/gopro/vc5/table17.inc
b/subprojects/rawspeed/src/external/gopro/vc5/table17.inc
new file mode 100644
index 00000000..4a625542
--- /dev/null
+++ b/subprojects/rawspeed/src/external/gopro/vc5/table17.inc
@@ -0,0 +1,291 @@
+// Taken from
https://raw.githubusercontent.com/gopro/gpr/d2d78d1b236a278a13ed5abee6870af2b490c89f/source/lib/vc5_common/table17.inc
+/*! @file bitstream.h
+ *
+ * @brief Declaration of the bitstream data structure.
+ *
+ * @version 1.0.0
+ *
+ * (C) Copyright 2018 GoPro Inc (http://gopro.com/).
+ *
+ * Licensed under either:
+ * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
+ * - MIT license, http://opensource.org/licenses/MIT
+ * at your option.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+RLVTABLE(264) table17 =
+{
+ 264,
+ {
+ {1, 0x00000000, 1, 0}, // m0
+ {2, 0x00000002, 1, 1}, // m1
+ {3, 0x00000007, 1, 2}, // m2
+ {5, 0x00000019, 1, 3}, // m3
+ {6, 0x00000030, 1, 4}, // m4
+ {6, 0x00000036, 1, 5}, // m5
+ {7, 0x0000006F, 1, 8}, // m8
+ {7, 0x00000063, 1, 6}, // m6
+ {7, 0x00000069, 12, 0}, // z12
+ {7, 0x0000006B, 1, 7}, // m7
+ {8, 0x000000D1, 20, 0}, // z20
+ {8, 0x000000D4, 1, 9}, // m9
+ {8, 0x000000DC, 1, 10}, // m10
+ {9, 0x00000189, 1, 11}, // m11
+ {9, 0x0000018A, 32, 0}, // z32
+ {9, 0x000001A0, 1, 12}, // m12
+ {9, 0x000001AB, 1, 13}, // m13
+ {10, 0x00000377, 1, 18}, // m18
+ {10, 0x00000310, 1, 14}, // m14
+ {10, 0x00000316, 1, 15}, // m15
+ {10, 0x00000343, 60, 0}, // z60
+ {10, 0x00000354, 1, 16}, // m16
+ {10, 0x00000375, 1, 17}, // m17
+ {11, 0x00000623, 1, 19}, // m19
+ {11, 0x00000684, 1, 20}, // m20
+ {11, 0x00000685, 100, 0}, // z100
+ {11, 0x000006AB, 1, 21}, // m21
+ {11, 0x000006EC, 1, 22}, // m22
+ {12, 0x00000DDB, 1, 29}, // m29
+ {12, 0x00000C5C, 1, 24}, // m24
+ {12, 0x00000C5E, 1, 25}, // m25
+ {12, 0x00000C44, 1, 23}, // m23
+ {12, 0x00000D55, 1, 26}, // m26
+ {12, 0x00000DD1, 1, 27}, // m27
+ {12, 0x00000DD3, 1, 28}, // m28
+ {13, 0x00001BB5, 1, 35}, // m35
+ {13, 0x0000188B, 1, 30}, // m30
+ {13, 0x000018BB, 1, 31}, // m31
+ {13, 0x000018BF, 180, 0}, // z180
+ {13, 0x00001AA8, 1, 32}, // m32
+ {13, 0x00001BA0, 1, 33}, // m33
+ {13, 0x00001BA5, 320, 0}, // z320
+ {13, 0x00001BA4, 1, 34}, // m34
+ {14, 0x00003115, 1, 36}, // m36
+ {14, 0x00003175, 1, 37}, // m37
+ {14, 0x0000317D, 1, 38}, // m38
+ {14, 0x00003553, 1, 39}, // m39
+ {14, 0x00003768, 1, 40}, // m40
+ {15, 0x00006E87, 1, 46}, // m46
+ {15, 0x00006ED3, 1, 47}, // m47
+ {15, 0x000062E8, 1, 42}, // m42
+ {15, 0x000062F8, 1, 43}, // m43
+ {15, 0x00006228, 1, 41}, // m41
+ {15, 0x00006AA4, 1, 44}, // m44
+ {15, 0x00006E85, 1, 45}, // m45
+ {16, 0x0000C453, 1, 48}, // m48
+ {16, 0x0000C5D3, 1, 49}, // m49
+ {16, 0x0000C5F3, 1, 50}, // m50
+ {16, 0x0000DDA4, 1, 53}, // m53
+ {16, 0x0000DD08, 1, 51}, // m51
+ {16, 0x0000DD0C, 1, 52}, // m52
+ {17, 0x0001BB4B, 1, 61}, // m61
+ {17, 0x0001BB4A, 1, 60}, // m60
+ {17, 0x00018BA5, 1, 55}, // m55
+ {17, 0x00018BE5, 1, 56}, // m56
+ {17, 0x0001AA95, 1, 57}, // m57
+ {17, 0x0001AA97, 1, 58}, // m58
+ {17, 0x000188A4, 1, 54}, // m54
+ {17, 0x0001BA13, 1, 59}, // m59
+ {18, 0x00031748, 1, 62}, // m62
+ {18, 0x000317C8, 1, 63}, // m63
+ {18, 0x00035528, 1, 64}, // m64
+ {18, 0x0003552C, 1, 65}, // m65
+ {18, 0x00037424, 1, 66}, // m66
+ {18, 0x00037434, 1, 67}, // m67
+ {18, 0x00037436, 1, 68}, // m68
+ {19, 0x00062294, 1, 69}, // m69
+ {19, 0x00062E92, 1, 70}, // m70
+ {19, 0x00062F92, 1, 71}, // m71
+ {19, 0x0006AA52, 1, 72}, // m72
+ {19, 0x0006AA5A, 1, 73}, // m73
+ {19, 0x0006E86A, 1, 75}, // m75
+ {19, 0x0006E86E, 1, 76}, // m76
+ {19, 0x0006E84A, 1, 74}, // m74
+ {20, 0x000C452A, 1, 77}, // m77
+ {20, 0x000C5D27, 1, 78}, // m78
+ {20, 0x000C5F26, 1, 79}, // m79
+ {20, 0x000D54A6, 1, 80}, // m80
+ {20, 0x000D54B6, 1, 81}, // m81
+ {20, 0x000DD096, 1, 82}, // m82
+ {20, 0x000DD0D6, 1, 83}, // m83
+ {20, 0x000DD0DE, 1, 84}, // m84
+ {21, 0x00188A56, 1, 85}, // m85
+ {21, 0x0018BA4D, 1, 86}, // m86
+ {21, 0x0018BE4E, 1, 87}, // m87
+ {21, 0x0018BE4F, 1, 88}, // m88
+ {21, 0x001AA96E, 1, 89}, // m89
+ {21, 0x001BA12E, 1, 90}, // m90
+ {21, 0x001BA12F, 1, 91}, // m91
+ {21, 0x001BA1AF, 1, 92}, // m92
+ {21, 0x001BA1BF, 1, 93}, // m93
+ {22, 0x0037435D, 1, 99}, // m99
+ {22, 0x0037437D, 1, 100}, // m100
+ {22, 0x00317498, 1, 94}, // m94
+ {22, 0x0035529C, 1, 95}, // m95
+ {22, 0x0035529D, 1, 96}, // m96
+ {22, 0x003552DE, 1, 97}, // m97
+ {22, 0x003552DF, 1, 98}, // m98
+ {23, 0x0062E933, 1, 102}, // m102
+ {23, 0x0062295D, 1, 101}, // m101
+ {23, 0x006AA53D, 1, 103}, // m103
+ {23, 0x006AA53F, 1, 105}, // m105
+ {23, 0x006AA53E, 1, 104}, // m104
+ {23, 0x006E86B9, 1, 106}, // m106
+ {23, 0x006E86F8, 1, 107}, // m107
+ {24, 0x00D54A79, 1, 111}, // m111
+ {24, 0x00C5D265, 1, 109}, // m109
+ {24, 0x00C452B8, 1, 108}, // m108
+ {24, 0x00DD0D71, 1, 113}, // m113
+ {24, 0x00D54A78, 1, 110}, // m110
+ {24, 0x00DD0D70, 1, 112}, // m112
+ {24, 0x00DD0DF2, 1, 114}, // m114
+ {24, 0x00DD0DF3, 1, 115}, // m115
+ {25, 0x0188A5F6, 1, 225}, // m225
+ {25, 0x0188A5F5, 1, 189}, // m189
+ {25, 0x0188A5F4, 1, 188}, // m188
+ {25, 0x0188A5F3, 1, 203}, // m203
+ {25, 0x0188A5F2, 1, 202}, // m202
+ {25, 0x0188A5F1, 1, 197}, // m197
+ {25, 0x0188A5F0, 1, 207}, // m207
+ {25, 0x0188A5EF, 1, 169}, // m169
+ {25, 0x0188A5EE, 1, 223}, // m223
+ {25, 0x0188A5ED, 1, 159}, // m159
+ {25, 0x0188A5AA, 1, 235}, // m235
+ {25, 0x0188A5E3, 1, 152}, // m152
+ {25, 0x0188A5DF, 1, 192}, // m192
+ {25, 0x0188A589, 1, 179}, // m179
+ {25, 0x0188A5DD, 1, 201}, // m201
+ {25, 0x0188A578, 1, 172}, // m172
+ {25, 0x0188A5E0, 1, 149}, // m149
+ {25, 0x0188A588, 1, 178}, // m178
+ {25, 0x0188A5D6, 1, 120}, // m120
+ {25, 0x0188A5DB, 1, 219}, // m219
+ {25, 0x0188A5E1, 1, 150}, // m150
+ {25, 0x0188A587, 1, 127}, // m127
+ {25, 0x0188A59A, 1, 211}, // m211
+ {25, 0x0188A5C4, 1, 125}, // m125
+ {25, 0x0188A5EC, 1, 158}, // m158
+ {25, 0x0188A586, 1, 247}, // m247
+ {25, 0x0188A573, 1, 238}, // m238
+ {25, 0x0188A59C, 1, 163}, // m163
+ {25, 0x0188A5C8, 1, 228}, // m228
+ {25, 0x0188A5FB, 1, 183}, // m183
+ {25, 0x0188A5A1, 1, 217}, // m217
+ {25, 0x0188A5EB, 1, 168}, // m168
+ {25, 0x0188A5A8, 1, 122}, // m122
+ {25, 0x0188A584, 1, 128}, // m128
+ {25, 0x0188A5D2, 1, 249}, // m249
+ {25, 0x0188A599, 1, 187}, // m187
+ {25, 0x0188A598, 1, 186}, // m186
+ {25, 0x0188A583, 1, 136}, // m136
+ {25, 0x018BA4C9, 1, 181}, // m181
+ {25, 0x0188A5D0, 1, 255}, // m255
+ {25, 0x0188A594, 1, 230}, // m230
+ {25, 0x0188A582, 1, 135}, // m135
+ {25, 0x0188A5CB, 1, 233}, // m233
+ {25, 0x0188A5D8, 1, 222}, // m222
+ {25, 0x0188A5E7, 1, 145}, // m145
+ {25, 0x0188A581, 1, 134}, // m134
+ {25, 0x0188A5EA, 1, 167}, // m167
+ {25, 0x0188A5A9, 1, 248}, // m248
+ {25, 0x0188A5A6, 1, 209}, // m209
+ {25, 0x0188A580, 1, 243}, // m243
+ {25, 0x0188A5A0, 1, 216}, // m216
+ {25, 0x0188A59D, 1, 164}, // m164
+ {25, 0x0188A5C3, 1, 140}, // m140
+ {25, 0x0188A57F, 1, 157}, // m157
+ {25, 0x0188A5C0, 1, 239}, // m239
+ {25, 0x0188A5DE, 1, 191}, // m191
+ {25, 0x0188A5D4, 1, 251}, // m251
+ {25, 0x0188A57E, 1, 156}, // m156
+ {25, 0x0188A5C2, 1, 139}, // m139
+ {25, 0x0188A592, 1, 242}, // m242
+ {25, 0x0188A5CD, 1, 133}, // m133
+ {25, 0x0188A57D, 1, 162}, // m162
+ {25, 0x0188A5A3, 1, 213}, // m213
+ {25, 0x0188A5E8, 1, 165}, // m165
+ {25, 0x0188A5A2, 1, 212}, // m212
+ {25, 0x0188A57C, 1, 227}, // m227
+ {25, 0x0188A58E, 1, 198}, // m198
+ {25, 0x0188A5B3, 1, 236}, // m236
+ {25, 0x0188A5B2, 1, 234}, // m234
+ {25, 0x0188A5B1, 1, 117}, // m117
+ {25, 0x0188A5B0, 1, 215}, // m215
+ {25, 0x0188A5AF, 1, 124}, // m124
+ {25, 0x0188A5AE, 1, 123}, // m123
+ {25, 0x0188A5AD, 1, 254}, // m254
+ {25, 0x0188A5AC, 1, 253}, // m253
+ {25, 0x0188A5AB, 1, 148}, // m148
+ {25, 0x0188A5DA, 1, 218}, // m218
+ {25, 0x0188A5E4, 1, 146}, // m146
+ {25, 0x0188A5E5, 1, 147}, // m147
+ {25, 0x0188A5D9, 1, 224}, // m224
+ {25, 0x0188A5B5, 1, 143}, // m143
+ {25, 0x0188A5BC, 1, 184}, // m184
+ {25, 0x0188A5BD, 1, 185}, // m185
+ {25, 0x0188A5E9, 1, 166}, // m166
+ {25, 0x0188A5CC, 1, 132}, // m132
+ {25, 0x0188A585, 1, 129}, // m129
+ {25, 0x0188A5D3, 1, 250}, // m250
+ {25, 0x0188A5E2, 1, 151}, // m151
+ {25, 0x0188A595, 1, 119}, // m119
+ {25, 0x0188A596, 1, 193}, // m193
+ {25, 0x0188A5B8, 1, 176}, // m176
+ {25, 0x0188A590, 1, 245}, // m245
+ {25, 0x0188A5C9, 1, 229}, // m229
+ {25, 0x0188A5A4, 1, 206}, // m206
+ {25, 0x0188A5E6, 1, 144}, // m144
+ {25, 0x0188A5A5, 1, 208}, // m208
+ {25, 0x0188A5CE, 1, 137}, // m137
+ {25, 0x0188A5BF, 1, 241}, // m241
+ {25, 0x0188A572, 1, 237}, // m237
+ {25, 0x0188A59B, 1, 190}, // m190
+ {25, 0x0188A5BE, 1, 240}, // m240
+ {25, 0x0188A5C7, 1, 131}, // m131
+ {25, 0x0188A5CA, 1, 232}, // m232
+ {25, 0x0188A5D5, 1, 252}, // m252
+ {25, 0x0188A57B, 1, 171}, // m171
+ {25, 0x0188A58D, 1, 205}, // m205
+ {25, 0x0188A58C, 1, 204}, // m204
+ {25, 0x0188A58B, 1, 118}, // m118
+ {25, 0x0188A58A, 1, 214}, // m214
+ {25, 0x018BA4C8, 1, 180}, // m180
+ {25, 0x0188A5C5, 1, 126}, // m126
+ {25, 0x0188A5FA, 1, 182}, // m182
+ {25, 0x0188A5BB, 1, 175}, // m175
+ {25, 0x0188A5C1, 1, 141}, // m141
+ {25, 0x0188A5CF, 1, 138}, // m138
+ {25, 0x0188A5B9, 1, 177}, // m177
+ {25, 0x0188A5B6, 1, 153}, // m153
+ {25, 0x0188A597, 1, 194}, // m194
+ {25, 0x0188A5FE, 1, 160}, // m160
+ {25, 0x0188A5D7, 1, 121}, // m121
+ {25, 0x0188A5BA, 1, 174}, // m174
+ {25, 0x0188A591, 1, 246}, // m246
+ {25, 0x0188A5C6, 1, 130}, // m130
+ {25, 0x0188A5DC, 1, 200}, // m200
+ {25, 0x0188A57A, 1, 170}, // m170
+ {25, 0x0188A59F, 1, 221}, // m221
+ {25, 0x0188A5F9, 1, 196}, // m196
+ {25, 0x0188A5B4, 1, 142}, // m142
+ {25, 0x0188A5A7, 1, 210}, // m210
+ {25, 0x0188A58F, 1, 199}, // m199
+ {25, 0x0188A5FD, 1, 155}, // m155
+ {25, 0x0188A5B7, 1, 154}, // m154
+ {25, 0x0188A593, 1, 244}, // m244
+ {25, 0x0188A59E, 1, 220}, // m220
+ {25, 0x0188A5F8, 1, 195}, // m195
+ {25, 0x0188A5FF, 1, 161}, // m161
+ {25, 0x0188A5FC, 1, 231}, // m231
+ {25, 0x0188A579, 1, 173}, // m173
+ {25, 0x0188A5F7, 1, 226}, // m226
+ {26, 0x03114BA2, 1, 116}, // m116
+ {26, 0x03114BA3, 0, 1}, // c256
+ }
+};
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/README.md b/subprojects/rawspeed/src/librawspeed/README.md
new file mode 100644
index 00000000..0775bc02
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/README.md
@@ -0,0 +1,243 @@
+# RawSpeed Developer Information
+
+## Include files
+
+All needed headers are available by including “RawSpeed-API.h”.
+
+RawSpeed uses OpenMP, pugixml, zlib and libjpeg, which is the only external requirements beside standard
C/C++ libraries.
+
+You must implement a single function: “int rawspeed_get_number_of_processor_cores();”, which should return
the maximum number of threads that should be used for decoding, if multithreaded decoding is possible.
+
+Everything is encapsulated on a “rawspeed” namespace. To avoid clutter the examples below assume you have a
“using namespace rawspeed;” before using the code.
+
+## The Camera Definition file
+
+This file describes basic information about different cameras, so new cameras can be supported without code
changes. See the separate documentation on the [Camera Definition File](/data/README.md).
+
+The camera definitions are read into the CameraMetaData object, which you can retain for re-use later. You
initialize this data by doing
+
+```cpp
+static CameraMetaData *metadata = nullptr;
+if (nullptr == metadata)
+{
+ try {
+ metadata = new CameraMetaData("path_to_cameras.xml");
+ } catch (CameraMetadataException &e) {
+ // Reading metadata failed. e.what() will contain error message.
+ }
+}
+```
+
+The memory impact of this object is quite small, so you don’t have to free it every time. You can however
delete and re-create it, if you know the metadata file has been updated.
+
+You can disable specific cameras in the xml file, or if you would want to do it in code, you can use:
+
+```cpp
+ // Disable specific camera
+ metadata.disableCamera("Canon", "Canon EOS 100D")
+
+ // Disable all cameras from maker:
+ metadata.disableCamera("Fuji")
+```
+
+## Using RawSpeed
+
+You need to have the file data in a Buffer object. This can either be created by supplying the file content
in memory using Buffer(buffer_pointer, size_of_buffer), or use a “FileReader” object to read the content of a
file, like this:
+
+```cpp
+FileReader reader(filename);
+Buffer* map = nullptr;
+try {
+ map = reader.readFile();
+} catch (FileIOException &e) {
+ // Handle errors
+}
+```
+
+The next step is to start decoding. The first step is to get a decoder:
+
+```cpp
+RawParser parser(map);
+RawDecoder *decoder = parser.getDecoder();
+```
+
+This will do basic parsing of the file, and return a decoder that is capable of decoding the image. If no
decoder can be found or another error occurs a “RawDecoderException” object will be thrown. The next step is
to determine whether the specific camera is supported:
+
+```cpp
+decoder->failOnUnknown = false;
+decoder->checkSupport(metadata);
+```
+
+The “failOnUnknown” property will indicate whether the decoder should refuse to decode unknown cameras.
Otherwise RawSpeed will only refuse to decode the image, if it is confirmed that the camera type cannot be
decoded correctly. If the image isn’t supported a “RawDecoderException” will be thrown.
+
+Reaching this point should be very fast in terms of CPU time, so the support check is very quick, if file
data is quickly available. Next we decode the image:
+
+```cpp
+decoder->decodeRaw();
+decoder->decodeMetaData(metadata);
+RawImage raw = decoder->mRaw;
+```
+
+This will decode the image, and apply metadata information. The RawImage is at this point completely
untouched Raw data, however the image has been cropped to the active image area in decodeMetaData. Error
reporting is: If a fatal error occurs a RawDecoderException is thrown.
+
+Non-fatal errors are pushed into a "vector" array in the decoder object called "errors". With these types of
errors, there WILL be a raw image available, but it will likely contain junk sections in undecodable parts.
However, as much as it was possible to decode will be available. So treat these messages as warnings.
+
+Another thing to note here is that the RawImage object is automatically refcounted, so you can pass the
object around without worrying about the image being freed before all instances are out of scope. Do however
keep this in mind if you pass the pointer to image data to another part of your application.
+
+```cpp
+raw->scaleBlackWhite();
+```
+
+This will apply the black/white scaling to the image, so the data is normalized into the 0->65535 range no
matter what the sensor adjustment is (for 16 bit images). This function does no throw any errors. Now you can
retrieve information about the image:
+
+```cpp
+int components_per_pixel = raw->getCpp();
+RawImageType type = raw->getDataType();
+bool is_cfa = r->isCFA;
+```
+
+Components per pixel indicates how many components are present per pixel. Common values are 1 on CFA images,
and 3, found in some DNG images for instance. Do note, you cannot assume that an images is CFA just because
it is 1 cpp - greyscale dng images from things like scanners can be saved like that.
+
+The RawImageType can be TYPE_USHORT16 (most common) which indicates unsigned 16 bit data or TYPE_FLOAT32
(found in some DNGs)
+
+The isCFA indicates whether the image has all components per pixel, or if it was taken with a colorfilter
array. This usually corresponds to the number of components per pixel (1 on CFA, 3 on non-CFA).
+
+The ColorfilterArray contains information about the placement of colors in the CFA:
+
+```cpp
+if (true == is_cfa) {
+ ColorFilterArray cfa = raw->cfa;
+ int dcraw_filter = cfa.getDcrawFilter();
+ int cfa_width = cfa.size.x;
+ int cfa_height = cfa.size.y;
+ CFAColor c = cfa.getColorAt(0,0);
+}
+```
+
+To get this information as a dcraw compatible filter information, you can use getDcrawFilter() function.
+
+You can also use getColorAt(x, y) to get a single color information. ~~Note that unlike dcraw, RawSpeed only
supports 2×2 patterns, so you can reuse this information.~~ CFAColor can be CFA_RED, CFA_GREEN, CFA_BLUE for
instance.
+
+Finally information about the image itself:
+
+```cpp
+unsigned char* data = raw->getData(0,0);
+int width = raw->dim.x;
+int height = raw->dim.y;
+int pitch_in_bytes = raw->pitch;
+```
+
+The getData(x, y) function will give you a pointer to the Raw data at pixel x, y. This is the coordinate
after crop, so you can start copying data right away. Don’t use this function on every pixel, but instead
increment the pointer yourself. The width and height gives you the size of the image in pixels – again after
crop.
+
+Pitch is the number of bytes between lines, since this is usually NOT width * components_per_pixel *
bytes_per_component. So in this instance, to calculate a pointer at line y, use &data[y * raw->pitch] for
instance.
+
+Finally to clean up, use:
+
+```cpp
+delete map;
+delete decoder;
+```
+
+Actually the map and decoder can be deallocated once the metadata has been decoded. The RawImage will
automatically be deallocated when it goes out of scope and the decoder has been deallocated. After that all
data pointers that have been retrieved will no longer be usable.
+
+## Tips & Tricks
+
+You will most likely find that a relatively long time is spent actually reading the file. The biggest trick
to speeding up raw reading is to have some sort of prefetching going on while the file is being decoded. This
is the main reason why RawSpeed decodes from memory, and doesn’t use direct file reads while decoding.
+
+The simplest solution is to start a thread that simply reads the file, and rely on the system cache to cache
the data. This is fairly simple and works in 99% of all cases. So if you are doing batch processing simply
start a process reading the next file, when the current image starts decoding. This will ensure that your
file is read linearly, which gives the highest possible throughput.
+
+A more complex option is to read the file to a memory portion, which is then given to RawSpeed to decode.
This might be a few milliseconds faster in the best case, but I have found no practical difference between
that and simply relying on system caching.
+
+You might want to try out memory mapped files. However this approach has in practical tests shown to be just
as fast in best cases (when file is cached), or slower (uncached files).
+
+## Bad pixel elimination
+
+A few cameras will mark bad pixels within their RAW files in various ways. For the camera we know how to
this will be picked up by RawSpeed. By default these pixels are eliminated by 4-way interpolating to the
closest valid pixels in an on-axis search from the current pixel.
+
+If you want to do bad pixel interpolation yourself you can set:
+
+```cpp
+RawDecoder.interpolateBadPixels = false;
+```
+
+Before calling the decoder. This will disable the automatic interpolation of bad pixels. You can retrieve
the bad pixels by using:
+
+```cpp
+std::vector<uint32> RawImage->mBadPixelPositions;
+```
+
+This is a vector that contains the positions of the detected bad pixels in the image. The positions are
stored as x | (y << 16), so maximum pixel position is 65535, which also corresponds with the limit of the
image sizes within RawSpeed. you can loop through all bad pixels with a loop like this:
+
+```cpp
+for (vector<uint32>::iterator i=mBadPixelPositions.begin(); i != mBadPixelPositions.end(); i++) {
+ uint32 pos_x = (*i)&0xffff;
+ uint32 pos_y = (*i)>>16;
+ ushort16* pix = (ushort16*)getDataUncropped(pos_x, pos_y);
+}
+```
+
+This however may not be most optimal format for you. You can also call RawImage->transferBadPixelsToMap().
This will create a bit-mask for you with all bad pixels. Each byte correspond to 8 pixels with the least
significant bit for the leftmost pixel. To set position x,y this operation is used:
+
+```cpp
+RawImage->mBadPixelMap[(x >> 8) + y * mBadPixelMapPitch] |= 1 << (x & 7);
+```
+
+This enables you to quickly search through the array. If you for instance cast the array to integers you can
check 32 pixels at the time.
+
+Note that all positions are uncropped image positions. Also note that if you keep the interpolation enabled
you can still retrieve the mBadPixelMap, but the mBadPixelPositions will be cleared.
+
+## Updating Camera Support
+
+If you implement an autoupdate feature, you simply update “cameras.xml” and delete and re-create the
CameraMetaData object.
+
+There might of course be some specific cameras that require code-changes to work properly. However, there is
a versioning check inplace, whereby cameras requirering a specific code version to decode properly will be
marked as such.
+
+That means you should safely be able to update cameras.xml to a newer version, and cameras requiring a code
update will then simply refuse to open.
+
+
+## Format Specific Notes
+
+### Canon sRaw/mRaw
+Canon reduced resolution Raws (mRaw/sRaw) are returned as RGB with 3 component per pixel without
whitebalance compensation, so color balance should match ordinary CR2 images. The subsampled color components
are linearly interpolated.
+
+This is even more complicated by the fact that Canon has changed the way they store the sraw whitebalance
values. This means that on newer cameras, you might have to specify "invert_sraw_wb" as a hint to properly
decode the whitebalance on these casmeras. To see examples of this, search cameras.xml for "invert_sraw_wb".
+
+### Sigma Foveon Support
+
+Sigma Foveon (x3f-based) images are not supported. If you want them to be supported, help welcomed :)
+
+### Fuji Rotated Support
+
+By default RawSpeed delivers Fuji SuperCCD images as 45 degree rotated images.
+
+RawSpeed does however use two camera hints to do this. The first hint is "fuji_rotate": When this is
specified in cameras.xml, the images are rotated.
+
+To check if an image has been rotated, check RawImage->fujiWidth after calling
RawDecoder->decodeMetaData(...) If it is > 0, then the image has been rotated, and you can use this value to
calculate the un-rotated image size. See
[here](https://rawstudio.org/svn/rawstudio/trunk/plugins/fuji-rotate/fuji-rotate.c) for an example on how to
rotate the image back after de-mosaic.
+
+If you do NOT want your images to be delivered rotated, you can disable it when decoding.
+```cpp
+RawDecoder->fujiRotate = false;
+```
+Do however note the CFA colors are still referring to the rotated color positions.
+
+
+## Other options
+
+### RawDecoder -> uncorrectedRawValues
+If you enable this on the decoder before calling RawDecoder->decodeRaw(), you will get complely unscaled
values. Some cameras have a "compressed" mode, where a non-linear compression curve is applied to the image
data. If you enable this parameter the compression curve will not be applied to the image. Currently there is
no way to retrieve the compression curve, so this option is only useful for diagnostics.
+
+
+### RawImage.mDitherScale
+This option will determine whether dither is applied when values are scaled to 16 bits. Dither is applied as
a random value between "+-scalefactor/2". This will make it so that images with less number of bits/pixel
doesn't have a big tendency for posterization, since values close to each other will be spaced out a bit.
+
+Another way of putting it, is that if your camera saves 12 bit per pixel, when RawSpeed upscales this to 16
bits, the 4 "new" bits will be random instead of always the same value.
+
+## Memory Usage
+
+RawSpeed will need:
+
+* Size of Raw File.
+* Image width * image height * 2 for ordinary Raw images with 16 bit output.
+* Image width * image height * 4 for float point images with float point output .
+* Image width * image height * 6 for ordinary Raw images with float point output.
+* Image width * image height / 8 for images with bad pixels.
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..dfa6cbd5
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/common/meson.build
@@ -0,0 +1,39 @@
+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',
+ 'GetNumberOfProcessorCores.cpp',
+ '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..c273f4d1
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/decompressors/meson.build
@@ -0,0 +1,66 @@
+dependencies = [libjpeg_dep, 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..f728bb8b
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/meson.build
@@ -0,0 +1,42 @@
+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,
+ install: librawspeed_install,
+ install_dir: rawspeed_libdir,
+ link_whole: librawspeed_libraries,
+)
+
+rawspeed_dep = declare_dependency(
+ include_directories: [rawspeed_include, rawspeed_external_include, rawspeed_librawspeed_include],
+ 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..905a7c6a
--- /dev/null
+++ b/subprojects/rawspeed/src/librawspeed/metadata/meson.build
@@ -0,0 +1,21 @@
+dependencies = [pugixml_dep]
+
+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,
+ dependencies: dependencies,
+ 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]