19#if defined(Q_OS_WINDOWS) && !defined(NOMINMAX)
23#include <libraw/libraw.h>
37using pi_unique_ptr = std::unique_ptr<libraw_processed_image_t, std::function<void(libraw_processed_image_t *)>>;
45 "cr2",
"cr3",
"cap",
"cine",
"cs1",
"crw",
46 "dcs",
"dc2",
"dcr",
"dng",
"drf",
"dxo",
51 "mdc",
"mef",
"mfw",
"mos",
"mrw",
56 "r3d",
"raf",
"raw",
"rdc",
"rw2",
"rwl",
"rwz",
57 "sr2",
"srf",
"srw",
"sti",
62inline int raw_scanf_one(
const QByteArray &ba,
const char *fmt,
void *val)
76 if (strcmp(fmt,
"%d") == 0) {
82 *(
static_cast<int *
>(val)) = d;
89 *(
static_cast<float *
>(val)) = f;
98class LibRaw_QIODevice :
public LibRaw_abstract_datastream
101 explicit LibRaw_QIODevice(
QIODevice *device)
105 virtual ~LibRaw_QIODevice()
override
108 virtual int valid()
override
110 return m_device !=
nullptr;
112 virtual int read(
void *ptr,
size_t sz,
size_t nmemb)
override
118 auto data =
reinterpret_cast<char*
>(ptr);
119 for (qint64 r = 0, size = sz * nmemb;
read < size;
read += r) {
120 if (m_device->atEnd()) {
123 r = m_device->read(data + read, size - read);
128 return int(read / sz);
130 virtual int eof()
override
132 return m_device->atEnd() ? 1 : 0;
134 virtual int seek(INT64 o,
int whence)
override
137 auto size = m_device->size();
138 if (whence == SEEK_CUR) {
139 pos = m_device->pos() + o;
141 if (whence == SEEK_END) {
144 if (pos < 0 || m_device->isSequential()) {
147 return m_device->seek(pos) ? 0 : -1;
149 virtual INT64 tell()
override
151 return m_device->pos();
153 virtual INT64 size()
override
155 return m_device->size();
157 virtual char *gets(
char *s,
int sz)
override
159 if (m_device->readLine(s, sz) > 0) {
164 virtual int scanf_one(
const char *fmt,
void *val)
override
167 for (
int xcnt = 0; xcnt < 24 && !m_device->atEnd(); ++xcnt) {
169 if (!m_device->getChar(&c)) {
172 if (ba.
isEmpty() && (c ==
' ' || c ==
'\t')) {
175 if (c ==
'\0' || c ==
' ' || c ==
'\t' || c ==
'\n') {
180 return raw_scanf_one(ba, fmt, val);
182 virtual int get_char()
override
185 if (!m_device->getChar(
reinterpret_cast<char *
>(&c))) {
190#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)) || defined(LIBRAW_OLD_VIDEO_SUPPORT)
191 virtual void *make_jas_stream()
override
218QString createTag(
char *asciiz,
const char *tag)
221 return createTag(value, tag);
224QString createTimeTag(time_t time,
const char *tag)
227 if (value.isValid() && time > 0) {
228 return createTag(value.toString(
Qt::ISODate), tag);
233QString createFlashTag(
short flash,
const char *tag)
238 auto t = QStringLiteral(
"true");
239 auto f = QStringLiteral(
"false");
240 l << QStringLiteral(
"<exif:Fired>%1</exif:Fired>").arg((flash & 1) ? t : f);
241 l << QStringLiteral(
"<exif:Function>%1</exif:Function>").arg((flash & (1 << 5)) ? t : f);
242 l << QStringLiteral(
"<exif:RedEyeMode>%1</exif:RedEyeMode>").arg((flash & (1 << 6)) ? t : f);
243 l << QStringLiteral(
"<exif:Mode>%1</exif:Mode>").arg(lc.toString((
int(flash) >> 3) & 3));
244 l << QStringLiteral(
"<exif:Return>%1</exif:Return>").arg(lc.toString((
int(flash) >> 1) & 3));
248QString createTag(quint64 n,
const char *tag, quint64 invalid = 0)
256QString createTag(qint16 n,
const char *tag, qint16 invalid = 0)
264QString createTag(quint16 n,
const char *tag, quint16 invalid = 0)
272QString createTag(
float f,
const char *tag, qint32 mul = 1)
282QString createTag(libraw_gps_info_t gps,
const char *tag)
286 if (gps.latref !=
'\0') {
288 auto value = QStringLiteral(
"%1,%2%3")
289 .
arg(lc.toString(gps.latitude[0],
'f', 0))
290 .
arg(lc.toString(gps.latitude[1] + gps.latitude[2] / 60,
'f', 4))
292 return createTag(value, tag);
296 if (gps.longref !=
'\0') {
298 auto value = QStringLiteral(
"%1,%2%3")
299 .
arg(lc.toString(gps.longitude[0],
'f', 0))
300 .
arg(lc.toString(gps.longitude[1] + gps.longitude[2] / 60,
'f', 4))
302 return createTag(value, tag);
306 if (gps.altitude != 0) {
307 return createTag(gps.altitude, tag, 1000);
316 lines << QStringLiteral(
"<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>");
317 lines << QStringLiteral(
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"KIMG RAW Plugin\">");
318 lines << QStringLiteral(
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
319 lines << QStringLiteral(
"</rdf:RDF>");
320 lines << QStringLiteral(
"</x:xmpmeta>");
321 for (
auto i = 30; i > 0; --i)
323 lines << QStringLiteral(
"<?xpacket end=\"w\"?>");
327QString updateXmpPacket(
const QString &xmpPacket, LibRaw *rawProcessor)
331 return updateXmpPacket(createXmpPacket(), rawProcessor);
335 lines << QStringLiteral(
"<rdf:Description rdf:about=\"\"");
336 lines << QStringLiteral(
" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"");
337 lines << QStringLiteral(
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"");
338 lines << QStringLiteral(
" xmlns:aux=\"http://ns.adobe.com/exif/1.0/aux/\"");
339 lines << QStringLiteral(
" xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\"");
340 lines << QStringLiteral(
" xmlns:stEvt=\"http://ns.adobe.com/xap/1.0/sType/ResourceEvent#\"");
341 lines << QStringLiteral(
" xmlns:stRef=\"http://ns.adobe.com/xap/1.0/sType/ResourceRef#\"");
342 lines << QStringLiteral(
" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"");
343 lines << QStringLiteral(
" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"");
344 lines << QStringLiteral(
" xmlns:xmpRights=\"http://ns.adobe.com/xap/1.0/rights/\">");
345 lines << QStringLiteral(
"<xmpMM:History>");
346 lines << QStringLiteral(
"<rdf:Seq>");
347 lines << QStringLiteral(
"<rdf:li rdf:parseType=\"Resource\">");
348 lines << QStringLiteral(
"<stEvt:action>converted</stEvt:action>");
349 lines << QStringLiteral(
"<stEvt:parameters>Converted from RAW to Qt Image using KIMG RAW plugin</stEvt:parameters>");
350 lines << QStringLiteral(
"<stEvt:softwareAgent>LibRaw %1</stEvt:softwareAgent>").arg(
QString::fromLatin1(LibRaw::version()));
352 lines << QStringLiteral(
"</rdf:li>");
353 lines << QStringLiteral(
"</rdf:Seq>");
354 lines << QStringLiteral(
"</xmpMM:History>");
356 auto &&iparams = rawProcessor->imgdata.idata;
357 addTag(createTag(iparams.normalized_model,
"tiff:Model"), lines);
358 addTag(createTag(iparams.normalized_make,
"tiff:Make"), lines);
359 addTag(createTag(iparams.software,
"xmp:CreatorTool"), lines);
361 auto &&iother = rawProcessor->imgdata.other;
362 addTag(createTag(createTag(createTag(iother.desc,
"rdf:li"),
"rdf:Alt"),
"dc:description"), lines);
363 addTag(createTag(createTag(createTag(iother.artist,
"rdf:li"),
"rdf:Seq"),
"dc:creator"), lines);
364 addTag(createTag(createTag(createTag(iother.iso_speed,
"rdf:li"),
"rdf:Seq"),
"exif:ISOSpeedRatings"), lines);
365 addTag(createTag(iother.shutter,
"exif:ExposureTime", 1000), lines);
366 addTag(createTag(iother.aperture,
"exif:ApertureValue", 1000), lines);
367 addTag(createTag(iother.focal_len,
"exif:FocalLength", 1000), lines);
368 addTag(createTimeTag(iother.timestamp,
"xmp:CreateDate"), lines);
369 addTag(createTag(iother.parsed_gps,
"exif:GPSLatitude"), lines);
370 addTag(createTag(iother.parsed_gps,
"exif:GPSLongitude"), lines);
371 addTag(createTag(iother.parsed_gps,
"exif:GPSAltitude"), lines);
373 auto &&shotinfo = rawProcessor->imgdata.shootinginfo;
374 addTag(createTag(shotinfo.ExposureMode,
"exif:ExposureMode",
short(-1)), lines);
375 addTag(createTag(shotinfo.MeteringMode,
"exif:MeteringMode",
short(-1)), lines);
376 addTag(createTag(shotinfo.BodySerial,
"aux:SerialNumber"), lines);
378 auto &&color = rawProcessor->imgdata.color;
379 addTag(createFlashTag(color.flash_used,
"exif:Flash"), lines);
381 auto &&lens = rawProcessor->imgdata.lens;
382 addTag(createTag(lens.FocalLengthIn35mmFormat,
"exif:FocalLengthIn35mmFilm"), lines);
383 addTag(createTag(lens.Lens,
"aux:Lens"), lines);
384 addTag(createTag(lens.LensSerial,
"aux:LensSerialNumber"), lines);
385 addTag(createTag(lens.nikon.LensIDNumber ? quint64(lens.nikon.LensIDNumber) : lens.makernotes.LensID,
"aux:LensID"), lines);
387 auto &&makernotes = rawProcessor->imgdata.makernotes;
388 addTag(createTag(makernotes.common.firmware,
"aux:Firmware"), lines);
390 lines << QStringLiteral(
"</rdf:Description>");
391 lines << xmpPacket.
mid(rdfEnd);
397inline void rgbToRgbX(uchar *target,
const uchar *source, qint32 targetSize, qint32 sourceSize)
399 auto s =
reinterpret_cast<const T *
>(source);
400 auto t =
reinterpret_cast<T *
>(target);
401 auto width = std::min(targetSize / 4, sourceSize / 3) / qint32(
sizeof(T));
402 for (qint32 x = 0; x < width; ++x) {
403 t[x * 4 + 0] = s[x * 3 + 0];
404 t[x * 4 + 1] = s[x * 3 + 1];
405 t[x * 4 + 2] = s[x * 3 + 2];
406 t[x * 4 + 3] = std::numeric_limits<T>::max();
411#define C_IQ(a) (((a) & 0xF) << 4)
412#define C_OC(a) (((a) & 0xF) << 8)
413#define C_CW(a) (((a) & 0x1) << 12)
414#define C_AW(a) (((a) & 0x1) << 13)
415#define C_BT(a) (((a) & 0x1) << 14)
416#define C_HS(a) (((a) & 0x1) << 15)
417#define C_CE(a) (((a) & 0x1) << 16)
418#define C_NR(a) (((a) & 0x3) << 17)
419#define C_FC(a) (((a) & 0x1) << 19)
420#define C_SR(a) (((a) & 0x1) << 20)
421#define C_FLAGS(a) (((a) & 0x1) << 31)
423#define T_IQ(a) (((a) >> 4) & 0xF)
424#define T_OC(a) (((a) >> 8) & 0xF)
425#define T_CW(a) (((a) >> 12) & 0x1)
426#define T_AW(a) (((a) >> 13) & 0x1)
427#define T_BT(a) (((a) >> 14) & 0x1)
428#define T_HS(a) (((a) >> 15) & 0x1)
429#define T_CE(a) (((a) >> 16) & 0x1)
430#define T_NR(a) (((a) >> 17) & 0x3)
431#define T_FC(a) (((a) >> 19) & 0x1)
432#define T_SR(a) (((a) >> 20) & 0x1)
433#define T_FLAGS(a) (((a) >> 31) & 0x1)
436#define DEFAULT_IMAGE_QUALITY (C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0) | C_FLAGS(1))
441#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0))
442 auto &&rawparams = rawProcessor->imgdata.params;
444 auto &&rawparams = rawProcessor->imgdata.rawparams;
465 switch (quality / 10) {
467 quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(1);
470 quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
473 quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
476 quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
479 quality = C_IQ(3) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
482 quality = C_IQ(3) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
485 quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
488 quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
491 quality = C_IQ(11) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
494 quality = C_IQ(11) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
497 quality |= C_FLAGS(1);
500 quality = DEFAULT_IMAGE_QUALITY;
502 Q_ASSERT(T_FLAGS(quality));
504 auto &¶ms = rawProcessor->imgdata.params;
512 params.use_camera_wb = T_CW(quality);
520 params.use_auto_wb = T_AW(quality);
529 params.output_bps = T_BT(quality) ? 16 : 8;
539 params.output_color = T_OC(quality);
549 params.user_qual = T_IQ(quality);
557 params.half_size = T_HS(quality);
563 params.dcb_enhance_fl = T_CE(quality);
569 params.fbdd_noiserd = std::min(2, T_NR(quality));
575 params.four_color_rgb = T_FC(quality);
581 params.use_fuji_rotate = T_SR(quality) ? 0 : 1;
586 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
589 setParams(handler, rawProcessor.get());
592 auto device = handler->
device();
593#ifndef EXCLUDE_LibRaw_QIODevice
594 LibRaw_QIODevice stream(device);
595 if (rawProcessor->open_datastream(&stream) != LIBRAW_SUCCESS) {
599 auto ba = device->readAll();
600 if (rawProcessor->open_buffer(ba.
data(), ba.
size()) != LIBRAW_SUCCESS) {
606 if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
611 if (rawProcessor->dcraw_process() != LIBRAW_SUCCESS) {
616 pi_unique_ptr processedImage(rawProcessor->dcraw_make_mem_image(), LibRaw::dcraw_clear_mem);
617 if (processedImage ==
nullptr) {
622 if ((processedImage->type != LIBRAW_IMAGE_BITMAP) ||
623 (processedImage->colors != 1 && processedImage->colors != 3 && processedImage->colors != 4) ||
624 (processedImage->bits != 8 && processedImage->bits != 16)) {
630 switch (processedImage->colors) {
646 img = imageAlloc(processedImage->width, processedImage->height, format);
651 auto rawBytesPerLine = qint32(processedImage->width * processedImage->bits * processedImage->colors + 7) / 8;
652 auto lineSize = std::min(qint32(img.
bytesPerLine()), rawBytesPerLine);
653 for (
int y = 0, h = img.
height(); y < h; ++y) {
656 rgbToRgbX<quint16>(scanline, processedImage->data + rawBytesPerLine * y, img.
bytesPerLine(), rawBytesPerLine);
658 memcpy(scanline, processedImage->data + rawBytesPerLine * y, lineSize);
662 auto &¶ms = rawProcessor->imgdata.params;
663 if (params.output_color == 0) {
664 auto &&color = rawProcessor->imgdata.color;
665 if (
auto profile =
reinterpret_cast<char *
>(color.profile)) {
669 if (processedImage->colors >= 3) {
670 if (params.output_color == 1) {
673 if (params.output_color == 2) {
676 if (params.output_color == 4) {
679 if (params.output_color == 7) {
685 auto &&iparams = rawProcessor->imgdata.idata;
688 if (
auto xmpdata = iparams.xmpdata) {
689 if (
auto xmplen = iparams.xmplen)
693 img.
setText(QStringLiteral(META_KEY_XMP_ADOBE), updateXmpPacket(xmpPacket, rawProcessor.get()));
696 if (!model.isEmpty()) {
697 img.
setText(QStringLiteral(META_KEY_MODEL), model);
700 if (!manufacturer.isEmpty()) {
701 img.
setText(QStringLiteral(META_KEY_MANUFACTURER), manufacturer);
704 if (!software.isEmpty()) {
705 img.
setText(QStringLiteral(META_KEY_SOFTWARE), software);
708 auto &&iother = rawProcessor->imgdata.other;
710 if (!description.isEmpty()) {
711 img.
setText(QStringLiteral(META_KEY_DESCRIPTION), description);
714 if (!artist.isEmpty()) {
715 img.
setText(QStringLiteral(META_KEY_AUTHOR), artist);
723RAWHandler::RAWHandler()
731bool RAWHandler::canRead()
const
733 if (canRead(device())) {
740bool RAWHandler::read(
QImage *image)
745 if (!dev->isSequential()) {
746 if (m_startPos < 0) {
747 m_startPos = dev->pos();
749 dev->seek(m_startPos);
759 if (!LoadRAW(
this, img)) {
767void RAWHandler::setOption(ImageOption option,
const QVariant &value)
771 auto q = value.
toInt(&ok);
778bool RAWHandler::supportsOption(ImageOption option)
const
780#ifndef EXCLUDE_LibRaw_QIODevice
793QVariant RAWHandler::option(ImageOption option)
const
797#ifndef EXCLUDE_LibRaw_QIODevice
800 d->startTransaction();
801 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
802 LibRaw_QIODevice stream(d);
803#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0))
804 rawProcessor->imgdata.params.shot_select = currentImageNumber();
806 rawProcessor->imgdata.rawparams.shot_select = currentImageNumber();
808 if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
809 auto w = libraw_get_iwidth(&rawProcessor->imgdata);
810 auto h = libraw_get_iheight(&rawProcessor->imgdata);
812 v = (rawProcessor->imgdata.sizes.flip & 4) ?
QSize(h, w) :
QSize(w, h);
814 d->rollbackTransaction();
825bool RAWHandler::jumpToNextImage()
827 return jumpToImage(m_imageNumber + 1);
830bool RAWHandler::jumpToImage(
int imageNumber)
832 if (imageNumber < 0 || imageNumber >= imageCount()) {
835 m_imageNumber = imageNumber;
839int RAWHandler::imageCount()
const
842 auto &&count = m_imageCount;
849#ifndef EXCLUDE_LibRaw_QIODevice
851 d->startTransaction();
853 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
854 LibRaw_QIODevice stream(d);
855 if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
856 count = rawProcessor->imgdata.rawdata.iparams.raw_count;
859 d->rollbackTransaction();
865int RAWHandler::currentImageNumber()
const
867 return m_imageNumber;
870bool RAWHandler::canRead(
QIODevice *device)
873 qWarning(
"RAWHandler::canRead() called with no device");
882 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
884#ifndef EXCLUDE_LibRaw_QIODevice
885 LibRaw_QIODevice stream(device);
886 auto ok = rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS;
889 auto ok = rawProcessor->open_buffer(ba.
data(), ba.
size()) == LIBRAW_SUCCESS;
898 if (supported_formats.contains(
QByteArray(format).toLower())) {
909 if (device->
isReadable() && RAWHandler::canRead(device)) {
923#include "moc_raw_p.cpp"
char * toString(const EngineQuery &query)
QFlags< Capability > Capabilities
QVariant read(const QByteArray &data, int versionOverride=0)
QByteArray & append(QByteArrayView data)
bool isEmpty() const const
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
QDateTime currentDateTimeUtc()
QDateTime fromSecsSinceEpoch(qint64 secs)
qsizetype bytesPerLine() const const
bool isNull() const const
void setColorSpace(const QColorSpace &colorSpace)
void setText(const QString &key, const QString &text)
virtual int currentImageNumber() const const
QIODevice * device() const const
virtual int imageCount() const const
virtual QVariant option(ImageOption option) const const
void setDevice(QIODevice *device)
virtual bool supportsOption(ImageOption option) const const
bool isOpen() const const
bool isReadable() const const
virtual bool isSequential() const const
void rollbackTransaction()
float toFloat(QStringView s, bool *ok) const const
int toInt(QStringView s, bool *ok) const const
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
QString mid(qsizetype position, qsizetype n) const const
QString join(QChar separator) const const
int toInt(bool *ok) const const