/src/exiv2/src/rw2image.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | |
3 | | // included header files |
4 | | #include "rw2image.hpp" |
5 | | #include "basicio.hpp" |
6 | | #include "config.h" |
7 | | #include "error.hpp" |
8 | | #include "futils.hpp" |
9 | | #include "image.hpp" |
10 | | #include "preview.hpp" |
11 | | #include "rw2image_int.hpp" |
12 | | #include "tags.hpp" |
13 | | #include "tiffcomposite_int.hpp" |
14 | | #include "tiffimage_int.hpp" |
15 | | |
16 | | // + standard includes |
17 | | #include <array> |
18 | | |
19 | | #ifdef EXIV2_DEBUG_MESSAGES |
20 | | #include <iostream> |
21 | | #include "value.hpp" |
22 | | #endif |
23 | | |
24 | | // ***************************************************************************** |
25 | | // class member definitions |
26 | | namespace Exiv2 { |
27 | | using namespace Internal; |
28 | | |
29 | 93 | Rw2Image::Rw2Image(BasicIo::UniquePtr io) : Image(ImageType::rw2, mdExif | mdIptc | mdXmp, std::move(io)) { |
30 | 93 | } // Rw2Image::Rw2Image |
31 | | |
32 | 366 | std::string Rw2Image::mimeType() const { |
33 | 366 | return "image/x-panasonic-rw2"; |
34 | 366 | } |
35 | | |
36 | 90 | uint32_t Rw2Image::pixelWidth() const { |
37 | 90 | auto imageWidth = exifData_.findKey(Exiv2::ExifKey("Exif.PanasonicRaw.SensorWidth")); |
38 | 90 | if (imageWidth == exifData_.end() || imageWidth->count() == 0) |
39 | 86 | return 0; |
40 | 4 | return imageWidth->toUint32(); |
41 | 90 | } |
42 | | |
43 | 90 | uint32_t Rw2Image::pixelHeight() const { |
44 | 90 | auto imageHeight = exifData_.findKey(Exiv2::ExifKey("Exif.PanasonicRaw.SensorHeight")); |
45 | 90 | if (imageHeight == exifData_.end() || imageHeight->count() == 0) |
46 | 80 | return 0; |
47 | 10 | return imageHeight->toUint32(); |
48 | 90 | } |
49 | | |
50 | 0 | void Rw2Image::setExifData(const ExifData& /*exifData*/) { |
51 | | // Todo: implement me! |
52 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "Exif metadata", "RW2")); |
53 | 0 | } |
54 | | |
55 | 0 | void Rw2Image::setIptcData(const IptcData& /*iptcData*/) { |
56 | | // Todo: implement me! |
57 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "IPTC metadata", "RW2")); |
58 | 0 | } |
59 | | |
60 | 0 | void Rw2Image::setComment(const std::string&) { |
61 | | // not supported |
62 | 0 | throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "RW2")); |
63 | 0 | } |
64 | | |
65 | 0 | void Rw2Image::printStructure(std::ostream& out, PrintStructureOption option, size_t depth) { |
66 | 0 | out << "RW2 IMAGE" << '\n'; |
67 | 0 | if (io_->open() != 0) |
68 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
69 | | // Ensure that this is the correct image type |
70 | 0 | if (imageType() == ImageType::none && !isRw2Type(*io_, false)) { |
71 | 0 | if (io_->error() || io_->eof()) |
72 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
73 | 0 | throw Error(ErrorCode::kerNotAJpeg); |
74 | 0 | } |
75 | | |
76 | 0 | io_->seek(0, BasicIo::beg); |
77 | |
|
78 | 0 | printTiffStructure(io(), out, option, depth); |
79 | 0 | } // Rw2Image::printStructure |
80 | | |
81 | 93 | void Rw2Image::readMetadata() { |
82 | | #ifdef EXIV2_DEBUG_MESSAGES |
83 | | std::cerr << "Reading RW2 file " << io_->path() << "\n"; |
84 | | #endif |
85 | 93 | if (io_->open() != 0) { |
86 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
87 | 0 | } |
88 | 93 | IoCloser closer(*io_); |
89 | | // Ensure that this is the correct image type |
90 | 93 | if (!isRw2Type(*io_, false)) { |
91 | 0 | if (io_->error() || io_->eof()) |
92 | 0 | throw Error(ErrorCode::kerFailedToReadImageData); |
93 | 0 | throw Error(ErrorCode::kerNotAnImage, "RW2"); |
94 | 0 | } |
95 | 93 | clearMetadata(); |
96 | 93 | ByteOrder bo = Rw2Parser::decode(exifData_, iptcData_, xmpData_, io_->mmap(), io_->size()); |
97 | 93 | setByteOrder(bo); |
98 | | |
99 | | // A lot more metadata is hidden in the embedded preview image |
100 | | // Todo: This should go into the Rw2Parser, but for that it needs the Image |
101 | 93 | PreviewManager loader(*this); |
102 | 93 | PreviewPropertiesList list = loader.getPreviewProperties(); |
103 | | // Todo: What if there are more preview images? |
104 | 93 | if (list.size() > 1) { |
105 | 0 | #ifndef SUPPRESS_WARNINGS |
106 | 0 | EXV_WARNING << "RW2 image contains more than one preview. None used.\n"; |
107 | 0 | #endif |
108 | 0 | } |
109 | 93 | if (list.size() != 1) |
110 | 92 | return; |
111 | 1 | ExifData exifData; |
112 | 1 | PreviewImage preview = loader.getPreviewImage(*list.begin()); |
113 | 1 | auto image = ImageFactory::open(preview.pData(), preview.size()); |
114 | 1 | if (!image) { |
115 | 0 | #ifndef SUPPRESS_WARNINGS |
116 | 0 | EXV_WARNING << "Failed to open RW2 preview image.\n"; |
117 | 0 | #endif |
118 | 0 | return; |
119 | 0 | } |
120 | 1 | image->readMetadata(); |
121 | 1 | ExifData& prevData = image->exifData(); |
122 | 1 | if (!prevData.empty()) { |
123 | | // Filter duplicate tags |
124 | 0 | for (const auto& pos : exifData_) { |
125 | 0 | if (pos.ifdId() == IfdId::panaRawId) |
126 | 0 | continue; |
127 | 0 | auto dup = prevData.findKey(ExifKey(pos.key())); |
128 | 0 | if (dup != prevData.end()) { |
129 | | #ifdef EXIV2_DEBUG_MESSAGES |
130 | | std::cerr << "Filtering duplicate tag " << pos.key() << " (values '" << pos.value() << "' and '" << dup->value() |
131 | | << "')\n"; |
132 | | #endif |
133 | 0 | prevData.erase(dup); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | | // Remove tags not applicable for raw images |
138 | 1 | static constexpr auto filteredTags = std::array{ |
139 | 1 | "Exif.Photo.ComponentsConfiguration", |
140 | 1 | "Exif.Photo.CompressedBitsPerPixel", |
141 | 1 | "Exif.Panasonic.ColorEffect", |
142 | 1 | "Exif.Panasonic.Contrast", |
143 | 1 | "Exif.Panasonic.NoiseReduction", |
144 | 1 | "Exif.Panasonic.ColorMode", |
145 | 1 | "Exif.Panasonic.OpticalZoomMode", |
146 | 1 | "Exif.Panasonic.Contrast", |
147 | 1 | "Exif.Panasonic.Saturation", |
148 | 1 | "Exif.Panasonic.Sharpness", |
149 | 1 | "Exif.Panasonic.FilmMode", |
150 | 1 | "Exif.Panasonic.SceneMode", |
151 | 1 | "Exif.Panasonic.WBRedLevel", |
152 | 1 | "Exif.Panasonic.WBGreenLevel", |
153 | 1 | "Exif.Panasonic.WBBlueLevel", |
154 | 1 | "Exif.Photo.ColorSpace", |
155 | 1 | "Exif.Photo.PixelXDimension", |
156 | 1 | "Exif.Photo.PixelYDimension", |
157 | 1 | "Exif.Photo.SceneType", |
158 | 1 | "Exif.Photo.CustomRendered", |
159 | 1 | "Exif.Photo.DigitalZoomRatio", |
160 | 1 | "Exif.Photo.SceneCaptureType", |
161 | 1 | "Exif.Photo.GainControl", |
162 | 1 | "Exif.Photo.Contrast", |
163 | 1 | "Exif.Photo.Saturation", |
164 | 1 | "Exif.Photo.Sharpness", |
165 | 1 | "Exif.Image.PrintImageMatching", |
166 | 1 | "Exif.Image.YCbCrPositioning", |
167 | 1 | }; |
168 | 1 | for (auto&& filteredTag : filteredTags) { |
169 | 0 | auto pos = prevData.findKey(ExifKey(filteredTag)); |
170 | 0 | if (pos != prevData.end()) { |
171 | | #ifdef EXIV2_DEBUG_MESSAGES |
172 | | std::cerr << "Exif tag " << pos->key() << " removed\n"; |
173 | | #endif |
174 | 0 | prevData.erase(pos); |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | // Add the remaining tags |
179 | 1 | for (const auto& pos : prevData) { |
180 | 0 | exifData_.add(pos); |
181 | 0 | } |
182 | | |
183 | 1 | } // Rw2Image::readMetadata |
184 | | |
185 | 0 | void Rw2Image::writeMetadata() { |
186 | | // Todo: implement me! |
187 | 0 | throw(Error(ErrorCode::kerWritingImageFormatUnsupported, "RW2")); |
188 | 0 | } // Rw2Image::writeMetadata |
189 | | |
190 | 93 | ByteOrder Rw2Parser::decode(ExifData& exifData, IptcData& iptcData, XmpData& xmpData, const byte* pData, size_t size) { |
191 | 93 | Rw2Header rw2Header; |
192 | 93 | return TiffParserWorker::decode(exifData, iptcData, xmpData, pData, size, Tag::pana, TiffMapping::findDecoder, |
193 | 93 | &rw2Header); |
194 | 93 | } |
195 | | |
196 | | // ************************************************************************* |
197 | | // free functions |
198 | 93 | Image::UniquePtr newRw2Instance(BasicIo::UniquePtr io, bool /*create*/) { |
199 | 93 | auto image = std::make_unique<Rw2Image>(std::move(io)); |
200 | 93 | if (!image->good()) { |
201 | 0 | return nullptr; |
202 | 0 | } |
203 | 93 | return image; |
204 | 93 | } |
205 | | |
206 | 8.31k | bool isRw2Type(BasicIo& iIo, bool advance) { |
207 | 8.31k | const int32_t len = 24; |
208 | 8.31k | byte buf[len]; |
209 | 8.31k | iIo.read(buf, len); |
210 | 8.31k | if (iIo.error() || iIo.eof()) { |
211 | 9 | return false; |
212 | 9 | } |
213 | 8.30k | Rw2Header header; |
214 | 8.30k | bool rc = header.read(buf, len); |
215 | 8.30k | if (!advance || !rc) { |
216 | 8.30k | iIo.seek(-len, BasicIo::cur); |
217 | 8.30k | } |
218 | 8.30k | return rc; |
219 | 8.31k | } |
220 | | |
221 | | } // namespace Exiv2 |