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 *)>>;
50 "mdc",
"mef",
"mos",
"mrw",
54 "raf",
"raw",
"rwl",
"rw2",
55 "sr2",
"srf",
"srw",
"sti",
60inline int raw_scanf_one(
const QByteArray &ba,
const char *fmt,
void *val)
74 if (strcmp(fmt,
"%d") == 0) {
80 *(
static_cast<int *
>(val)) = d;
87 *(
static_cast<float *
>(val)) = f;
96class LibRaw_QIODevice :
public LibRaw_abstract_datastream
99 explicit LibRaw_QIODevice(QIODevice *device)
103 virtual ~LibRaw_QIODevice()
override
106 virtual int valid()
override
108 return m_device !=
nullptr;
110 virtual int read(
void *ptr,
size_t sz,
size_t nmemb)
override
116 auto data =
reinterpret_cast<char*
>(ptr);
117 for (qint64 r = 0, size = sz * nmemb;
read < size;
read += r) {
118 if (m_device->atEnd()) {
121 r = m_device->read(data + read, size - read);
126 return int(read / sz);
128 virtual int eof()
override
130 return m_device->atEnd() ? 1 : 0;
132 virtual int seek(INT64 o,
int whence)
override
135 auto size = m_device->size();
136 if (whence == SEEK_CUR) {
137 pos = m_device->pos() + o;
139 if (whence == SEEK_END) {
142 if (pos < 0 || m_device->isSequential()) {
145 return m_device->seek(pos) ? 0 : -1;
147 virtual INT64 tell()
override
149 return m_device->pos();
151 virtual INT64 size()
override
153 return m_device->size();
155 virtual char *gets(
char *s,
int sz)
override
157 if (m_device->readLine(s, sz) > 0) {
162 virtual int scanf_one(
const char *fmt,
void *val)
override
165 for (
int xcnt = 0; xcnt < 24 && !m_device->atEnd(); ++xcnt) {
167 if (!m_device->getChar(&c)) {
170 if (ba.
isEmpty() && (c ==
' ' || c ==
'\t')) {
173 if (c ==
'\0' || c ==
' ' || c ==
'\t' || c ==
'\n') {
178 return raw_scanf_one(ba, fmt, val);
180 virtual int get_char()
override
183 if (!m_device->getChar(
reinterpret_cast<char *
>(&c))) {
188#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)) || defined(LIBRAW_OLD_VIDEO_SUPPORT)
189 virtual void *make_jas_stream()
override
216QString createTag(
char *asciiz,
const char *tag)
219 return createTag(value, tag);
222QString createTimeTag(time_t time,
const char *tag)
225 if (value.isValid() && time > 0) {
226 return createTag(value.toString(
Qt::ISODate), tag);
231QString createFlashTag(
short flash,
const char *tag)
236 auto t = QStringLiteral(
"true");
237 auto f = QStringLiteral(
"false");
238 l << QStringLiteral(
"<exif:Fired>%1</exif:Fired>").arg((flash & 1) ? t : f);
239 l << QStringLiteral(
"<exif:Function>%1</exif:Function>").arg((flash & (1 << 5)) ? t : f);
240 l << QStringLiteral(
"<exif:RedEyeMode>%1</exif:RedEyeMode>").arg((flash & (1 << 6)) ? t : f);
241 l << QStringLiteral(
"<exif:Mode>%1</exif:Mode>").arg(lc.toString((
int(flash) >> 3) & 3));
242 l << QStringLiteral(
"<exif:Return>%1</exif:Return>").arg(lc.toString((
int(flash) >> 1) & 3));
246QString createTag(quint64 n,
const char *tag, quint64 invalid = 0)
254QString createTag(qint16 n,
const char *tag, qint16 invalid = 0)
262QString createTag(quint16 n,
const char *tag, quint16 invalid = 0)
270QString createTag(
float f,
const char *tag, qint32 mul = 1)
280QString createTag(libraw_gps_info_t gps,
const char *tag)
284 if (gps.latref !=
'\0') {
286 auto value = QStringLiteral(
"%1,%2%3")
287 .
arg(lc.toString(gps.latitude[0],
'f', 0))
288 .
arg(lc.toString(gps.latitude[1] + gps.latitude[2] / 60,
'f', 4))
290 return createTag(value, tag);
294 if (gps.longref !=
'\0') {
296 auto value = QStringLiteral(
"%1,%2%3")
297 .
arg(lc.toString(gps.longitude[0],
'f', 0))
298 .
arg(lc.toString(gps.longitude[1] + gps.longitude[2] / 60,
'f', 4))
300 return createTag(value, tag);
304 if (gps.altitude != 0) {
305 return createTag(gps.altitude, tag, 1000);
314 lines << QStringLiteral(
"<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>");
315 lines << QStringLiteral(
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"KIMG RAW Plugin\">");
316 lines << QStringLiteral(
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
317 lines << QStringLiteral(
"</rdf:RDF>");
318 lines << QStringLiteral(
"</x:xmpmeta>");
319 for (
auto i = 30; i > 0; --i)
321 lines << QStringLiteral(
"<?xpacket end=\"w\"?>");
325QString updateXmpPacket(
const QString &xmpPacket, LibRaw *rawProcessor)
329 return updateXmpPacket(createXmpPacket(), rawProcessor);
333 lines << QStringLiteral(
"<rdf:Description rdf:about=\"\"");
334 lines << QStringLiteral(
" xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"");
335 lines << QStringLiteral(
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"");
336 lines << QStringLiteral(
" xmlns:aux=\"http://ns.adobe.com/exif/1.0/aux/\"");
337 lines << QStringLiteral(
" xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\"");
338 lines << QStringLiteral(
" xmlns:stEvt=\"http://ns.adobe.com/xap/1.0/sType/ResourceEvent#\"");
339 lines << QStringLiteral(
" xmlns:stRef=\"http://ns.adobe.com/xap/1.0/sType/ResourceRef#\"");
340 lines << QStringLiteral(
" xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"");
341 lines << QStringLiteral(
" xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"");
342 lines << QStringLiteral(
" xmlns:xmpRights=\"http://ns.adobe.com/xap/1.0/rights/\">");
343 lines << QStringLiteral(
"<xmpMM:History>");
344 lines << QStringLiteral(
"<rdf:Seq>");
345 lines << QStringLiteral(
"<rdf:li rdf:parseType=\"Resource\">");
346 lines << QStringLiteral(
"<stEvt:action>converted</stEvt:action>");
347 lines << QStringLiteral(
"<stEvt:parameters>Converted from RAW to Qt Image using KIMG RAW plugin</stEvt:parameters>");
348 lines << QStringLiteral(
"<stEvt:softwareAgent>LibRaw %1</stEvt:softwareAgent>").arg(
QString::fromLatin1(LibRaw::version()));
350 lines << QStringLiteral(
"</rdf:li>");
351 lines << QStringLiteral(
"</rdf:Seq>");
352 lines << QStringLiteral(
"</xmpMM:History>");
354 auto &&iparams = rawProcessor->imgdata.idata;
355 addTag(createTag(iparams.normalized_model,
"tiff:Model"), lines);
356 addTag(createTag(iparams.normalized_make,
"tiff:Make"), lines);
357 addTag(createTag(iparams.software,
"xmp:CreatorTool"), lines);
359 auto &&iother = rawProcessor->imgdata.other;
360 addTag(createTag(createTag(createTag(iother.desc,
"rdf:li"),
"rdf:Alt"),
"dc:description"), lines);
361 addTag(createTag(createTag(createTag(iother.artist,
"rdf:li"),
"rdf:Seq"),
"dc:creator"), lines);
362 addTag(createTag(createTag(createTag(iother.iso_speed,
"rdf:li"),
"rdf:Seq"),
"exif:ISOSpeedRatings"), lines);
363 addTag(createTag(iother.shutter,
"exif:ExposureTime", 1000), lines);
364 addTag(createTag(iother.aperture,
"exif:ApertureValue", 1000), lines);
365 addTag(createTag(iother.focal_len,
"exif:FocalLength", 1000), lines);
366 addTag(createTimeTag(iother.timestamp,
"xmp:CreateDate"), lines);
367 addTag(createTag(iother.parsed_gps,
"exif:GPSLatitude"), lines);
368 addTag(createTag(iother.parsed_gps,
"exif:GPSLongitude"), lines);
369 addTag(createTag(iother.parsed_gps,
"exif:GPSAltitude"), lines);
371 auto &&shotinfo = rawProcessor->imgdata.shootinginfo;
372 addTag(createTag(shotinfo.ExposureMode,
"exif:ExposureMode",
short(-1)), lines);
373 addTag(createTag(shotinfo.MeteringMode,
"exif:MeteringMode",
short(-1)), lines);
374 addTag(createTag(shotinfo.BodySerial,
"aux:SerialNumber"), lines);
376 auto &&color = rawProcessor->imgdata.color;
377 addTag(createFlashTag(color.flash_used,
"exif:Flash"), lines);
379 auto &&lens = rawProcessor->imgdata.lens;
380 addTag(createTag(lens.FocalLengthIn35mmFormat,
"exif:FocalLengthIn35mmFilm"), lines);
381 addTag(createTag(lens.Lens,
"aux:Lens"), lines);
382 addTag(createTag(lens.LensSerial,
"aux:LensSerialNumber"), lines);
383 addTag(createTag(lens.nikon.LensIDNumber ? quint64(lens.nikon.LensIDNumber) : lens.makernotes.LensID,
"aux:LensID"), lines);
385 auto &&makernotes = rawProcessor->imgdata.makernotes;
386 addTag(createTag(makernotes.common.firmware,
"aux:Firmware"), lines);
388 lines << QStringLiteral(
"</rdf:Description>");
389 lines << xmpPacket.
mid(rdfEnd);
395inline void rgbToRgbX(uchar *target,
const uchar *source, qint32 targetSize, qint32 sourceSize)
397 auto s =
reinterpret_cast<const T *
>(source);
398 auto t =
reinterpret_cast<T *
>(target);
399 auto width = std::min(targetSize / 4, sourceSize / 3) / qint32(
sizeof(T));
400 for (qint32 x = 0; x < width; ++x) {
401 t[x * 4 + 0] = s[x * 3 + 0];
402 t[x * 4 + 1] = s[x * 3 + 1];
403 t[x * 4 + 2] = s[x * 3 + 2];
404 t[x * 4 + 3] = std::numeric_limits<T>::max();
409#define C_IQ(a) (((a) & 0xF) << 4)
410#define C_OC(a) (((a) & 0xF) << 8)
411#define C_CW(a) (((a) & 0x1) << 12)
412#define C_AW(a) (((a) & 0x1) << 13)
413#define C_BT(a) (((a) & 0x1) << 14)
414#define C_HS(a) (((a) & 0x1) << 15)
415#define C_CE(a) (((a) & 0x1) << 16)
416#define C_NR(a) (((a) & 0x3) << 17)
417#define C_FC(a) (((a) & 0x1) << 19)
418#define C_SR(a) (((a) & 0x1) << 20)
419#define C_FLAGS(a) (((a) & 0x1) << 31)
421#define T_IQ(a) (((a) >> 4) & 0xF)
422#define T_OC(a) (((a) >> 8) & 0xF)
423#define T_CW(a) (((a) >> 12) & 0x1)
424#define T_AW(a) (((a) >> 13) & 0x1)
425#define T_BT(a) (((a) >> 14) & 0x1)
426#define T_HS(a) (((a) >> 15) & 0x1)
427#define T_CE(a) (((a) >> 16) & 0x1)
428#define T_NR(a) (((a) >> 17) & 0x3)
429#define T_FC(a) (((a) >> 19) & 0x1)
430#define T_SR(a) (((a) >> 20) & 0x1)
431#define T_FLAGS(a) (((a) >> 31) & 0x1)
434#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))
439#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0))
440 auto &&rawparams = rawProcessor->imgdata.params;
442 auto &&rawparams = rawProcessor->imgdata.rawparams;
463 switch (quality / 10) {
465 quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(1);
468 quality = C_IQ(0) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
471 quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
474 quality = C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
477 quality = C_IQ(3) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
480 quality = C_IQ(3) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
483 quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(0) | C_HS(0);
486 quality = C_IQ(11) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
489 quality = C_IQ(11) | C_OC(2) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
492 quality = C_IQ(11) | C_OC(4) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0);
495 quality |= C_FLAGS(1);
498 quality = DEFAULT_IMAGE_QUALITY;
500 Q_ASSERT(T_FLAGS(quality));
502 auto &¶ms = rawProcessor->imgdata.params;
510 params.use_camera_wb = T_CW(quality);
518 params.use_auto_wb = T_AW(quality);
527 params.output_bps = T_BT(quality) ? 16 : 8;
537 params.output_color = T_OC(quality);
547 params.user_qual = T_IQ(quality);
555 params.half_size = T_HS(quality);
561 params.dcb_enhance_fl = T_CE(quality);
567 params.fbdd_noiserd = std::min(2, T_NR(quality));
573 params.four_color_rgb = T_FC(quality);
579 params.use_fuji_rotate = T_SR(quality) ? 0 : 1;
584 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
587 auto device = handler->
device();
588#ifndef EXCLUDE_LibRaw_QIODevice
589 LibRaw_QIODevice stream(device);
590 if (rawProcessor->open_datastream(&stream) != LIBRAW_SUCCESS) {
594 auto ba = device->readAll();
595 if (rawProcessor->open_buffer(ba.
data(), ba.
size()) != LIBRAW_SUCCESS) {
600#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0))
602 if (rawProcessor->unpack_thumb() != LIBRAW_SUCCESS) {
607 auto &&tlist = rawProcessor->imgdata.thumbs_list;
609 for (
auto n = 1; n < std::min(tlist.thumbcount, LIBRAW_THUMBNAIL_MAXCOUNT); ++n) {
610 if (tlist.thumblist[n].twidth > tlist.thumblist[idx].twidth)
615 if (rawProcessor->unpack_thumb_ex(idx) != LIBRAW_SUCCESS) {
621 pi_unique_ptr processedImage(rawProcessor->dcraw_make_mem_thumb(), LibRaw::dcraw_clear_mem);
622 if (processedImage ==
nullptr) {
625 auto ba =
QByteArray(
reinterpret_cast<const char *
>(processedImage->data), qsizetype(processedImage->data_size));
629 if (processedImage->type == LIBRAW_IMAGE_BITMAP) {
633 .
arg(processedImage->width)
634 .
arg(processedImage->height)
635 .
arg((1 << processedImage->bits)-1);
644 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
647 setParams(handler, rawProcessor.get());
650 auto device = handler->
device();
651#ifndef EXCLUDE_LibRaw_QIODevice
652 LibRaw_QIODevice stream(device);
653 if (rawProcessor->open_datastream(&stream) != LIBRAW_SUCCESS) {
657 auto ba = device->readAll();
658 if (rawProcessor->open_buffer(ba.
data(), ba.
size()) != LIBRAW_SUCCESS) {
664 if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
669 if (rawProcessor->dcraw_process() != LIBRAW_SUCCESS) {
674 pi_unique_ptr processedImage(rawProcessor->dcraw_make_mem_image(), LibRaw::dcraw_clear_mem);
675 if (processedImage ==
nullptr) {
680 if ((processedImage->type != LIBRAW_IMAGE_BITMAP) ||
681 (processedImage->colors != 1 && processedImage->colors != 3 && processedImage->colors != 4) ||
682 (processedImage->bits != 8 && processedImage->bits != 16)) {
688 switch (processedImage->colors) {
704 img = imageAlloc(processedImage->width, processedImage->height, format);
709 auto rawBytesPerLine = qint32(processedImage->width * processedImage->bits * processedImage->colors + 7) / 8;
710 auto lineSize = std::min(qint32(img.
bytesPerLine()), rawBytesPerLine);
711 for (
int y = 0, h = img.
height(); y < h; ++y) {
714 rgbToRgbX<quint16>(scanline, processedImage->data + rawBytesPerLine * y, img.
bytesPerLine(), rawBytesPerLine);
716 memcpy(scanline, processedImage->data + rawBytesPerLine * y, lineSize);
720 auto &¶ms = rawProcessor->imgdata.params;
721 if (params.output_color == 0) {
722 auto &&color = rawProcessor->imgdata.color;
723 if (
auto profile =
reinterpret_cast<char *
>(color.profile)) {
727 if (processedImage->colors >= 3) {
728 if (params.output_color == 1) {
731 if (params.output_color == 2) {
734 if (params.output_color == 4) {
737 if (params.output_color == 7) {
740#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
741 if (params.output_color == 8) {
748 auto &&iparams = rawProcessor->imgdata.idata;
751 if (
auto xmpdata = iparams.xmpdata) {
752 if (
auto xmplen = iparams.xmplen)
756 img.
setText(QStringLiteral(META_KEY_XMP_ADOBE), updateXmpPacket(xmpPacket, rawProcessor.get()));
759 if (!model.isEmpty()) {
760 img.
setText(QStringLiteral(META_KEY_MODEL), model);
763 if (!manufacturer.isEmpty()) {
764 img.
setText(QStringLiteral(META_KEY_MANUFACTURER), manufacturer);
767 if (!software.isEmpty()) {
768 img.
setText(QStringLiteral(META_KEY_SOFTWARE), software);
771 auto &&iother = rawProcessor->imgdata.other;
773 if (!description.isEmpty()) {
774 img.
setText(QStringLiteral(META_KEY_DESCRIPTION), description);
777 if (!artist.isEmpty()) {
778 img.
setText(QStringLiteral(META_KEY_AUTHOR), artist);
786RAWHandler::RAWHandler()
794bool RAWHandler::canRead()
const
796 if (canRead(device())) {
803bool RAWHandler::read(
QImage *image)
808 if (!dev->isSequential()) {
809 if (m_startPos < 0) {
810 m_startPos = dev->pos();
812 dev->seek(m_startPos);
823 if (m_quality == 0) {
824 ok = LoadTHUMB(
this, img);
825 if (!ok && !dev->isSequential())
826 dev->seek(m_startPos);
829 ok = LoadRAW(
this, img);
839void RAWHandler::setOption(ImageOption option,
const QVariant &value)
843 auto q = value.
toInt(&ok);
850bool RAWHandler::supportsOption(ImageOption option)
const
852#ifndef EXCLUDE_LibRaw_QIODevice
865QVariant RAWHandler::option(ImageOption option)
const
869#ifndef EXCLUDE_LibRaw_QIODevice
872 d->startTransaction();
873 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
874 LibRaw_QIODevice stream(d);
875#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0))
876 rawProcessor->imgdata.params.shot_select = currentImageNumber();
878 rawProcessor->imgdata.rawparams.shot_select = currentImageNumber();
880 if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
881 auto w = libraw_get_iwidth(&rawProcessor->imgdata);
882 auto h = libraw_get_iheight(&rawProcessor->imgdata);
884 v = (rawProcessor->imgdata.sizes.flip & 4) ?
QSize(h, w) :
QSize(w, h);
886 d->rollbackTransaction();
897bool RAWHandler::jumpToNextImage()
899 return jumpToImage(m_imageNumber + 1);
902bool RAWHandler::jumpToImage(
int imageNumber)
904 if (imageNumber < 0 || imageNumber >= imageCount()) {
907 m_imageNumber = imageNumber;
911int RAWHandler::imageCount()
const
914 auto &&count = m_imageCount;
921#ifndef EXCLUDE_LibRaw_QIODevice
923 d->startTransaction();
925 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
926 LibRaw_QIODevice stream(d);
927 if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
928 count = rawProcessor->imgdata.rawdata.iparams.raw_count;
931 d->rollbackTransaction();
937int RAWHandler::currentImageNumber()
const
939 return m_imageNumber;
942bool RAWHandler::canRead(
QIODevice *device)
945 qWarning(
"RAWHandler::canRead() called with no device");
954 std::unique_ptr<LibRaw> rawProcessor(
new LibRaw);
956#ifndef EXCLUDE_LibRaw_QIODevice
957 LibRaw_QIODevice stream(device);
958 auto ok = rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS;
961 auto ok = rawProcessor->open_buffer(ba.
data(), ba.
size()) == LIBRAW_SUCCESS;
970 if (supported_formats.contains(
QByteArray(format).toLower())) {
981 if (device->
isReadable() && RAWHandler::canRead(device)) {
995#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
QByteArray & prepend(QByteArrayView ba)
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
QDateTime currentDateTimeUtc()
QDateTime fromSecsSinceEpoch(qint64 secs)
qsizetype bytesPerLine() const const
bool isNull() const const
bool loadFromData(QByteArrayView data, const char *format)
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