You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exiv2/src/rw2image.cpp

221 lines
6.6 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// included header files
#include "rw2image.hpp"
#include "config.h"
#include "error.hpp"
#include "futils.hpp"
#include "image.hpp"
#include "preview.hpp"
#include "rw2image_int.hpp"
#include "tiffcomposite_int.hpp"
#include "tiffimage_int.hpp"
// + standard includes
#include <array>
#ifdef EXIV2_DEBUG_MESSAGES
#include <iostream>
#endif
// *****************************************************************************
// class member definitions
namespace Exiv2 {
using namespace Internal;
Rw2Image::Rw2Image(BasicIo::UniquePtr io) : Image(ImageType::rw2, mdExif | mdIptc | mdXmp, std::move(io)) {
} // Rw2Image::Rw2Image
std::string Rw2Image::mimeType() const {
return "image/x-panasonic-rw2";
}
uint32_t Rw2Image::pixelWidth() const {
auto imageWidth = exifData_.findKey(Exiv2::ExifKey("Exif.PanasonicRaw.SensorWidth"));
if (imageWidth != exifData_.end() && imageWidth->count() > 0) {
return imageWidth->toUint32();
}
return 0;
}
uint32_t Rw2Image::pixelHeight() const {
auto imageHeight = exifData_.findKey(Exiv2::ExifKey("Exif.PanasonicRaw.SensorHeight"));
if (imageHeight != exifData_.end() && imageHeight->count() > 0) {
return imageHeight->toUint32();
}
return 0;
}
void Rw2Image::setExifData(const ExifData& /*exifData*/) {
// Todo: implement me!
throw(Error(ErrorCode::kerInvalidSettingForImage, "Exif metadata", "RW2"));
}
void Rw2Image::setIptcData(const IptcData& /*iptcData*/) {
// Todo: implement me!
throw(Error(ErrorCode::kerInvalidSettingForImage, "IPTC metadata", "RW2"));
}
void Rw2Image::setComment(const std::string&) {
// not supported
throw(Error(ErrorCode::kerInvalidSettingForImage, "Image comment", "RW2"));
}
void Rw2Image::printStructure(std::ostream& out, PrintStructureOption option, size_t depth) {
out << "RW2 IMAGE" << std::endl;
if (io_->open() != 0)
throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
// Ensure that this is the correct image type
if (imageType() == ImageType::none && !isRw2Type(*io_, false)) {
if (io_->error() || io_->eof())
throw Error(ErrorCode::kerFailedToReadImageData);
throw Error(ErrorCode::kerNotAJpeg);
}
io_->seek(0, BasicIo::beg);
printTiffStructure(io(), out, option, depth);
} // Rw2Image::printStructure
void Rw2Image::readMetadata() {
#ifdef EXIV2_DEBUG_MESSAGES
std::cerr << "Reading RW2 file " << io_->path() << "\n";
#endif
if (io_->open() != 0) {
throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
}
IoCloser closer(*io_);
// Ensure that this is the correct image type
if (!isRw2Type(*io_, false)) {
if (io_->error() || io_->eof())
throw Error(ErrorCode::kerFailedToReadImageData);
throw Error(ErrorCode::kerNotAnImage, "RW2");
}
clearMetadata();
ByteOrder bo = Rw2Parser::decode(exifData_, iptcData_, xmpData_, io_->mmap(), io_->size());
setByteOrder(bo);
// A lot more metadata is hidden in the embedded preview image
// Todo: This should go into the Rw2Parser, but for that it needs the Image
PreviewManager loader(*this);
PreviewPropertiesList list = loader.getPreviewProperties();
// Todo: What if there are more preview images?
if (list.size() > 1) {
#ifndef SUPPRESS_WARNINGS
EXV_WARNING << "RW2 image contains more than one preview. None used.\n";
#endif
}
if (list.size() != 1)
return;
ExifData exifData;
PreviewImage preview = loader.getPreviewImage(*list.begin());
auto image = ImageFactory::open(preview.pData(), preview.size());
if (!image) {
#ifndef SUPPRESS_WARNINGS
EXV_WARNING << "Failed to open RW2 preview image.\n";
#endif
return;
}
image->readMetadata();
ExifData& prevData = image->exifData();
if (!prevData.empty()) {
// Filter duplicate tags
for (const auto& pos : exifData_) {
if (pos.ifdId() == IfdId::panaRawId)
continue;
auto dup = prevData.findKey(ExifKey(pos.key()));
if (dup != prevData.end()) {
#ifdef EXIV2_DEBUG_MESSAGES
std::cerr << "Filtering duplicate tag " << pos.key() << " (values '" << pos.value() << "' and '" << dup->value()
<< "')\n";
#endif
prevData.erase(dup);
}
}
}
// Remove tags not applicable for raw images
static constexpr auto filteredTags = std::array{
"Exif.Photo.ComponentsConfiguration",
"Exif.Photo.CompressedBitsPerPixel",
"Exif.Panasonic.ColorEffect",
"Exif.Panasonic.Contrast",
"Exif.Panasonic.NoiseReduction",
"Exif.Panasonic.ColorMode",
"Exif.Panasonic.OpticalZoomMode",
"Exif.Panasonic.Contrast",
"Exif.Panasonic.Saturation",
"Exif.Panasonic.Sharpness",
"Exif.Panasonic.FilmMode",
"Exif.Panasonic.SceneMode",
"Exif.Panasonic.WBRedLevel",
"Exif.Panasonic.WBGreenLevel",
"Exif.Panasonic.WBBlueLevel",
"Exif.Photo.ColorSpace",
"Exif.Photo.PixelXDimension",
"Exif.Photo.PixelYDimension",
"Exif.Photo.SceneType",
"Exif.Photo.CustomRendered",
"Exif.Photo.DigitalZoomRatio",
"Exif.Photo.SceneCaptureType",
"Exif.Photo.GainControl",
"Exif.Photo.Contrast",
"Exif.Photo.Saturation",
"Exif.Photo.Sharpness",
"Exif.Image.PrintImageMatching",
"Exif.Image.YCbCrPositioning",
};
for (auto&& filteredTag : filteredTags) {
auto pos = prevData.findKey(ExifKey(filteredTag));
if (pos != prevData.end()) {
#ifdef EXIV2_DEBUG_MESSAGES
std::cerr << "Exif tag " << pos->key() << " removed\n";
#endif
prevData.erase(pos);
}
}
// Add the remaining tags
for (const auto& pos : prevData) {
exifData_.add(pos);
}
} // Rw2Image::readMetadata
void Rw2Image::writeMetadata() {
// Todo: implement me!
throw(Error(ErrorCode::kerWritingImageFormatUnsupported, "RW2"));
} // Rw2Image::writeMetadata
ByteOrder Rw2Parser::decode(ExifData& exifData, IptcData& iptcData, XmpData& xmpData, const byte* pData, size_t size) {
Rw2Header rw2Header;
return TiffParserWorker::decode(exifData, iptcData, xmpData, pData, size, Tag::pana, TiffMapping::findDecoder,
&rw2Header);
}
// *************************************************************************
// free functions
Image::UniquePtr newRw2Instance(BasicIo::UniquePtr io, bool /*create*/) {
auto image = std::make_unique<Rw2Image>(std::move(io));
if (!image->good()) {
return nullptr;
}
return image;
}
bool isRw2Type(BasicIo& iIo, bool advance) {
const int32_t len = 24;
byte buf[len];
iIo.read(buf, len);
if (iIo.error() || iIo.eof()) {
return false;
}
Rw2Header header;
bool rc = header.read(buf, len);
if (!advance || !rc) {
iIo.seek(-len, BasicIo::cur);
}
return rc;
}
} // namespace Exiv2