From 7983d3a8b32b253443ee9dc9099165c6de042818 Mon Sep 17 00:00:00 2001 From: birkeh Date: Tue, 21 Jan 2020 23:36:07 +0100 Subject: [PATCH] initial commit --- cexif.cpp | 1169 ++++++++++++++++++++++++++++++++++++++++++++++ cexif.h | 870 ++++++++++++++++++++++++++++++++++ cfilebrowser.cpp | 14 + cfilebrowser.h | 22 + cfilebrowser.ui | 21 + cfileviewer.cpp | 14 + cfileviewer.h | 22 + cfileviewer.ui | 21 + cimage.cpp | 428 +++++++++++++++++ cimage.h | 313 +++++++++++++ 10 files changed, 2894 insertions(+) create mode 100644 cexif.cpp create mode 100644 cexif.h create mode 100644 cfilebrowser.cpp create mode 100644 cfilebrowser.h create mode 100644 cfilebrowser.ui create mode 100644 cfileviewer.cpp create mode 100644 cfileviewer.h create mode 100644 cfileviewer.ui create mode 100644 cimage.cpp create mode 100644 cimage.h diff --git a/cexif.cpp b/cexif.cpp new file mode 100644 index 0000000..c11a812 --- /dev/null +++ b/cexif.cpp @@ -0,0 +1,1169 @@ +/*! + \file cexif.cpp + +*/ + +#include "cexif.h" + +#include "common.h" + +#include +#include +#include +#include + +#include +#include + +#include + + +cEXIF::cEXIF(cEXIFTagList* lpEXIFTagList, cEXIFCompressionList* lpEXIFCompressionList, cEXIFLightSourceList* lpEXIFLightSourceList, cEXIFFlashList* lpEXIFFlashList, cIPTCTagList* lpIPTCTagList, cXMPTagList* lpXMPTagList) : + m_szMimeType(""), + m_iWidth(0), + m_iHeight(0), + m_szFileName(""), + m_lpEXIFTagList(lpEXIFTagList), + m_lpEXIFCompressionList(lpEXIFCompressionList), + m_lpEXIFLightSourceList(lpEXIFLightSourceList), + m_lpEXIFFlashList(lpEXIFFlashList), + m_lpIPTCTagList(lpIPTCTagList), + m_lpXMPTagList(lpXMPTagList) +{ +} + +bool cEXIF::fromFile(const QString& szFileName) +{ + if(!QFile::exists(szFileName)) + return(false); + + m_exifValueList.clear(); + m_previewList.clear(); + + m_szFileName = ""; + + try + { + Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open(szFileName.toLocal8Bit().toStdString()); + if(!image.get()) + return(false); + + image->readMetadata(); + + Exiv2::ExifData& exifData = image->exifData(); + Exiv2::IptcData& iptcData = image->iptcData(); + Exiv2::XmpData& xmpData = image->xmpData(); + + m_szMimeType = QString::fromStdString(image->mimeType()); + m_iWidth = image->pixelWidth(); + m_iHeight = image->pixelHeight(); + + m_szFileName = szFileName; + + if(!iptcData.empty()) + { + Exiv2::IptcData::const_iterator end = iptcData.end(); + + for(Exiv2::IptcData::const_iterator i = iptcData.begin(); i != end; ++i) + { + cIPTCTag* lpTag = m_lpIPTCTagList->find(i->tag()); + + if(lpTag) + { + cIPTCValue* lpValue = m_iptcValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + + if(!exifData.empty()) + { + Exiv2::ExifData::const_iterator end = exifData.end(); + for(Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) + { + cEXIFTag* lpTag = m_lpEXIFTagList->find(QString::fromUtf8(i->key().c_str())); + + if(lpTag) + { + cEXIFValue* lpValue = m_exifValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + + if(!xmpData.empty()) + { + Exiv2::XmpData::const_iterator end = xmpData.end(); + + for(Exiv2::XmpData::const_iterator i = xmpData.begin(); i != end; ++i) + { + cXMPTag* lpTag = m_lpXMPTagList->find(QString::fromStdString(i->key())); + + if(lpTag) + { + cXMPValue* lpValue = m_xmpValueList.add(lpTag); + if(lpValue) + lpValue->setValue(QString::fromStdString(i->value().toString()), i->typeId(), i->count()); + } + } + + Exiv2::PreviewManager previewManager(*image); + Exiv2::PreviewPropertiesList previewPropertiesList = previewManager.getPreviewProperties(); + + for(Exiv2::PreviewPropertiesList::const_iterator i = previewPropertiesList.begin();i != previewPropertiesList.end();i++) + { + Exiv2::PreviewImage previewImage = previewManager.getPreviewImage(*i); + QImage image; + image.loadFromData(static_cast(previewImage.pData()), static_cast(previewImage.size())); + + m_previewList.append(image); + } + } + } + catch (Exiv2::AnyError& e) + { + qDebug() << "Caught Exiv2 exception '" << e.what(); + return(false); + } + + if(!m_previewList.count()) + { + QImage image; + if(image.load(szFileName)) + { + QTransform rotation; + int angle = 0; + + switch(imageOrientation()) + { + case 8: + angle = 270; + break; + case 3: + angle = 180; + break; + case 6: + angle = 90; + break; + } + + if(angle != 0) + { + rotation.rotate(angle); + image = image.transformed(rotation); + } + + m_thumbnail = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + } + else + { + qint32 index = -1; + qint32 widthDiff = std::numeric_limits::max(); + qint32 heightDiff = std::numeric_limits::max(); + + for(qint32 i = 0;i < m_previewList.count();i++) + { + qint32 wd = m_previewList[i].width() - THUMBNAIL_WIDTH; + qint32 hd = m_previewList[i].height() - THUMBNAIL_HEIGHT; + + if(wd >= 0 && hd >= 0) + { + if(wd <= widthDiff && hd <= heightDiff) + { + index = i; + widthDiff = wd; + heightDiff = hd; + } + } + } + + if(index == -1) + { + for(qint32 i = 0;i < m_previewList.count();i++) + { + qint32 wd = m_previewList[i].width() - THUMBNAIL_WIDTH; + qint32 hd = m_previewList[i].height() - THUMBNAIL_HEIGHT; + + if(abs(wd) <= widthDiff && abs(hd) <= heightDiff) + { + index = i; + widthDiff = abs(wd); + heightDiff = abs(hd); + } + } + } + + if(index != -1) + { + QTransform rotation; + int angle = 0; + + switch(imageOrientation()) + { + case 8: + angle = 270; + break; + case 3: + angle = 180; + break; + case 6: + angle = 90; + break; + } + + m_thumbnail = m_previewList[index].scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation); + + if(angle != 0) + { + rotation.rotate(angle); + m_thumbnail = m_thumbnail.transformed(rotation); + } + } + } + + return(true); +} + +QString cEXIF::mimeType() +{ + return(m_szMimeType); +} + +qint32 cEXIF::imageWidth() +{ + if(m_iWidth) + return(m_iWidth); + + qint32 iWidth = getEXIFTag(0x0100, "Image").value(); + + if(iWidth) + return(iWidth); + + return(getXMPTag("Xmp.video.Width").value()); +} + +qint32 cEXIF::imageHeight() +{ + if(m_iHeight) + return(m_iHeight); + + qint32 iHeight = getEXIFTag(0x0101, "Image").value(); + + if(iHeight) + return(iHeight); + + return(getXMPTag("Xmp.video.Height").value()); +} + +qint16 cEXIF::imageOrientation() +{ + return(getEXIFTag(0x0112, "Image").value()); +} + +QString cEXIF::cameraMake() +{ + QString szCameraMake = getEXIFTag(0x010f, "Image").toString(); + + if(!szCameraMake.isEmpty()) + return(szCameraMake); + + return(getXMPTag("Xmp.video.MajorBrand").toString()); +} + +QString cEXIF::cameraModel() +{ + QString szCameraModel = getEXIFTag(0x0110, "Image").toString(); + + if(!szCameraModel.isEmpty()) + return(szCameraModel); + + return(getXMPTag("Xmp.video.MajorBrand").toString()); +} + +QDateTime cEXIF::dateTime() +{ + QDateTime dateTime = QDateTime::fromString(getEXIFTag(0x9004, "Image").value(), "yyyy:MM:dd hh:mm:ss"); + if(dateTime.isValid()) + return(dateTime); + + dateTime = QDateTime::fromString(getXMPTag("Xmp.video.DateTimeDigitized").toString(), "yyyy-MM-ddThh:mm:ssZ"); + if(dateTime.isValid()) + return(dateTime); + + dateTime = QDateTime::fromString(getEXIFTag(0x0132, "Image").value(), "yyyy:MM:dd hh:mm:ss"); + if(dateTime.isValid()) + return(dateTime); + + QFileInfo info(m_szFileName); + return(info.lastModified()); +} + +QString cEXIF::fNumber() +{ + return(getEXIFTag(0x829d, "Photo").value()); +} + +qint32 cEXIF::iso() +{ + return(getEXIFTag(0x8827, "Photo").value()); +} + +QString cEXIF::flash() +{ + return(m_lpEXIFFlashList->flash(getEXIFTag(0x9209, "Photo").value())); +} + +qint32 cEXIF::flashID() +{ + return(getEXIFTag(0x9209, "Photo").value()); +} + +qreal cEXIF::focalLength() +{ + return(getEXIFTag(0x920a, "Photo").value()); +} + +QString cEXIF::lensMake() +{ + return(getEXIFTag(0xa433, "Photo").value()); +} + +QString cEXIF::lensModel() +{ + return(getEXIFTag(0xa434, "Photo").value()); +} + +QString cEXIF::exposureTime() +{ + qreal value = getEXIFTag(0x829a, "Photo").value(); + + if(value == 0.0) + return("error"); + + if(value >= 1.0) + return(QString("%1 sec").arg(value)); + + return(QString("1/%1").arg(1/value)); +} + +qint32 cEXIF::exposureBias() +{ + return(getEXIFTag(0x9204, "Photo").value()); +} + +QString cEXIF::exifVersion() +{ + return(getEXIFTag(0x9000, "Photo").value()); +} + +QDateTime cEXIF::dateTimeOriginal() +{ + return(QDateTime::fromString(getEXIFTag(0x9003, "Photo").value(), "yyyy:MM:dd hh:mm:ss")); +} + +QDateTime cEXIF::dateTimeDigitized() +{ + return(QDateTime::fromString(getEXIFTag(0x9004, "Photo").value(), "yyyy:MM:dd hh:mm:ss")); +} + +qint32 cEXIF::whiteBalance() +{ + return(getEXIFTag(0xa403, "Photo").value()); +} + +qreal cEXIF::focalLength35() +{ + return(getEXIFTag(0xa405, "Photo").value()); +} + +QString cEXIF::gps() +{ + QList nList = getTagList(0x0002, "GPSInfo"); + QList eList = getTagList(0x0004, "GPSInfo"); + + if(nList.count() != 3) + return(""); + if(eList.count() != 3) + return(""); + + QString szGPS = QString("%1 %2° %3' %4\" %5 %6° %7' %8\"").arg(getEXIFTag(0x0001, "GPSInfo").value()).arg(nList[0].value()).arg(nList[1].value()).arg(nList[2].value()).arg(getEXIFTag(0x0001, "GPSInfo").value()).arg(eList[0].value()).arg(eList[1].value()).arg(eList[2].value()); + return(szGPS); +} + +QString cEXIF::duration() +{ + return(getXMPTag("Xmp.video.Duration").toString()); +} + +QString cEXIF::fileName() +{ + return(m_szFileName); +} + +QList cEXIF::previewList() +{ + return(m_previewList); +} + +QImage cEXIF::thumbnail() +{ + return(m_thumbnail); +} + +QVariant cEXIF::getEXIFTag(qint32 iTAGID, const QString& szIFD) +{ + cEXIFTag* lpTag = m_lpEXIFTagList->find(iTAGID, szIFD); + + if(!lpTag) + return(QVariant()); + + cEXIFValue* lpValue = m_exifValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QVariant cEXIF::getIPTCTag(qint32 iTAGID) +{ + cIPTCTag* lpTag = m_lpIPTCTagList->find(iTAGID); + + if(!lpTag) + return(QVariant()); + + cIPTCValue* lpValue = m_iptcValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QVariant cEXIF::getXMPTag(const QString& szTAGName) +{ + cXMPTag* lpTag = m_lpXMPTagList->find(szTAGName); + + if(!lpTag) + return(QVariant()); + + cXMPValue* lpValue = m_xmpValueList.find(lpTag); + + if(!lpValue) + return(QVariant()); + + return(lpValue->value()); +} + +QList cEXIF::getTagList(qint32 iTAGID, const QString& szIFD) +{ + cEXIFTag* lpTag = m_lpEXIFTagList->find(iTAGID, szIFD); + + if(!lpTag) + return(QList()); + + cEXIFValue* lpValue = m_exifValueList.find(lpTag); + + if(!lpValue) + return(QList()); + + return(lpValue->valueList()); +} + +bool cEXIF::copyTo(const QString& fileName) +{ + try + { + Exiv2::Image::UniquePtr image = Exiv2::ImageFactory::open(fileName.toLocal8Bit().toStdString()); + if(!image.get()) + return(false); + + image->readMetadata(); + + Exiv2::ExifData& exifData = image->exifData(); + + for(int x = 0;x < m_exifValueList.count();x++) + { + cEXIFValue* lpEXIFValue = m_exifValueList[x]; + +// if(!lpEXIFValue->exifTag()->m_szKey.startsWith("Exif.Image")) + exifData[lpEXIFValue->exifTag()->m_szKey.toUtf8().constData()] = lpEXIFValue->value().toString().toUtf8().constData(); + } + + image->setExifData(exifData); + image->writeMetadata(); + } + catch (Exiv2::AnyError& e) + { + qDebug() << "Caught Exiv2 exception '" << e.what(); + return(false); + } + + return(true); +} + +cEXIFCompression::cEXIFCompression(const qint32& iID, const QString& szCompression) : + m_iID(iID), + m_szCompression(szCompression) +{ +} + +cEXIFCompressionList::cEXIFCompressionList() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName("localhost"); + db.setDatabaseName("pictureConvert.db"); + + if(!db.open()) + return; + + QSqlQuery query; + + query.prepare("SELECT id, compression FROM exifCompression;"); + if(!query.exec()) + { + db.close(); + return; + } + + while(query.next()) + add(query.value("id").toInt(), query.value("compression").toString()); + + db.close(); +} + +cEXIFCompression* cEXIFCompressionList::add(const qint32& iID, const QString& szCompression) +{ + cEXIFCompression* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFCompression(iID, szCompression); + append(lpNew); + return(lpNew); +} + +cEXIFCompression* cEXIFCompressionList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFCompression* lpCompression= at(x); + + if(lpCompression->m_iID == iID) + return(lpCompression); + } + return(nullptr); +} + +QString cEXIFCompressionList::compression(const qint32& iID) +{ + cEXIFCompression* lpCompression = find(iID); + if(!lpCompression) + return(QObject::tr("unknown")); + return(lpCompression->m_szCompression); +} + +cEXIFLightSource::cEXIFLightSource(const qint32& iID, const QString& szLightSource) : + m_iID(iID), + m_szLightSource(szLightSource) +{ +} + +cEXIFLightSourceList::cEXIFLightSourceList() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName("localhost"); + db.setDatabaseName("pictureConvert.db"); + + if(!db.open()) + return; + + QSqlQuery query; + + query.prepare("SELECT id, lightsource FROM exifLightSource;"); + if(!query.exec()) + { + db.close(); + return; + } + + while(query.next()) + add(query.value("id").toInt(), query.value("lightsource").toString()); + + db.close(); +} + +cEXIFLightSource* cEXIFLightSourceList::add(const qint32& iID, const QString& szLightSource) +{ + cEXIFLightSource* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFLightSource(iID, szLightSource); + append(lpNew); + return(lpNew); +} + +cEXIFLightSource* cEXIFLightSourceList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFLightSource* lpLightSource = at(x); + + if(lpLightSource->m_iID == iID) + return(lpLightSource); + } + return(nullptr); +} + +QString cEXIFLightSourceList::lightSource(const qint32& iID) +{ + cEXIFLightSource* lpLightSource = find(iID); + if(!lpLightSource) + return(QObject::tr("unknown")); + return(lpLightSource->m_szLightSource); +} + +cEXIFFlash::cEXIFFlash(const qint32& iID, const QString& szFlash) : + m_iID(iID), + m_szFlash(szFlash) +{ +} + +cEXIFFlashList::cEXIFFlashList() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName("localhost"); + db.setDatabaseName("pictureConvert.db"); + + if(!db.open()) + return; + + QSqlQuery query; + + query.prepare("SELECT id, lightsource FROM exifLightSource;"); + if(!query.exec()) + { + db.close(); + return; + } + + while(query.next()) + add(query.value("id").toInt(), query.value("lightsource").toString()); + + db.close(); +} + +cEXIFFlash* cEXIFFlashList::add(const qint32& iID, const QString& szFlash) +{ + cEXIFFlash* lpNew = find(iID); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFFlash(iID, szFlash); + append(lpNew); + return(lpNew); +} + +cEXIFFlash* cEXIFFlashList::find(const qint32& iID) +{ + for(int x = 0;x < count();x++) + { + cEXIFFlash* lpFlash = at(x); + + if(lpFlash->m_iID == iID) + return(lpFlash); + } + return(nullptr); +} + +QString cEXIFFlashList::flash(const qint32& iID) +{ + cEXIFFlash* lpFlash = find(iID); + if(!lpFlash) + return(QObject::tr("unknown")); + return(lpFlash->m_szFlash); +} + +cEXIFTag::cEXIFTag(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription) : + m_iTAGID(iTAGID), + m_szTAGName(szTAGName), + m_szIFD(szIFD), + m_szKey(szKey), + m_szType(szType), + m_szDescription(szDescription) +{ +} + +cEXIFTagList::cEXIFTagList() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName("localhost"); + db.setDatabaseName("pictureConvert.db"); + + if(!db.open()) + return; + + QSqlQuery query; + + query.prepare("SELECT tag, ifd, key, type, description FROM exifTags;"); + if(!query.exec()) + { + db.close(); + return; + } + + while(query.next()) + add(query.value("tag").toInt(), query.value("key").toString(), query.value("ifd").toString(), query.value("key").toString(), query.value("type").toString(), query.value("description").toString()); + + db.close(); +} + +cEXIFTag* cEXIFTagList::add(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription) +{ + cEXIFTag* lpNew = find(iTAGID, szIFD); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFTag(iTAGID, szTAGName, szIFD, szKey, szType, szDescription); + append(lpNew); + return(lpNew); +} + +cEXIFTag* cEXIFTagList::find(const QString& szKey) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_szKey == szKey) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFTagList::find(const qint32& iTAGID, const QString& szIFD) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_iTAGID == iTAGID && lpTag->m_szIFD== szIFD) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFTagList::find(const QString& szTAG, const QString& szIFD) +{ + for(int x = 0;x < count();x++) + { + cEXIFTag* lpTag = at(x); + + if(lpTag->m_szTAGName == szTAG && lpTag->m_szIFD== szIFD) + return(lpTag); + } + return(nullptr); +} + +cEXIFTag* cEXIFValue::exifTag() +{ + return(m_lpEXIFTag); +} + +cEXIFValue::cEXIFValue(cEXIFTag* lpEXIFTag) : + m_lpEXIFTag(lpEXIFTag) +{ +} + +QVariant cEXIFValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cEXIFValue::setValue(const QString& szValue, qint32 iTypeId, qint32 iCount) +{ + if(iCount == 1 || iTypeId == 2 || iTypeId == 7) + { + m_valueList.append(convertValue(szValue, iTypeId)); + return; + } + + QStringList valueList = szValue.split(" "); + + if(valueList.count() != iCount) + return; + + for(int x = 0;x < iCount;x++) + m_valueList.append(convertValue(valueList[x], iTypeId)); +} + +QVariant cEXIFValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cEXIFValue::valueList() +{ + return(m_valueList); +} + +cEXIFValueList::cEXIFValueList() +{ +} + +cEXIFValue* cEXIFValueList::add(cEXIFTag* lpEXIFTag) +{ + cEXIFValue* lpNew = find(lpEXIFTag); + + if(lpNew) + return(nullptr); + + lpNew = new cEXIFValue(lpEXIFTag); + + append(lpNew); + return(lpNew); +} + +cEXIFValue* cEXIFValueList::find(cEXIFTag* lpEXIFTag) +{ + for(int x = 0;x < count();x++) + { + cEXIFValue* lpValue = at(x); + if(lpValue->exifTag() == lpEXIFTag) + return(lpValue); + } + return(nullptr); +} + +cIPTCTag::cIPTCTag(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) : + m_iTAGID(iTAGID), + m_szTAGName(szTAGName), + m_iTypeID(iTypeID), + m_szDescription(szDescription) +{ +} + +cIPTCTagList::cIPTCTagList() +{ +// add(0x000b, QObject::tr("ProcessingSoftware"), 1, 2, QObject::tr("The name and version of the software used to post-process the picture.")); +} + +cIPTCTag* cIPTCTagList::add(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) +{ + cIPTCTag* lpNew = find(iTAGID); + + if(lpNew) + return(nullptr); + + lpNew = new cIPTCTag(iTAGID, szTAGName, iTypeID, szDescription); + append(lpNew); + return(lpNew); +} + +cIPTCTag* cIPTCTagList::find(const qint32& iTAGID) +{ + for(int x = 0;x < count();x++) + { + cIPTCTag* lpTag = at(x); + + if(lpTag->m_iTAGID == iTAGID) + return(lpTag); + } + return(nullptr); +} + +cIPTCTag* cIPTCValue::iptcTag() +{ + return(m_lpIPTCTag); +} + +cIPTCValue::cIPTCValue(cIPTCTag* lpIPTCTag) : + m_lpIPTCTag(lpIPTCTag) +{ +} + +QVariant cIPTCValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cIPTCValue::setValue(const QString& szValue, qint32 iTypeId, qint32 iCount) +{ + if(iCount == 1 || iTypeId == 2 || iTypeId == 7) + { + m_valueList.append(convertValue(szValue, iTypeId)); + return; + } + + QStringList valueList = szValue.split(" "); + + if(valueList.count() != iCount) + return; + + for(int x = 0;x < iCount;x++) + m_valueList.append(convertValue(valueList[x], iTypeId)); +} + +QVariant cIPTCValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cIPTCValue::valueList() +{ + return(m_valueList); +} + +cIPTCValueList::cIPTCValueList() +{ +} + +cIPTCValue* cIPTCValueList::add(cIPTCTag* lpIPTCTag) +{ + cIPTCValue* lpNew = find(lpIPTCTag); + + if(lpNew) + return(nullptr); + + lpNew = new cIPTCValue(lpIPTCTag); + + append(lpNew); + return(lpNew); +} + +cIPTCValue* cIPTCValueList::find(cIPTCTag* lpIPTCTag) +{ + for(int x = 0;x < count();x++) + { + cIPTCValue* lpValue = at(x); + if(lpValue->iptcTag() == lpIPTCTag) + return(lpValue); + } + return(nullptr); +} + +cXMPTag::cXMPTag(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) : + m_szTAGName(szTAGName), + m_iTypeID(iTypeID), + m_szDescription(szDescription) +{ +} + +cXMPTagList::cXMPTagList() +{ + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName("localhost"); + db.setDatabaseName("pictureConvert.db"); + + if(!db.open()) + return; + + QSqlQuery query; + + query.prepare("SELECT tag, type, description FROM xmpTags;"); + if(!query.exec()) + { + db.close(); + return; + } + + while(query.next()) + add(query.value("tag").toString(), query.value("type").toInt(), query.value("description").toString()); + + db.close(); +} + +cXMPTag* cXMPTagList::add(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription) +{ + cXMPTag* lpNew = find(szTAGName); + + if(lpNew) + return(nullptr); + + lpNew = new cXMPTag(szTAGName, iTypeID, szDescription); + append(lpNew); + return(lpNew); +} + +cXMPTag* cXMPTagList::find(const QString& szTAGName) +{ + for(int x = 0;x < count();x++) + { + cXMPTag* lpTag = at(x); + + if(lpTag->m_szTAGName == szTAGName) + return(lpTag); + } + return(nullptr); +} + +cXMPTag* cXMPValue::xmpTag() +{ + return(m_lpXMPTag); +} + +cXMPValue::cXMPValue(cXMPTag* lpXMPTag) : + m_lpXMPTag(lpXMPTag) +{ +} + +QVariant cXMPValue::convertValue(const QString& szValue, qint32 iTypeId) +{ + QVariant variant; + + switch(iTypeId) + { + case 1: //byte + case 3: //short + case 4: //long + case 8: //signed short + variant = QVariant::fromValue(szValue.toLong()); + break; + case 5: //rational + case 10: //signed rational + case 11: //float + if(szValue.contains("/")) + { + QStringList tmp = szValue.split("/"); + variant = QVariant::fromValue(tmp[0].toDouble()/tmp[1].toDouble()); + } + else + variant = QVariant::fromValue(szValue.toDouble()); + break; + default: + variant = szValue; + } + return(variant); +} + +void cXMPValue::setValue(const QString& szValue, qint32 iTypeId, qint32 /*iCount*/) +{ + m_valueList.append(convertValue(szValue, iTypeId)); +} + +QVariant cXMPValue::value() +{ + if(m_valueList.count()) + return(m_valueList[0]); + + return(QVariant()); +} + +QList cXMPValue::valueList() +{ + return(m_valueList); +} + +cXMPValueList::cXMPValueList() +{ +} + +cXMPValue* cXMPValueList::add(cXMPTag* lpXMPTag) +{ + cXMPValue* lpNew = find(lpXMPTag); + + if(lpNew) + return(nullptr); + + lpNew = new cXMPValue(lpXMPTag); + + append(lpNew); + return(lpNew); +} + +cXMPValue* cXMPValueList::find(cXMPTag* lpXMPTag) +{ + for(int x = 0;x < count();x++) + { + cXMPValue* lpValue = at(x); + if(lpValue->xmpTag() == lpXMPTag) + return(lpValue); + } + return(nullptr); +} diff --git a/cexif.h b/cexif.h new file mode 100644 index 0000000..11a0335 --- /dev/null +++ b/cexif.h @@ -0,0 +1,870 @@ +/*! + \file cexif.h + +*/ + +#ifndef CEXIF_H +#define CEXIF_H + +#include +#include +#include +#include + +#include +#include + +#include + + +/*! + \brief + + \class cEXIFCompression cexif.h "cexif.h" +*/ +class cEXIFCompression +{ +public: + cEXIFCompression(const qint32& iID, const QString& szCompression); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szCompression; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFCompression*) + +/*! + \brief + + \class cEXIFCompressionList cexif.h "cexif.h" +*/ +class cEXIFCompressionList : public QList +{ +public: + cEXIFCompressionList(); + + /*! + \brief + + \fn add + \param iID + \param szCompression + \return cEXIFCompression + */ + cEXIFCompression* add(const qint32& iID, const QString& szCompression); + /*! + \brief + + \fn find + \param iID + \return cEXIFCompression + */ + cEXIFCompression* find(const qint32& iID); + + /*! + \brief + + \fn compression + \param iID + \return QString + */ + QString compression(const qint32& iID); +}; + +Q_DECLARE_METATYPE(cEXIFCompressionList) + +/*! + \brief + + \class cEXIFLightSource cexif.h "cexif.h" +*/ +class cEXIFLightSource +{ +public: + cEXIFLightSource(const qint32& iID, const QString& szLightSource); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szLightSource; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFLightSource*) + +/*! + \brief + + \class cEXIFLightSourceList cexif.h "cexif.h" +*/ +class cEXIFLightSourceList : public QList +{ +public: + cEXIFLightSourceList(); + + /*! + \brief + + \fn add + \param iID + \param szLightSource + \return cEXIFLightSource + */ + cEXIFLightSource* add(const qint32& iID, const QString& szLightSource); + /*! + \brief + + \fn find + \param iID + \return cEXIFLightSource + */ + cEXIFLightSource* find(const qint32& iID); + + /*! + \brief + + \fn lightSource + \param iID + \return QString + */ + QString lightSource(const qint32& iID); +}; + +/*! + \brief + + \class cEXIFFlash cexif.h "cexif.h" +*/ +class cEXIFFlash +{ +public: + cEXIFFlash(const qint32& iID, const QString& szFlash); + + qint32 m_iID; /*!< TODO: describe */ + QString m_szFlash; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFFlash*) + +/*! + \brief + + \class cEXIFFlashList cexif.h "cexif.h" +*/ +class cEXIFFlashList : public QList +{ +public: + cEXIFFlashList(); + + /*! + \brief + + \fn add + \param iID + \param szFlash + \return cEXIFFlash + */ + cEXIFFlash* add(const qint32& iID, const QString& szFlash); + /*! + \brief + + \fn find + \param iID + \return cEXIFFlash + */ + cEXIFFlash* find(const qint32& iID); + + /*! + \brief + + \fn flash + \param iID + \return QString + */ + QString flash(const qint32& iID); +}; + +/** + * @brief + * + */ +class cEXIFTag +{ +public: + cEXIFTag(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription); + + qint32 m_iTAGID; /*!< TODO: describe */ + QString m_szTAGName; /*!< TODO: describe */ + QString m_szIFD; /*!< TODO: describe */ + QString m_szKey; /*!< TODO: describe */ + QString m_szType; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cEXIFTag*) + +/*! + \brief + + \class cEXIFTagList cexif.h "cexif.h" +*/ +class cEXIFTagList : public QList +{ +public: + cEXIFTagList(); + + /*! + \brief + + \fn add + \param iTAGID + \param szTAGName + \param szIFD + \param szType + \param szDescription + \return cEXIFTag + */ + cEXIFTag* add(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription); + /*! + \brief + + \fn find + \param szKey + \return cEXIFTag + */ + cEXIFTag* find(const QString& szKey); + /*! + \brief + + \fn find + \param iTAGID + \param szIFD + \return cEXIFTag + */ + cEXIFTag* find(const qint32& iTAGID, const QString& szIFD); + /*! + \brief + + \fn find + \param szTAG + \param szIFD + \return cEXIFTag + */ + cEXIFTag* find(const QString& szTAG, const QString& szIFD); +}; + +/*! + \brief + +*/ +class cEXIFValue +{ +public: + cEXIFValue(cEXIFTag* lpEXIFTag); + + /*! + \brief + + \fn exifTag + \return cEXIFTag + */ + cEXIFTag* exifTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cEXIFTag* m_lpEXIFTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cEXIFValue*) + +/*! + \brief + + \class cEXIFValueList cexif.h "cexif.h" +*/ +class cEXIFValueList : public QList +{ +public: + cEXIFValueList(); + + /*! + \brief + + \fn add + \param lpEXIFTag + \return cEXIFValue + */ + cEXIFValue* add(cEXIFTag* lpEXIFTag); + /*! + \brief + + \fn find + \param lpEXIFTag + \return cEXIFValue + */ + cEXIFValue* find(cEXIFTag* lpEXIFTag); +}; + +/** + * @brief + * + */ +class cIPTCTag +{ +public: + cIPTCTag(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + + qint32 m_iTAGID; /*!< TODO: describe */ + QString m_szTAGName; /*!< TODO: describe */ + qint32 m_iTypeID; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cIPTCTag*) + +/*! + \brief + + \class cIPTCTagList cexif.h "cexif.h" +*/ +class cIPTCTagList : public QList +{ +public: + cIPTCTagList(); + + /*! + \brief + + \fn add + \param iTAGID + \param szTAGName + \param iTypeID + \param szDescription + \return cIPTCTag + */ + cIPTCTag* add(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + /*! + \brief + + \fn find + \param iTAGID + \return cIPTCTag + */ + cIPTCTag* find(const qint32& iTAGID); +}; + +/*! + \brief + +*/ +class cIPTCValue +{ +public: + cIPTCValue(cIPTCTag* lpIPTCTag); + + /*! + \brief + + \fn iptcTag + \return cIPTCTag + */ + cIPTCTag* iptcTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cIPTCTag* m_lpIPTCTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cIPTCValue*) + +/*! + \brief + + \class cIPTCValueList cexif.h "cexif.h" +*/ +class cIPTCValueList : public QList +{ +public: + cIPTCValueList(); + + /*! + \brief + + \fn add + \param lpIPTCTag + \return cIPTCValue + */ + cIPTCValue* add(cIPTCTag* lpIPTCTag); + /*! + \brief + + \fn find + \param lpIPTCTag + \return cIPTCValue + */ + cIPTCValue* find(cIPTCTag* lpIPTCTag); +}; + +/** + * @brief + * + */ +class cXMPTag +{ +public: + cXMPTag(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + + QString m_szTAGName; /*!< TODO: describe */ + qint32 m_iTypeID; /*!< TODO: describe */ + QString m_szDescription; /*!< TODO: describe */ +}; + +Q_DECLARE_METATYPE(cXMPTag*) + +/*! + \brief + + \class cXMPTagList cexif.h "cexif.h" +*/ +class cXMPTagList : public QList +{ +public: + cXMPTagList(); + + /*! + \brief + + \fn add + \param szTAGName + \param iTypeID + \param szDescription + \return cXMPTag + */ + cXMPTag* add(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription); + /*! + \brief + + \fn find + \param szTAGName + \return cXMPTag + */ + cXMPTag* find(const QString& szTAGName); +}; + +/*! + \brief + +*/ +class cXMPValue +{ +public: + cXMPValue(cXMPTag* lpXMPTag); + + /*! + \brief + + \fn exifTag + \return cXMPTag + */ + cXMPTag* xmpTag(); + /*! + \brief + + \fn setValue + \param szValue + \param iTypeId + \param iCount + */ + void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1); + /*! + \brief + + \fn value + \return QVariant + */ + QVariant value(); + /*! + \brief + + \fn valueList + \return QList + */ + QList valueList(); + +private: + cXMPTag* m_lpXMPTag; /*!< TODO: describe */ + QList m_valueList; /*!< TODO: describe */ + + /*! + \brief + + \fn convertValue + \param szValue + \param iTypeId + \return QVariant + */ + QVariant convertValue(const QString& szValue, qint32 iTypeId); +}; + +Q_DECLARE_METATYPE(cXMPValue*) + +/*! + \brief + + \class cXMPValueList cexif.h "cexif.h" +*/ +class cXMPValueList : public QList +{ +public: + cXMPValueList(); + + /*! + \brief + + \fn add + \param lpXMPTag + \return cXMPValue + */ + cXMPValue* add(cXMPTag* lpXMPTag); + /*! + \brief + + \fn find + \param lpXMPTag + \return cXMPValue + */ + cXMPValue* find(cXMPTag* lpXMPTag); +}; + +/*! + \brief + + \class cEXIF cexif.h "cexif.h" +*/ +class cEXIF +{ +public: + cEXIF(cEXIFTagList* lpEXIFTagList, cEXIFCompressionList* lpEXIFCompressionList, cEXIFLightSourceList* lpEXIFLightSourceList, cEXIFFlashList* lpEXIFFlashList, cIPTCTagList* lpIPTCTagList, cXMPTagList* lpXMPTagList); + + /*! + \brief + + \fn fromFile + \param szFileName + \return bool + */ + bool fromFile(const QString& szFileName); + + /*! + \brief + + \fn mimeType + \return QString + */ + QString mimeType(); + /*! + \brief + + \fn imageWidth + \return qint32 + */ + qint32 imageWidth(); + /*! + \brief + + \fn imageHeight + \return qint32 + */ + qint32 imageHeight(); + /*! + \brief + + \fn imageOrientation + \return qint16 + */ + qint16 imageOrientation(); + /*! + \brief + + \fn cameraMake + \return QString + */ + QString cameraMake(); + /*! + \brief + + \fn cameraModel + \return QString + */ + QString cameraModel(); + /*! + \brief + + \fn dateTime + \return QDateTime + */ + QDateTime dateTime(); + /*! + \brief + + \fn fNumber + \return QString + */ + QString fNumber(); + /*! + \brief + + \fn iso + \return qint32 + */ + qint32 iso(); + /*! + \brief + + \fn flash + \return QString + */ + QString flash(); + /*! + \brief + + \fn flashID + \return qint32 + */ + qint32 flashID(); + /*! + \brief + + \fn focalLength + \return qreal + */ + qreal focalLength(); + /*! + \brief + + \fn lensMake + \return QString + */ + QString lensMake(); + /*! + \brief + + \fn lensModel + \return QString + */ + QString lensModel(); + /*! + \brief + + \fn exposureTime + \return QString + */ + QString exposureTime(); + /*! + \brief + + \fn exposureBias + \return qint32 + */ + qint32 exposureBias(); + /*! + \brief + + \fn exifVersion + \return QString + */ + QString exifVersion(); + /*! + \brief + + \fn dateTimeOriginal + \return QDateTime + */ + QDateTime dateTimeOriginal(); + /*! + \brief + + \fn dateTimeDigitized + \return QDateTime + */ + QDateTime dateTimeDigitized(); + /*! + \brief + + \fn whiteBalance + \return qint32 + */ + qint32 whiteBalance(); + /*! + \brief + + \fn focalLength35 + \return qreal + */ + qreal focalLength35(); + /*! + \brief + + \fn gps + \return QString + */ + QString gps(); + /*! + \brief + + \fn duration + \return QString + */ + QString duration(); + /*! + \brief + + \fn fileName + \return QString + */ + QString fileName(); + /*! + \brief + + \fn previewList + \return QList + */ + QList previewList(); + /*! + \brief + + \fn thumbnail + \return QImage + */ + QImage thumbnail(); + + /*! + \brief + + \fn copyTo + \param fileName + */ + bool copyTo(const QString& fileName); + +private: + cEXIFValueList m_exifValueList; /*!< TODO: describe */ + cIPTCValueList m_iptcValueList; /*!< TODO: describe */ + cXMPValueList m_xmpValueList; /*!< TODO: describe */ + QString m_szMimeType; /*!< TODO: describe */ + qint32 m_iWidth; /*!< TODO: describe */ + qint32 m_iHeight; /*!< TODO: describe */ + QString m_szFileName; /*!< TODO: describe */ + QList m_previewList; /*!< TODO: describe */ + QImage m_thumbnail; /*!< TODO: describe */ + + cEXIFTagList* m_lpEXIFTagList; /*!< TODO: describe */ + cEXIFCompressionList* m_lpEXIFCompressionList; /*!< TODO: describe */ + cEXIFLightSourceList* m_lpEXIFLightSourceList; /*!< TODO: describe */ + cEXIFFlashList* m_lpEXIFFlashList; /*!< TODO: describe */ + + cIPTCTagList* m_lpIPTCTagList; /*!< TODO: describe */ + + cXMPTagList* m_lpXMPTagList; /*!< TODO: describe */ + + /*! + \brief + + \fn getEXIFTag + \param iTAGID + \param szIFD + \return QVariant + */ + QVariant getEXIFTag(qint32 iTAGID, const QString& szIFD); + /*! + \brief + + \fn getIPTCTag + \param iTAGID + \return QVariant + */ + QVariant getIPTCTag(qint32 iTAGID); + /*! + \brief + + \fn getXMPTag + \param szTAGName + \return QVariant + */ + QVariant getXMPTag(const QString& szTAGName); + /*! + \brief + + \fn getTagList + \param iTAGID + \param iIFDID + \return QList + */ + QList getTagList(qint32 iTAGID, const QString& szIFD); +}; + +Q_DECLARE_METATYPE(cEXIF*) + +#endif // CEXIF_H diff --git a/cfilebrowser.cpp b/cfilebrowser.cpp new file mode 100644 index 0000000..9b0d156 --- /dev/null +++ b/cfilebrowser.cpp @@ -0,0 +1,14 @@ +#include "cfilebrowser.h" +#include "ui_cfilebrowser.h" + +cFileBrowser::cFileBrowser(QWidget *parent) : + QWidget(parent), + ui(new Ui::cFileBrowser) +{ + ui->setupUi(this); +} + +cFileBrowser::~cFileBrowser() +{ + delete ui; +} diff --git a/cfilebrowser.h b/cfilebrowser.h new file mode 100644 index 0000000..0d70e42 --- /dev/null +++ b/cfilebrowser.h @@ -0,0 +1,22 @@ +#ifndef CFILEBROWSER_H +#define CFILEBROWSER_H + +#include + +namespace Ui { +class cFileBrowser; +} + +class cFileBrowser : public QWidget +{ + Q_OBJECT + +public: + explicit cFileBrowser(QWidget *parent = nullptr); + ~cFileBrowser(); + +private: + Ui::cFileBrowser *ui; +}; + +#endif // CFILEBROWSER_H diff --git a/cfilebrowser.ui b/cfilebrowser.ui new file mode 100644 index 0000000..c52210f --- /dev/null +++ b/cfilebrowser.ui @@ -0,0 +1,21 @@ + + + + + cFileBrowser + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/cfileviewer.cpp b/cfileviewer.cpp new file mode 100644 index 0000000..40f4614 --- /dev/null +++ b/cfileviewer.cpp @@ -0,0 +1,14 @@ +#include "cfileviewer.h" +#include "ui_cfileviewer.h" + +cFileViewer::cFileViewer(QWidget *parent) : + QWidget(parent), + ui(new Ui::cFileViewer) +{ + ui->setupUi(this); +} + +cFileViewer::~cFileViewer() +{ + delete ui; +} diff --git a/cfileviewer.h b/cfileviewer.h new file mode 100644 index 0000000..6b20d77 --- /dev/null +++ b/cfileviewer.h @@ -0,0 +1,22 @@ +#ifndef CFILEVIEWER_H +#define CFILEVIEWER_H + +#include + +namespace Ui { +class cFileViewer; +} + +class cFileViewer : public QWidget +{ + Q_OBJECT + +public: + explicit cFileViewer(QWidget *parent = nullptr); + ~cFileViewer(); + +private: + Ui::cFileViewer *ui; +}; + +#endif // CFILEVIEWER_H diff --git a/cfileviewer.ui b/cfileviewer.ui new file mode 100644 index 0000000..7322fc3 --- /dev/null +++ b/cfileviewer.ui @@ -0,0 +1,21 @@ + + + + + cFileViewer + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/cimage.cpp b/cimage.cpp new file mode 100644 index 0000000..42f7645 --- /dev/null +++ b/cimage.cpp @@ -0,0 +1,428 @@ +/*! + \file cimage.cpp + +*/ + +#include "cimage.h" + +#include "opencv2/imgproc/types_c.h" +#include "opencv2/imgproc.hpp" + +#include +#include + +#include +#include + + +cImage::cImage() : + QImage(), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(const QSize &size, QImage::Format format) : + QImage(size, format), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(int width, int height, QImage::Format format) : + QImage(width, height, format), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) : + QImage(data, width, height, format, cleanupFunction, cleanupInfo), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) : + QImage(data, width, height, format, cleanupFunction, cleanupInfo), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) : + QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) : + QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(const QString &fileName, const char *format) : + QImage(), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ + load(fileName, format); +} + +cImage::cImage(const QImage &image) : + QImage(image), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +cImage::cImage(QImage &&other) : + QImage(other), + m_isChromatic(true), + m_camType(camera_unknown), + m_raw(false) +{ +} + +bool cImage::load(const QString &fileName, const char *format) +{ + QFileInfo fileInfo(fileName); + + if(!fileInfo.suffix().compare("NEF", Qt::CaseInsensitive)) + return(loadRAW(fileName)); + + if(QImage::load(fileName, format)) + return(true); + + return(loadRAW(fileName)); +} + +bool cImage::openBuffer(const QString &fileName, const QSharedPointer& ba, LibRaw& iProcessor) +{ + int error = LIBRAW_DATA_ERROR; + QFileInfo fi(fileName); + + if(fi.suffix().contains("iiq", Qt::CaseInsensitive) || !ba || ba->isEmpty()) + error = iProcessor.open_file(fileName.toStdString().c_str()); + else + { + if(ba->isEmpty() || ba->size() < 100) + return(false); + + error = iProcessor.open_buffer((void*)ba->constData(), static_cast(ba->size())); + } + + return (error == LIBRAW_SUCCESS); +} + +void cImage::detectSpecialCamera(const LibRaw & iProcessor) +{ + if(QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic") + m_isChromatic = false; + + if (QString(iProcessor.imgdata.idata.model).contains("IQ260")) + m_camType = camera_iiq; + else if (!QString(iProcessor.imgdata.idata.make).compare("Canon", Qt::CaseInsensitive)) + m_camType = camera_canon; +} + +cv::Mat cImage::demosaic(LibRaw & iProcessor) +{ + cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC1); + double dynamicRange = static_cast(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black); + + // normalize all image values + for(int rIdx = 0; rIdx < rawMat.rows; rIdx++) + { + unsigned short *ptrRaw = rawMat.ptr(rIdx); + + for (int cIdx = 0; cIdx < rawMat.cols; cIdx++) + { + int colIdx = iProcessor.COLOR(rIdx, cIdx); + double val = static_cast(iProcessor.imgdata.image[(rawMat.cols*rIdx) + cIdx][colIdx]); + + // normalize the value w.r.t the black point defined + val = (val - iProcessor.imgdata.color.black) / dynamicRange; + ptrRaw[cIdx] = clip(val * USHRT_MAX); // for conversion to 16U + } + } + + // no demosaicing + if(m_isChromatic) + { + unsigned long type = static_cast(iProcessor.imgdata.idata.filters); + type = type & 255; + + cv::Mat rgbImg; + + //define bayer pattern + if(type == 180) + cvtColor(rawMat, rgbImg, CV_BayerBG2RGB); //bitmask 10 11 01 00 -> 3(G) 2(B) 1(G) 0(R) -> RG RG RG + else if(type == 30) + cvtColor(rawMat, rgbImg, CV_BayerRG2RGB); //bitmask 00 01 11 10 -> 0 1 3 2 + else if(type == 225) + cvtColor(rawMat, rgbImg, CV_BayerGB2RGB); //bitmask 11 10 00 01 + else if(type == 75) + cvtColor(rawMat, rgbImg, CV_BayerGR2RGB); //bitmask 01 00 10 11 + else + { + qWarning() << "Wrong Bayer Pattern (not BG, RG, GB, GR)\n"; + return cv::Mat(); + } + + rawMat = rgbImg; + } + + // 16U (1 or 3 channeled) Mat + return(rawMat); +} + +cv::Mat cImage::prepareImg(const LibRaw & iProcessor) +{ + cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC3, cv::Scalar(0)); + double dynamicRange = static_cast(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black); + + // normalization function + auto normalize = [&](double val) + { + val = (val - iProcessor.imgdata.color.black) / dynamicRange; + return clip(val * USHRT_MAX); + }; + + for (int rIdx = 0; rIdx < rawMat.rows; rIdx++) + { + unsigned short *ptrI = rawMat.ptr(rIdx); + + for (int cIdx = 0; cIdx < rawMat.cols; cIdx++) + { + + *ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][0]); + ptrI++; + *ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][1]); + ptrI++; + *ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][2]); + ptrI++; + } + } + + return rawMat; +} + +void cImage::whiteBalance(const LibRaw & iProcessor, cv::Mat & img) +{ + // white balance must not be empty at this point + cv::Mat wb = whiteMultipliers(iProcessor); + const float* wbp = wb.ptr(); + assert(wb.cols == 4); + + for(int rIdx = 0; rIdx < img.rows; rIdx++) + { + unsigned short *ptr = img.ptr(rIdx); + + for (int cIdx = 0; cIdx < img.cols; cIdx++) + { + //apply white balance correction + unsigned short r = clip(*ptr * wbp[0]); + unsigned short g = clip(*(ptr+1) * wbp[1]); + unsigned short b = clip(*(ptr+2) * wbp[2]); + + //apply color correction + int cr = qRound(iProcessor.imgdata.color.rgb_cam[0][0] * r + + iProcessor.imgdata.color.rgb_cam[0][1] * g + + iProcessor.imgdata.color.rgb_cam[0][2] * b); + int cg = qRound(iProcessor.imgdata.color.rgb_cam[1][0] * r + + iProcessor.imgdata.color.rgb_cam[1][1] * g + + iProcessor.imgdata.color.rgb_cam[1][2] * b); + int cb = qRound(iProcessor.imgdata.color.rgb_cam[2][0] * r + + iProcessor.imgdata.color.rgb_cam[2][1] * g + + iProcessor.imgdata.color.rgb_cam[2][2] * b); + + // clip & save color corrected values + *ptr = clip(cr); + ptr++; + *ptr = clip(cg); + ptr++; + *ptr = clip(cb); + ptr++; + } + } +} + +cv::Mat cImage::whiteMultipliers(const LibRaw & iProcessor) +{ + // get camera white balance multipliers + cv::Mat wm(1, 4, CV_32FC1); + + float* wmp = wm.ptr(); + + for(int idx = 0; idx < wm.cols; idx++) + wmp[idx] = iProcessor.imgdata.color.cam_mul[idx]; + + if(wmp[3] == 0.0f) + wmp[3] = wmp[1]; // take green (usually its RGBG) + + // normalize white balance multipliers + float w = static_cast(cv::sum(wm)[0] / 4.0f); + float maxW = 1.0f; + + //clipping according the camera model + //if w > 2.0 maxW is 256, otherwise 512 + //tested empirically + //check if it can be defined by some metadata settings? + if(w > 2.0f) + maxW = 255.0f; + if(w > 2.0f && m_camType == camera_canon) + maxW = 511.0f; // some cameras would even need ~800 - why? + + //normalize white point + wm /= maxW; + + // 1 x 4 32FC1 white balance vector + return wm; +} + +void cImage::gammaCorrection(const LibRaw & iProcessor, cv::Mat& img) +{ + // white balance must not be empty at this point + cv::Mat gt = gammaTable(iProcessor); + const unsigned short* gammaLookup = gt.ptr(); + assert(gt.cols == USHRT_MAX); + + for(int rIdx = 0; rIdx < img.rows; rIdx++) + { + unsigned short *ptr = img.ptr(rIdx); + + for(int cIdx = 0; cIdx < img.cols * img.channels(); cIdx++) + { + // values close to 0 are treated linear + if (ptr[cIdx] <= 5) // 0.018 * 255 + ptr[cIdx] = static_cast(qRound(ptr[cIdx] * static_cast(iProcessor.imgdata.params.gamm[1]) / 255.0)); + else + ptr[cIdx] = gammaLookup[ptr[cIdx]]; + } + } + +} + +cv::Mat cImage::gammaTable(const LibRaw & iProcessor) +{ + // OK this is an instance of reverse engineering: + // we found out that the values of (at least) the PhaseOne's achromatic back have to be doubled + // our images are no close to what their software (Capture One does) - only the gamma correction + // seems to be slightly different... -> now we can load compressed IIQs that are not supported by PS : ) + double cameraHackMlp = (QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic") ? 2.0 : 1.0; + + //read gamma value and create gamma table + double gamma = static_cast(iProcessor.imgdata.params.gamm[0]); + + cv::Mat gmt(1, USHRT_MAX, CV_16UC1); + unsigned short* gmtp = gmt.ptr(); + + for (int idx = 0; idx < gmt.cols; idx++) { + gmtp[idx] = clip(qRound((1.099*std::pow(static_cast(idx) / USHRT_MAX, gamma) - 0.099) * 255 * cameraHackMlp)); + } + + // a 1 x 65535 U16 gamma table + return gmt; +} + +QImage cImage::raw2Img(const LibRaw & iProcessor, cv::Mat & img) +{ + //check the pixel aspect ratio of the raw image + if(iProcessor.imgdata.sizes.pixel_aspect != 1.0f) + cv::resize(img, img, cv::Size(), static_cast(iProcessor.imgdata.sizes.pixel_aspect), 1.0f); + + // revert back to 8-bit image + img.convertTo(img, CV_8U); + + // TODO: for now - fix this! + if(img.channels() == 1) + cv::cvtColor(img, img, CV_GRAY2RGB); + + return(mat2QImage(img)); +} + +QImage cImage::mat2QImage(cv::Mat img) +{ + QImage qImg; + + // since Mat header is copied, a new buffer should be allocated (check this!) + if(img.depth() == CV_32F) + img.convertTo(img, CV_8U, 255); + + if(img.type() == CV_8UC1) + qImg = QImage(img.data, static_cast(img.cols), static_cast(img.rows), static_cast(img.step), QImage::Format_Indexed8); // opencv uses size_t for scaling in x64 applications + if(img.type() == CV_8UC3) + qImg = QImage(img.data, static_cast(img.cols), static_cast(img.rows), static_cast(img.step), QImage::Format_RGB888); + if(img.type() == CV_8UC4) + qImg = QImage(img.data, static_cast(img.cols), static_cast(img.rows), static_cast(img.step), QImage::Format_ARGB32); + + qImg = qImg.copy(); + + return qImg; +} + +bool cImage::loadRAW(const QString &fileName) +{ + QSharedPointer ba; + LibRaw iProcessor; + + if(!openBuffer(fileName, ba, iProcessor)) + { + qDebug() << "could not open buffer"; + return(false); + } + + detectSpecialCamera(iProcessor); + + int error = iProcessor.unpack(); + + if(strcmp(iProcessor.version(), "0.13.5") != 0) // fixes a bug specific to libraw 13 - version call is UNTESTED + iProcessor.raw2image(); + + if(error != LIBRAW_SUCCESS) + return(false); + + cv::Mat rawMat; + + if(iProcessor.imgdata.idata.filters) + rawMat = demosaic(iProcessor); + else + rawMat = prepareImg(iProcessor); + + // color correction + white balance + if(m_isChromatic) + whiteBalance(iProcessor, rawMat); + + gammaCorrection(iProcessor, rawMat); + + // reduce color noise +// if (DkSettingsManager::param().resources().filterRawImages && mIsChromatic) +// reduceColorNoise(iProcessor, rawMat); + + *this = raw2Img(iProcessor, rawMat); + + iProcessor.recycle(); + rawMat.release(); + + m_raw = true; + + return(true); +} + +bool cImage::isRaw() +{ + return(m_raw); +} diff --git a/cimage.h b/cimage.h new file mode 100644 index 0000000..103309b --- /dev/null +++ b/cimage.h @@ -0,0 +1,313 @@ +/*! + \file cimage.h + +*/ + +#ifndef CIMAGE_H +#define CIMAGE_H + + +#include "libraw/libraw.h" +#include + +#include + + +/*! + \brief + + \class cImage cimage.h "cimage.h" +*/ +class cImage : public QImage +{ +public: + cImage(); + /*! + \brief + + \fn cImage + \param size + \param format + */ + cImage(const QSize &size, QImage::Format format); + /*! + \brief + + \fn cImage + \param width + \param height + \param format + */ + cImage(int width, int height, QImage::Format format); + /*! + \brief + + \fn cImage + \param data + \param width + \param height + \param format + \param cleanupFunction + \param cleanupInfo + */ + cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); + /*! + \brief + + \fn cImage + \param data + \param width + \param height + \param format + \param cleanupFunction + \param cleanupInfo + */ + cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); + /*! + \brief + + \fn cImage + \param data + \param width + \param height + \param bytesPerLine + \param format + \param cleanupFunction + \param cleanupInfo + */ + cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); + /*! + \brief + + \fn cImage + \param data + \param width + \param height + \param bytesPerLine + \param format + \param cleanupFunction + \param cleanupInfo + */ + cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); + /*! + \brief + + \fn cImage + \param fileName + \param format + */ + cImage(const QString &fileName, const char *format = nullptr); + /*! + \brief + + \fn cImage + \param image + */ + cImage(const QImage &image); + /*! + \brief + + \fn cImage + \param other + */ + cImage(QImage &&other); + + /*! + \brief + + \fn load + \param fileName + \param format + \return bool + */ + bool load(const QString &fileName, const char *format = nullptr); + + /*! + \brief + + \fn isRaw + \return bool + */ + bool isRaw(); + +protected: + /*! + \brief + + \enum Cam + */ + enum Cam + { + camera_unknown = 0, + camera_iiq, + camera_canon, + camera_end + }; + + template + /*! + \brief + + \fn clip + \param val + \return num + */ + num clip(float val) const + { + int vr = qRound(val); + + // trust me I'm an engineer @ -2 + // with -2 we do not get pink in oversaturated areas + if (vr > std::numeric_limits::max()) + vr = std::numeric_limits::max()-2; + if (vr < 0) + vr = 0; + + return static_cast(vr); + } + + template + /*! + \brief + + \fn clip + \param val + \return num + */ + num clip(double val) const + { + int vr = qRound(val); + + // trust me I'm an engineer @ -2 + // with -2 we do not get pink in oversaturated areas + if (vr > std::numeric_limits::max()) + vr = std::numeric_limits::max()-2; + if (vr < 0) + vr = 0; + + return static_cast(vr); + } + + template + /*! + \brief + + \fn clip + \param val + \return num + */ + num clip(int val) const + { + int vr = qRound(static_cast(val)); + + // trust me I'm an engineer @ -2 + // with -2 we do not get pink in oversaturated areas + if (vr > std::numeric_limits::max()) + vr = std::numeric_limits::max()-2; + if (vr < 0) + vr = 0; + + return static_cast(vr); + } + +private: + bool m_isChromatic; /*!< TODO: describe */ + Cam m_camType; /*!< TODO: describe */ + bool m_raw; /*!< TODO: describe */ + + /*! + \brief + + \fn loadRAW + \param fileName + \return bool + */ + bool loadRAW(const QString &fileName); + + /*! + \brief + + \fn openBuffer + \param fileName + \param ba + \param iProcessor + \return bool + */ + bool openBuffer(const QString &fileName, const QSharedPointer& ba, LibRaw& iProcessor); + /*! + \brief + + \fn detectSpecialCamera + \param iProcessor + */ + void detectSpecialCamera(const LibRaw & iProcessor); + + /*! + \brief + + \fn demosaic + \param iProcessor + \return cv::Mat + */ + cv::Mat demosaic(LibRaw & iProcessor); + /*! + \brief + + \fn prepareImg + \param iProcessor + \return cv::Mat + */ + cv::Mat prepareImg(const LibRaw & iProcessor); + /*! + \brief + + \fn whiteBalance + \param iProcessor + \param img + */ + void whiteBalance(const LibRaw & iProcessor, cv::Mat & img); + /*! + \brief + + \fn whiteMultipliers + \param iProcessor + \return cv::Mat + */ + cv::Mat whiteMultipliers(const LibRaw & iProcessor); + /*! + \brief + + \fn gammaCorrection + \param iProcessor + \param img + */ + void gammaCorrection(const LibRaw & iProcessor, cv::Mat& img); + /*! + \brief + + \fn gammaTable + \param iProcessor + \return cv::Mat + */ + cv::Mat gammaTable(const LibRaw & iProcessor); + + /*! + \brief + + \fn raw2Img + \param iProcessor + \param img + \return QImage + */ + QImage raw2Img(const LibRaw & iProcessor, cv::Mat & img); + /*! + \brief + + \fn mat2QImage + \param img + \return QImage + */ + QImage mat2QImage(cv::Mat img); +}; + +#endif // CIMAGE_H