EXIF improvements and bugfixes

- Fixes a writing issue for float values ​​less than 1
- Fixes a missing definition of `EXIF_DATETIMEDIGITIZED` tag
- Adds support for some common camera shot metadata
- Adds missing metadata to writing tests

The following plugins automatically gain support for the new metadata: AVIF, IFF, HEIF, JXL, JXR, PSD and TGA (V2E).

The new metadata added with this patch is usually saved by smartphones (e.g. iPhone or Google Pixel).
This commit is contained in:
Mirco Miranda
2026-04-26 06:08:25 +02:00
parent 51db11eefc
commit 191e5e6a69
7 changed files with 559 additions and 13 deletions

View File

@@ -155,8 +155,31 @@ About the image:
- `Owner`: Name of the owner of the image. - `Owner`: Name of the owner of the image.
- `Software`: Name and version number of the software package(s) used to - `Software`: Name and version number of the software package(s) used to
create the image. create the image.
- `Speed`: Floating-point number indicating the speed of GPS receiver
movement in Km/h (e.g. 30.2).
- `Title`: The title of the image. - `Title`: The title of the image.
About the shot:
- `DigitalZoomRatio`: Floating-point number indicating the digital zoom ratio
when the image was shot.
- `ExposureMode`: Integer number indicating the exposure mode set when the
image was shot as reported in the EXIF specifications.
- `ExposureProgram`: Integer number indicating the class of the program used
by the camera to set exposure when the picture is taken as reported in the
EXIF specifications.
- `ExposureTime`: Floating-point number indicating the exposure time,
given in seconds (s).
- `Flash`: Integer number indicating the status of flash when the image
was shot as reported in the EXIF specifications.
- `FNumber`: Floating-point number indicating the F number.
- `FocalLength`: Floating-point number indicating the actual focal length
of the lens, in millimeters (mm).
- `ISOSpeedRatings`: Integer number indicating the sensitivity of the camera
or input device when the image was shot as reported in the EXIF
specifications.
- `WhiteBalance`: Integer number indicating the white balance mode set when
the image was shot as reported in the EXIF specifications.
About the camera: About the camera:
- `Manufacturer`: The manufacturer of the recording equipment. - `Manufacturer`: The manufacturer of the recording equipment.
- `Model`: The model name or model number of the recording equipment. - `Model`: The model name or model number of the recording equipment.

View File

@@ -13,14 +13,18 @@
"key" : "ModificationDate", "key" : "ModificationDate",
"value" : "2025-02-14T15:58:44+01:00" "value" : "2025-02-14T15:58:44+01:00"
}, },
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.2 (Windows)"
},
{ {
"key" : "Altitude", "key" : "Altitude",
"value" : "34" "value" : "34"
}, },
{
"key" : "Title",
"value" : "A test"
},
{
"key" : "Software",
"value" : "KImageFormats write test"
},
{ {
"key" : "Author", "key" : "Author",
"value" : "KDE Project" "value" : "KDE Project"
@@ -45,6 +49,10 @@
"key" : "LensModel", "key" : "LensModel",
"value" : "A1234" "value" : "A1234"
}, },
{
"key" : "LensSerialNumber",
"value" : "S/N:1234567"
},
{ {
"key" : "Longitude", "key" : "Longitude",
"value" : "10.9254" "value" : "10.9254"
@@ -56,6 +64,50 @@
{ {
"key" : "Model", "key" : "Model",
"value" : "KImageFormats" "value" : "KImageFormats"
},
{
"key" : "SerialNumber",
"value" : "S/N:7654321"
},
{
"key" : "Speed",
"value" : "13.2"
},
{
"key" : "DigitalZoomRatio",
"value" : "3.4"
},
{
"key" : "ExposureMode",
"value" : "2"
},
{
"key" : "ExposureProgram",
"value" : "6"
},
{
"key" : "ExposureTime",
"value" : "0.004"
},
{
"key" : "Flash",
"value" : "16"
},
{
"key" : "FNumber",
"value" : "1.6"
},
{
"key" : "FocalLength",
"value" : "5.96"
},
{
"key" : "ISOSpeedRatings",
"value" : "50"
},
{
"key" : "WhiteBalance",
"value" : "1"
} }
], ],
"resolution" : { "resolution" : {

View File

@@ -41,6 +41,10 @@
"key" : "LensModel", "key" : "LensModel",
"value" : "A1234" "value" : "A1234"
}, },
{
"key" : "LensSerialNumber",
"value" : "S/N:1234567"
},
{ {
"key" : "Longitude", "key" : "Longitude",
"value" : "10.9254" "value" : "10.9254"
@@ -52,6 +56,50 @@
{ {
"key" : "Model", "key" : "Model",
"value" : "KImageFormats" "value" : "KImageFormats"
},
{
"key" : "SerialNumber",
"value" : "S/N:7654321"
},
{
"key" : "Speed",
"value" : "13.2"
},
{
"key" : "DigitalZoomRatio",
"value" : "3.4"
},
{
"key" : "ExposureMode",
"value" : "2"
},
{
"key" : "ExposureProgram",
"value" : "6"
},
{
"key" : "ExposureTime",
"value" : "0.004"
},
{
"key" : "Flash",
"value" : "16"
},
{
"key" : "FNumber",
"value" : "1.6"
},
{
"key" : "FocalLength",
"value" : "5.96"
},
{
"key" : "ISOSpeedRatings",
"value" : "50"
},
{
"key" : "WhiteBalance",
"value" : "1"
} }
], ],
"resolution" : { "resolution" : {

View File

@@ -62,7 +62,7 @@ void setOptionalInfo(QImage &image, const QString &suffix)
// Set metadata // Set metadata
auto meta = obj.value("metadata").toArray(); auto meta = obj.value("metadata").toArray();
for (auto jv : meta) { for (auto &&jv : meta) {
auto obj = jv.toObject(); auto obj = jv.toObject();
auto key = obj.value("key").toString(); auto key = obj.value("key").toString();
auto val = obj.value("value").toString(); auto val = obj.value("value").toString();
@@ -106,7 +106,7 @@ bool checkOptionalInfo(QImage &image, const QString &suffix)
// Test metadata // Test metadata
auto meta = obj.value("metadata").toArray(); auto meta = obj.value("metadata").toArray();
for (auto jv : meta) { for (auto &&jv : meta) {
auto obj = jv.toObject(); auto obj = jv.toObject();
auto key = obj.value("key").toString(); auto key = obj.value("key").toString();
auto val = obj.value("value").toString(); auto val = obj.value("value").toString();

View File

@@ -36,17 +36,26 @@
#define TIFF_VAL_URES_CENTIMETER 3 #define TIFF_VAL_URES_CENTIMETER 3
// EXIF 3 specs // EXIF 3 specs
#define EXIF_EXPOSURETIME 0x829A
#define EXIF_FNUMBER 0x829D
#define EXIF_EXIFIFD 0x8769 #define EXIF_EXIFIFD 0x8769
#define EXIF_EXPOSUREPROGRAM 0x8822
#define EXIF_GPSIFD 0x8825 #define EXIF_GPSIFD 0x8825
#define EXIF_ISOSPEEDRATINGS 0x8827
#define EXIF_EXIFVERSION 0x9000 #define EXIF_EXIFVERSION 0x9000
#define EXIF_DATETIMEORIGINAL 0x9003 #define EXIF_DATETIMEORIGINAL 0x9003
#define EXIF_DATETIMEDIGITIZED 0x9004 #define EXIF_DATETIMEDIGITIZED 0x9004
#define EXIF_OFFSETTIME 0x9010 #define EXIF_OFFSETTIME 0x9010
#define EXIF_OFFSETTIMEORIGINAL 0x9011 #define EXIF_OFFSETTIMEORIGINAL 0x9011
#define EXIF_OFFSETTIMEDIGITIZED 0x9012 #define EXIF_OFFSETTIMEDIGITIZED 0x9012
#define EXIF_FLASH 0x9209
#define EXIF_FOCALLENGTH 0x920A
#define EXIF_COLORSPACE 0xA001 #define EXIF_COLORSPACE 0xA001
#define EXIF_PIXELXDIM 0xA002 #define EXIF_PIXELXDIM 0xA002
#define EXIF_PIXELYDIM 0xA003 #define EXIF_PIXELYDIM 0xA003
#define EXIF_EXPOSUREMODE 0xA402
#define EXIF_WHITEBALANCE 0xA403
#define EXIF_DIGITALZOOMRATIO 0xA404
#define EXIF_IMAGEUNIQUEID 0xA420 #define EXIF_IMAGEUNIQUEID 0xA420
#define EXIF_BODYSERIALNUMBER 0xA431 #define EXIF_BODYSERIALNUMBER 0xA431
#define EXIF_LENSMAKE 0xA433 #define EXIF_LENSMAKE 0xA433
@@ -64,6 +73,8 @@
#define GPS_LONGITUDE 4 #define GPS_LONGITUDE 4
#define GPS_ALTITUDEREF 5 #define GPS_ALTITUDEREF 5
#define GPS_ALTITUDE 6 #define GPS_ALTITUDE 6
#define GPS_SPEEDREF 12
#define GPS_SPEED 13
#define GPS_IMGDIRECTIONREF 16 #define GPS_IMGDIRECTIONREF 16
#define GPS_IMGDIRECTION 17 #define GPS_IMGDIRECTION 17
#define EXIF_TAG_VALUE(n, byteSize) (((n) << 6) | ((byteSize) & 0x3F)) #define EXIF_TAG_VALUE(n, byteSize) (((n) << 6) | ((byteSize) & 0x3F))
@@ -123,16 +134,25 @@ static const KnownTags staticTagTypes = {
TagInfo(TIFF_ARTIST, ExifTagType::Utf8), TagInfo(TIFF_ARTIST, ExifTagType::Utf8),
TagInfo(TIFF_DATETIME, ExifTagType::Ascii), TagInfo(TIFF_DATETIME, ExifTagType::Ascii),
TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8), TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8),
TagInfo(EXIF_EXPOSURETIME, ExifTagType::Rational),
TagInfo(EXIF_FNUMBER, ExifTagType::Rational),
TagInfo(EXIF_EXIFIFD, ExifTagType::Long), TagInfo(EXIF_EXIFIFD, ExifTagType::Long),
TagInfo(EXIF_EXPOSUREPROGRAM, ExifTagType::Short),
TagInfo(EXIF_GPSIFD, ExifTagType::Long), TagInfo(EXIF_GPSIFD, ExifTagType::Long),
TagInfo(EXIF_ISOSPEEDRATINGS, ExifTagType::Short),
TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii), TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii),
TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii), TagInfo(EXIF_DATETIMEDIGITIZED, ExifTagType::Ascii),
TagInfo(EXIF_OFFSETTIME, ExifTagType::Ascii), TagInfo(EXIF_OFFSETTIME, ExifTagType::Ascii),
TagInfo(EXIF_OFFSETTIMEORIGINAL, ExifTagType::Ascii), TagInfo(EXIF_OFFSETTIMEORIGINAL, ExifTagType::Ascii),
TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii), TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii),
TagInfo(EXIF_FLASH, ExifTagType::Short),
TagInfo(EXIF_FOCALLENGTH, ExifTagType::Rational),
TagInfo(EXIF_COLORSPACE, ExifTagType::Short), TagInfo(EXIF_COLORSPACE, ExifTagType::Short),
TagInfo(EXIF_PIXELXDIM, ExifTagType::Long), TagInfo(EXIF_PIXELXDIM, ExifTagType::Long),
TagInfo(EXIF_PIXELYDIM, ExifTagType::Long), TagInfo(EXIF_PIXELYDIM, ExifTagType::Long),
TagInfo(EXIF_EXPOSUREMODE, ExifTagType::Short),
TagInfo(EXIF_WHITEBALANCE, ExifTagType::Short),
TagInfo(EXIF_DIGITALZOOMRATIO, ExifTagType::Rational),
TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii), TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii),
TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii), TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii),
TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8), TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8),
@@ -155,6 +175,8 @@ static const KnownTags staticGpsTagTypes = {
TagInfo(GPS_LONGITUDE, ExifTagType::Rational), TagInfo(GPS_LONGITUDE, ExifTagType::Rational),
TagInfo(GPS_ALTITUDEREF, ExifTagType::Byte), TagInfo(GPS_ALTITUDEREF, ExifTagType::Byte),
TagInfo(GPS_ALTITUDE, ExifTagType::Rational), TagInfo(GPS_ALTITUDE, ExifTagType::Rational),
TagInfo(GPS_SPEEDREF, ExifTagType::Ascii),
TagInfo(GPS_SPEED, ExifTagType::Rational),
TagInfo(GPS_IMGDIRECTIONREF, ExifTagType::Ascii), TagInfo(GPS_IMGDIRECTIONREF, ExifTagType::Ascii),
TagInfo(GPS_IMGDIRECTION, ExifTagType::Rational) TagInfo(GPS_IMGDIRECTION, ExifTagType::Rational)
}; };
@@ -317,7 +339,7 @@ static void writeList(QDataStream &ds, const QVariant &value)
inline qint32 rationalPrecision(double v) inline qint32 rationalPrecision(double v)
{ {
v = qAbs(v); v = qAbs(v);
return 8 - qBound(0, v < 1 ? 8 : int(std::log10(v)), 8); return v < 1 ? 8 : 8 - qBound(0, int(std::log10(v)), 8);
} }
template<class T> template<class T>
@@ -884,7 +906,7 @@ QDateTime MicroExif::dateTime() const
auto ofTag = exifString(EXIF_OFFSETTIME); auto ofTag = exifString(EXIF_OFFSETTIME);
if (dt.isValid() && !ofTag.isEmpty()) if (dt.isValid() && !ofTag.isEmpty())
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60)); dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
return(dt); return dt;
} }
void MicroExif::setDateTime(const QDateTime &dt) void MicroExif::setDateTime(const QDateTime &dt)
@@ -904,7 +926,7 @@ QDateTime MicroExif::dateTimeOriginal() const
auto ofTag = exifString(EXIF_OFFSETTIMEORIGINAL); auto ofTag = exifString(EXIF_OFFSETTIMEORIGINAL);
if (dt.isValid() && !ofTag.isEmpty()) if (dt.isValid() && !ofTag.isEmpty())
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60)); dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
return(dt); return dt;
} }
void MicroExif::setDateTimeOriginal(const QDateTime &dt) void MicroExif::setDateTimeOriginal(const QDateTime &dt)
@@ -924,7 +946,7 @@ QDateTime MicroExif::dateTimeDigitized() const
auto ofTag = exifString(EXIF_OFFSETTIMEDIGITIZED); auto ofTag = exifString(EXIF_OFFSETTIMEDIGITIZED);
if (dt.isValid() && !ofTag.isEmpty()) if (dt.isValid() && !ofTag.isEmpty())
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60)); dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
return(dt); return dt;
} }
void MicroExif::setDateTimeDigitized(const QDateTime &dt) void MicroExif::setDateTimeDigitized(const QDateTime &dt)
@@ -966,6 +988,138 @@ void MicroExif::setUniqueId(const QUuid &uuid)
setExifString(EXIF_IMAGEUNIQUEID, uuid.toString(QUuid::WithoutBraces).replace(QStringLiteral("-"), QString())); setExifString(EXIF_IMAGEUNIQUEID, uuid.toString(QUuid::WithoutBraces).replace(QStringLiteral("-"), QString()));
} }
double MicroExif::digitalZoomRatio() const
{
if (!m_exifTags.contains(EXIF_DIGITALZOOMRATIO))
return qQNaN();
return m_exifTags.value(EXIF_DIGITALZOOMRATIO).toDouble();
}
void MicroExif::setDigitalZoomRatio(double zoom)
{
if (qIsNaN(zoom))
m_exifTags.remove(EXIF_DIGITALZOOMRATIO);
else
m_exifTags.insert(EXIF_DIGITALZOOMRATIO, zoom);
}
quint16 MicroExif::isoSpeedRatings() const
{
return quint16(m_exifTags.value(EXIF_ISOSPEEDRATINGS).toUInt());
}
void MicroExif::setIsoSpeedRatings(quint16 iso)
{
if (iso == 0)
m_exifTags.remove(EXIF_ISOSPEEDRATINGS);
else
m_exifTags.insert(EXIF_ISOSPEEDRATINGS, iso);
}
ExposureMode MicroExif::exposureMode() const
{
auto ok = false;
auto v = m_exifTags.value(EXIF_EXPOSUREMODE).toUInt(&ok);
return ok ? ExposureMode(v) : ExposureMode::NotSet;
}
void MicroExif::setExposureMode(const ExposureMode &em)
{
if (em == ExposureMode::NotSet)
m_exifTags.remove(EXIF_EXPOSUREMODE);
else
m_exifTags.insert(EXIF_EXPOSUREMODE, quint16(em));
}
ExposureProgram MicroExif::exposureProgram() const
{
auto ok = false;
auto v = m_exifTags.value(EXIF_EXPOSUREPROGRAM).toUInt(&ok);
return ok ? ExposureProgram(v) : ExposureProgram::NotSet;
}
void MicroExif::setExposureProgram(const ExposureProgram &ep)
{
if (ep == ExposureProgram::NotSet)
m_exifTags.remove(EXIF_EXPOSUREPROGRAM);
else
m_exifTags.insert(EXIF_EXPOSUREPROGRAM, quint16(ep));
}
double MicroExif::exposureTime() const
{
if (!m_exifTags.contains(EXIF_EXPOSURETIME))
return qQNaN();
return m_exifTags.value(EXIF_EXPOSURETIME).toDouble();
}
void MicroExif::setExposureTime(double et)
{
if (qIsNaN(et))
m_exifTags.remove(EXIF_EXPOSURETIME);
else
m_exifTags.insert(EXIF_EXPOSURETIME, et);
}
double MicroExif::fNumber() const
{
if (!m_exifTags.contains(EXIF_FNUMBER))
return qQNaN();
return m_exifTags.value(EXIF_FNUMBER).toDouble();
}
void MicroExif::setFNumber(double f)
{
if (qIsNaN(f))
m_exifTags.remove(EXIF_FNUMBER);
else
m_exifTags.insert(EXIF_FNUMBER, f);
}
double MicroExif::focalLength() const
{
if (!m_exifTags.contains(EXIF_FOCALLENGTH))
return qQNaN();
return m_exifTags.value(EXIF_FOCALLENGTH).toDouble();
}
void MicroExif::setFocalLength(double fl)
{
if (qIsNaN(fl))
m_exifTags.remove(EXIF_FOCALLENGTH);
else
m_exifTags.insert(EXIF_FOCALLENGTH, fl);
}
FlashFlags MicroExif::flash() const
{
return FlashFlags(m_exifTags.value(EXIF_FLASH).toUInt());
}
void MicroExif::setFlash(const FlashFlags &flash)
{
if (flash == Flash::NotSet)
m_exifTags.remove(EXIF_FLASH);
else
m_exifTags.insert(EXIF_FLASH, quint16(flash));
}
WhiteBalance MicroExif::whiteBalance() const
{
auto ok = false;
auto v = m_exifTags.value(EXIF_WHITEBALANCE).toUInt(&ok);
return ok ? WhiteBalance(v) : WhiteBalance::NotSet;
}
void MicroExif::setWhiteBalance(const WhiteBalance &wb)
{
if (wb == WhiteBalance::NotSet)
m_exifTags.remove(EXIF_WHITEBALANCE);
else
m_exifTags.insert(EXIF_WHITEBALANCE, quint16(wb));
}
double MicroExif::latitude() const double MicroExif::latitude() const
{ {
auto ref = gpsString(GPS_LATITUDEREF).toUpper(); auto ref = gpsString(GPS_LATITUDEREF).toUpper();
@@ -1045,6 +1199,30 @@ void MicroExif::setAltitude(double meters)
m_gpsTags.insert(GPS_ALTITUDE, meters); m_gpsTags.insert(GPS_ALTITUDE, meters);
} }
double MicroExif::imageSpeed() const
{
if (!m_gpsTags.contains(GPS_SPEED))
return qQNaN();
auto ref = gpsString(GPS_SPEEDREF).toUpper();
auto speed = m_gpsTags.value(GPS_SPEED).toDouble();
if (ref == QStringLiteral("M"))
speed *= 1.60934;
else if (ref == QStringLiteral("N"))
speed *= 1.852;
return speed;
}
void MicroExif::setImageSpeed(double kmh)
{
if (qIsNaN(kmh)) {
m_gpsTags.remove(GPS_SPEEDREF);
m_gpsTags.remove(GPS_SPEED);
return;
}
m_gpsTags.insert(GPS_SPEEDREF, QStringLiteral("K"));
m_gpsTags.insert(GPS_SPEED, kmh);
}
double MicroExif::imageDirection(bool *isMagnetic) const double MicroExif::imageDirection(bool *isMagnetic) const
{ {
auto tmp = false; auto tmp = false;
@@ -1191,6 +1369,58 @@ void MicroExif::updateImageMetadata(QImage &targetImage, bool replaceExisting) c
if (!qIsNaN(v)) if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_DIRECTION), QStringLiteral("%1").arg(v, 0, 'g', 9)); targetImage.setText(QStringLiteral(META_KEY_DIRECTION), QStringLiteral("%1").arg(v, 0, 'g', 9));
} }
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_SPEED)).isEmpty()) {
auto v = imageSpeed();
if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_SPEED), QStringLiteral("%1").arg(v, 0, 'g', 9));
}
// shot info
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_DIGITALZOOMRATIO)).isEmpty()) {
auto v = digitalZoomRatio();
if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_DIGITALZOOMRATIO), QStringLiteral("%1").arg(v, 0, 'g', 9));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSUREMODE)).isEmpty()) {
auto v = exposureMode();
if (v != ExposureMode::NotSet)
targetImage.setText(QStringLiteral(META_KEY_EXPOSUREMODE), QStringLiteral("%1").arg(quint16(v)));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSUREPROGRAM)).isEmpty()) {
auto v = exposureProgram();
if (v != ExposureProgram::NotSet)
targetImage.setText(QStringLiteral(META_KEY_EXPOSUREPROGRAM), QStringLiteral("%1").arg(quint16(v)));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSURETIME)).isEmpty()) {
auto v = exposureTime();
if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_EXPOSURETIME), QStringLiteral("%1").arg(v, 0, 'g', 9));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FLASH)).isEmpty()) {
auto v = flash();
if (v != Flash::NotSet)
targetImage.setText(QStringLiteral(META_KEY_FLASH), QStringLiteral("%1").arg(quint16(v)));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FNUMBER)).isEmpty()) {
auto v = fNumber();
if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_FNUMBER), QStringLiteral("%1").arg(v, 0, 'g', 9));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FOCALLENGTH)).isEmpty()) {
auto v = focalLength();
if (!qIsNaN(v))
targetImage.setText(QStringLiteral(META_KEY_FOCALLENGTH), QStringLiteral("%1").arg(v, 0, 'g', 9));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_ISOSPEEDRATINGS)).isEmpty()) {
auto v = isoSpeedRatings();
if (v != 0)
targetImage.setText(QStringLiteral(META_KEY_ISOSPEEDRATINGS), QStringLiteral("%1").arg(v));
}
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_WHITEBALANCE)).isEmpty()) {
auto v = whiteBalance();
if (v != WhiteBalance::NotSet)
targetImage.setText(QStringLiteral(META_KEY_WHITEBALANCE), QStringLiteral("%1").arg(quint16(v)));
}
} }
bool MicroExif::updateImageResolution(QImage &targetImage) bool MicroExif::updateImageResolution(QImage &targetImage)
@@ -1217,7 +1447,7 @@ MicroExif MicroExif::fromByteArray(const QByteArray &ba, bool searchHeader)
idx = std::min(idxLE, idxBE); idx = std::min(idxLE, idxBE);
else else
idx = idxLE > -1 ? idxLE : idxBE; idx = idxLE > -1 ? idxLE : idxBE;
if(idx > 0) if (idx > 0)
ba0 = ba0.mid(idx); ba0 = ba0.mid(idx);
} }
QBuffer buf; QBuffer buf;
@@ -1308,7 +1538,7 @@ MicroExif MicroExif::fromImage(const QImage &image)
dt = QDateTime::currentDateTime(); dt = QDateTime::currentDateTime();
exif.setDateTimeOriginal(dt); exif.setDateTimeOriginal(dt);
// GPS Info // GPS info
auto ok = false; auto ok = false;
auto alt = image.text(QStringLiteral(META_KEY_ALTITUDE)).toDouble(&ok); auto alt = image.text(QStringLiteral(META_KEY_ALTITUDE)).toDouble(&ok);
if (ok) if (ok)
@@ -1322,6 +1552,38 @@ MicroExif MicroExif::fromImage(const QImage &image)
auto dir = image.text(QStringLiteral(META_KEY_DIRECTION)).toDouble(&ok); auto dir = image.text(QStringLiteral(META_KEY_DIRECTION)).toDouble(&ok);
if (ok) if (ok)
exif.setImageDirection(dir); exif.setImageDirection(dir);
auto spd = image.text(QStringLiteral(META_KEY_SPEED)).toDouble(&ok);
if (ok)
exif.setImageSpeed(spd);
// EXIF shot info
auto zoom = image.text(QStringLiteral(META_KEY_DIGITALZOOMRATIO)).toDouble(&ok);
if (ok)
exif.setDigitalZoomRatio(zoom);
auto expm = image.text(QStringLiteral(META_KEY_EXPOSUREMODE)).toUShort(&ok);
if (ok)
exif.setExposureMode(ExposureMode(expm));
auto expp = image.text(QStringLiteral(META_KEY_EXPOSUREPROGRAM)).toUShort(&ok);
if (ok)
exif.setExposureProgram(ExposureProgram(expp));
auto expt = image.text(QStringLiteral(META_KEY_EXPOSURETIME)).toDouble(&ok);
if (ok)
exif.setExposureTime(expt);
auto flsh = image.text(QStringLiteral(META_KEY_FLASH)).toUShort(&ok);
if (ok)
exif.setFlash(FlashFlags(flsh));
auto fnum = image.text(QStringLiteral(META_KEY_FNUMBER)).toDouble(&ok);
if (ok)
exif.setFNumber(fnum);
auto flen = image.text(QStringLiteral(META_KEY_FOCALLENGTH)).toDouble(&ok);
if (ok)
exif.setFocalLength(flen);
auto isos = image.text(QStringLiteral(META_KEY_ISOSPEEDRATINGS)).toUShort(&ok);
if (ok)
exif.setIsoSpeedRatings(isos);
auto whtb = image.text(QStringLiteral(META_KEY_WHITEBALANCE)).toUShort(&ok);
if (ok)
exif.setWhiteBalance(WhiteBalance(whtb));
return exif; return exif;
} }

View File

@@ -24,6 +24,85 @@
#define EXIF_DEFAULT_BYTEORDER QDataStream::BigEndian #define EXIF_DEFAULT_BYTEORDER QDataStream::BigEndian
#endif #endif
/*!
* \brief The Flash enum
*/
enum class Flash : quint16 {
NotSet = 0,
// Values for bit 0 indicating whether the flash fired.
// 0b = Flash did not fire.
// 1b = Flash fired.
Fired = 1,
// Values for bits 1 and 2 indicating the status of returned light.
// 00b = No strobe return detection function
// 01b = reserved
// 10b = Strobe return light not detected.
// 11b = Strobe return light detected.
ReturnLightNotDetected = 2 << 1,
ReturnLightDetected = 3 << 1,
// Values for bits 3 and 4 indicating the camera's flash mode.
// 00b = unknown
// 01b = Compulsory flash firing
// 10b = Compulsory flash suppression
// 11b = Auto mode
CompulsoryFiring = 1 << 3,
CompulsorySuppression = 2 << 3,
AutoMode = 3 << 3,
// Values for bit 5 indicating the presence of a flash function.
// 0b = Flash function present
// 1b = No flash function
FlashNotAvailable = 1 << 5,
// Values for bit 6 indicating the camera's red-eye mode.
// 0b = No red-eye reduction mode or unknown
// 1b = Red-eye reduction supported
RedEyeReductionSupported = 1 << 6,
};
Q_DECLARE_FLAGS(FlashFlags, Flash)
Q_DECLARE_OPERATORS_FOR_FLAGS(FlashFlags)
/*!
* \brief The ExposureMode enum
*/
enum class ExposureMode : quint16 {
Auto,
Manual,
AutoBracket,
NotSet = 65535
};
/*!
* \brief The ExposureProgram enum
*/
enum class ExposureProgram : quint16 {
NotDefined,
Manual,
Normal,
AperturePriority,
ShutterPriority,
Creative,
Action,
PortraitMode,
LandscapeMode,
NotSet = 65535
};
/*!
* \brief The WhiteBalance enum
*/
enum class WhiteBalance : quint16 {
Auto,
Manual,
NotSet = 65535
};
/*! /*!
* \brief The MicroExif class * \brief The MicroExif class
* Class to extract / write minimal EXIF data (e.g. resolution, rotation, * Class to extract / write minimal EXIF data (e.g. resolution, rotation,
@@ -236,6 +315,69 @@ public:
QUuid uniqueId() const; QUuid uniqueId() const;
void setUniqueId(const QUuid &uuid); void setUniqueId(const QUuid &uuid);
/*!
* \brief digitalZoomRatio
* \return The digital zoom ratio when the image was shot or NaN if not set.
*/
double digitalZoomRatio() const;
void setDigitalZoomRatio(double zoom);
/*!
* \brief exposureMode
* \return The exposure mode set when the image was shot. In auto-bracketing mode, the camera shoots a series of frames of the same scene at different exposure settings.
*/
ExposureMode exposureMode() const;
void setExposureMode(const ExposureMode& em);
/*!
* \brief exposureProgram
* \return The class of the program used by the camera to set exposure when the picture is taken.
*/
ExposureProgram exposureProgram() const;
void setExposureProgram(const ExposureProgram& ep);
/*!
* \brief exposureTime
* \return Exposure time, given in seconds (sec) or NaN if not set.
*/
double exposureTime() const;
void setExposureTime(double et);
/*!
* \brief fNumber
* \return The F number or NaN if not set.
*/
double fNumber() const;
void setFNumber(double f);
/*!
* \brief focalLength
* \return The actual focal length of the lens, in mm.
*/
double focalLength() const;
void setFocalLength(double fl);
/*!
* \brief flash
* \return The status of flash when the image was shot.
*/
FlashFlags flash() const;
void setFlash(const FlashFlags& flash);
/*!
* \brief isoSpeedRatings
* \return The sensitivity of the camera or input device when the image was shot.
*/
quint16 isoSpeedRatings() const;
void setIsoSpeedRatings(quint16 iso);
/*!
* \brief whiteBalance
* \return The white balance mode set when the image was shot.
*/
WhiteBalance whiteBalance() const;
void setWhiteBalance(const WhiteBalance& wb);
/*! /*!
* \brief latitude * \brief latitude
* \return Floating-point number indicating the latitude in degrees north of the equator (e.g. 27.717) or NaN if not set. * \return Floating-point number indicating the latitude in degrees north of the equator (e.g. 27.717) or NaN if not set.
@@ -258,6 +400,13 @@ public:
double altitude() const; double altitude() const;
void setAltitude(double meters); void setAltitude(double meters);
/*!
* \brief imageSpeed
* \return The speed in Km/h or NaN if not set.
*/
double imageSpeed() const;
void setImageSpeed(double kmh);
/*! /*!
* \brief imageDirection * \brief imageDirection
* \param isMagnetic Set to true if the direction is relative to magnetic north, false if it is relative to true north. Leave nullptr if is not of interest. * \param isMagnetic Set to true if the direction is relative to magnetic north, false if it is relative to true north. Leave nullptr if is not of interest.

View File

@@ -34,10 +34,22 @@
#define META_KEY_MODIFICATIONDATE "ModificationDate" #define META_KEY_MODIFICATIONDATE "ModificationDate"
#define META_KEY_OWNER "Owner" #define META_KEY_OWNER "Owner"
#define META_KEY_SOFTWARE "Software" #define META_KEY_SOFTWARE "Software"
#define META_KEY_SPEED "Speed"
#define META_KEY_TITLE "Title" #define META_KEY_TITLE "Title"
#define META_KEY_XML_GIMP "XML:org.gimp.xml" #define META_KEY_XML_GIMP "XML:org.gimp.xml"
#define META_KEY_XMP_ADOBE "XML:com.adobe.xmp" #define META_KEY_XMP_ADOBE "XML:com.adobe.xmp"
// Shot info metadata keys
#define META_KEY_DIGITALZOOMRATIO "DigitalZoomRatio"
#define META_KEY_EXPOSUREMODE "ExposureMode"
#define META_KEY_EXPOSUREPROGRAM "ExposureProgram"
#define META_KEY_EXPOSURETIME "ExposureTime"
#define META_KEY_FLASH "Flash"
#define META_KEY_FNUMBER "FNumber"
#define META_KEY_FOCALLENGTH "FocalLength"
#define META_KEY_ISOSPEEDRATINGS "ISOSpeedRatings"
#define META_KEY_WHITEBALANCE "WhiteBalance"
// Camera info metadata keys // Camera info metadata keys
#define META_KEY_MANUFACTURER "Manufacturer" #define META_KEY_MANUFACTURER "Manufacturer"
#define META_KEY_MODEL "Model" #define META_KEY_MODEL "Model"