Permalink
Browse files

Make RAW image thumbnails much faster

Turns out that nearly all supported RAW image formats actually have embedded
JPG thumbnails in them.  Rather than decode to RAW then reencode, just load
the thumbnail from it if it's there.  Also, rotate the image based on the
EXIF orientation in the thumbnail.

Now creating a thumbnail from a supported RAW format takes avout 200ms instead
of 10-15s.

Refs #10185
  • Loading branch information...
1 parent 3b83dbd commit 9cfa8a17a2c41b0cbe24110a65e9cf63193a3b64 @Beirdo Beirdo committed Jan 3, 2012
@@ -742,6 +742,11 @@ if test "$gallery" = "yes" ; then
echo " Automatically generated by configure - do not modify" >> ./mythgallery/dcrawplugin/config.h
echo "*/" >> ./mythgallery/dcrawplugin/config.h
+ if test "$exif" = "yes" ; then
+ echo "#define EXIF_SUPPORT 1" >> ./mythgallery/dcrawplugin/config.h
+
+ fi
+
if test x"$icc" != x ; then
echo " ICC profile $icc"
# Quote C escape characters.
@@ -45,7 +45,7 @@ QStringList DcrawFormats::getFilters()
{
QSet<QString> formats(getFormats());
QStringList filters;
- for (QSet<QString>::const_iterator i(formats.begin()); i != formats.end(); ++i)
+ for (QSet<QString>::iterator i = formats.begin(); i != formats.end(); ++i)
filters << ("*." + *i);
return filters;
}
@@ -9,8 +9,11 @@
#include <QImage>
#include <QIODevice>
#include <QString>
+
#include "mythsystem.h"
#include "exitcodes.h"
+#include "../mythgallery/galleryutil.h"
+#include "mythlogging.h"
namespace
{
@@ -74,3 +77,36 @@ bool DcrawHandler::read(QImage *image)
return loaded;
}
+int DcrawHandler::loadThumbnail(QImage *image, QString fileName)
+{
+ QStringList arguments;
+ arguments << "-e" << "-c";
+ arguments << "'" + fileName + "'";
+
+ uint flags = kMSRunShell | kMSStdOut | kMSBuffered;
+ MythSystem ms("dcraw", arguments, flags);
+ ms.Run();
+ if (ms.Wait() != GENERIC_EXIT_OK)
+ return -1;
+
+ QByteArray buffer = ms.ReadAll();
+ if (buffer.isEmpty())
+ return -1;
+
+ if (!image->loadFromData(buffer, "JPG"))
+ return -1;
+
+ int rotateAngle = 0;
+
+#ifdef EXIF_SUPPORT
+ const unsigned char *buf = (const unsigned char *)buffer.constData();
+ int size = buffer.size();
+ rotateAngle = GalleryUtil::GetNaturalRotation(buf, size);
+#endif
+
+ return rotateAngle;
+}
+
+/*
+ * vim:ts=4:sw=4:ai:et:si:sts=4
+ */
@@ -1,13 +1,12 @@
#include <QImage>
#include <QImageIOHandler>
+#include <QString>
class DcrawHandler : public QImageIOHandler
{
-
-public:
-
+ public:
bool canRead() const;
bool read(QImage *image);
-
+ static int loadThumbnail(QImage *image, QString fileName);
};
@@ -15,6 +15,5 @@ class DcrawPlugin : public QImageIOPlugin
QStringList keys() const;
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
QImageIOHandler *create(QIODevice *device, const QByteArray &format) const;
-
};
@@ -15,3 +15,4 @@ SOURCES += dcrawformats.cpp
SOURCES += dcrawhandler.cpp
SOURCES += dcrawplugin.cpp
+QT += sql
@@ -39,7 +39,6 @@
#ifdef EXIF_SUPPORT
#include <libexif/exif-data.h>
#include <libexif/exif-entry.h>
-// include "exif.hpp"
#endif // EXIF_SUPPORT
#define LOC QString("GalleryUtil:")
@@ -122,87 +121,119 @@ bool GalleryUtil::IsMovie(const QString &filePath)
return false;
}
+long GalleryUtil::GetNaturalRotation(const unsigned char *buffer, int size)
+{
+ long rotateAngle = 0;
+
+#ifdef EXIF_SUPPORT
+ try
+ {
+ ExifData *data = exif_data_new_from_data(buffer, size);
+ if (data)
+ {
+ rotateAngle = GetNaturalRotation(data);
+ exif_data_free(data);
+ }
+ else
+ {
+ LOG(VB_FILE, LOG_ERR, LOC + "Could not load exif data from buffer");
+ }
+ }
+ catch (...)
+ {
+ LOG(VB_GENERAL, LOG_ERR, LOC +
+ "Failed to extract EXIF headers from buffer");
+ }
+#else
+ // Shut the compiler up about the unused argument
+ (void)buffer;
+ (void)length;
+#endif
+
+ return rotateAngle;
+}
+
long GalleryUtil::GetNaturalRotation(const QString &filePathString)
{
long rotateAngle = 0;
+
#ifdef EXIF_SUPPORT
QByteArray filePathBA = filePathString.toLocal8Bit();
const char *filePath = filePathBA.constData();
try
{
- ExifData *data = exif_data_new_from_file (filePath);
+ ExifData *data = exif_data_new_from_file(filePath);
if (data)
{
- for (int i = 0; i < EXIF_IFD_COUNT; i++)
- {
- ExifEntry *entry = exif_content_get_entry (data->ifd[i],
- EXIF_TAG_ORIENTATION);
- ExifByteOrder byteorder = exif_data_get_byte_order (data);
-
- if (entry)
- {
- ExifShort v_short = exif_get_short (entry->data, byteorder);
- LOG(VB_GENERAL, LOG_DEBUG,
- QString("Exif entry=%1").arg(v_short));
-
- /* See http://sylvana.net/jpegcrop/exif_orientation.html*/
- if (v_short == 8)
- {
- rotateAngle = -90;
- }
- else if (v_short == 6)
- {
- rotateAngle = 90;
- }
- break;
- }
- }
+ rotateAngle = GetNaturalRotation(data);
exif_data_free(data);
}
else
{
LOG(VB_FILE, LOG_ERR, LOC +
QString("Could not load exif data from '%1'") .arg(filePath));
}
-
-#if 0
- Exiv2::ExifData exifData;
- int rc = exifData.read(filePath);
- if (!rc)
- {
- Exiv2::ExifKey key = Exiv2::ExifKey("Exif.Image.Orientation");
- Exiv2::ExifData::iterator pos = exifData.findKey(key);
- if (pos != exifData.end())
- {
- long orientation = pos->toLong();
- switch (orientation)
- {
- case 6:
- rotateAngle = 90;
- break;
- case 8:
- rotateAngle = -90;
- break;
- default:
- rotateAngle = 0;
- break;
- }
- }
- }
-#endif
}
catch (...)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
- QString("Failed to extract EXIF headers from '%1'")
- .arg(filePathString));
+ QString("Failed to extract EXIF headers from '%1'") .arg(filePath));
}
-
#else
// Shut the compiler up about the unused argument
(void)filePathString;
+#endif
+
+ return rotateAngle;
+}
+
+long GalleryUtil::GetNaturalRotation(void *exifData)
+{
+ long rotateAngle = 0;
+
+#ifdef EXIF_SUPPORT
+ ExifData *data = (ExifData *)exifData;
+
+ if (!data)
+ return 0;
+
+ for (int i = 0; i < EXIF_IFD_COUNT; i++)
+ {
+ ExifEntry *entry = exif_content_get_entry(data->ifd[i],
+ EXIF_TAG_ORIENTATION);
+ ExifByteOrder byteorder = exif_data_get_byte_order(data);
+
+ if (entry)
+ {
+ ExifShort v_short = exif_get_short(entry->data, byteorder);
+ LOG(VB_GENERAL, LOG_DEBUG,
+ QString("Exif entry=%1").arg(v_short));
+
+ /* See http://sylvana.net/jpegcrop/exif_orientation.html*/
+ switch (v_short)
+ {
+ case 3:
+ rotateAngle = 180;
+ break;
+ case 6:
+ rotateAngle = 90;
+ break;
+ case 8:
+ rotateAngle = -90;
+ break;
+ default:
+ rotateAngle = 0;
+ break;
+ }
+ break;
+ }
+ }
+#else
+ // Shut the compiler up about the unused argument
+ (void)exifData;
#endif // EXIF_SUPPORT
+
return rotateAngle;
}
@@ -776,3 +807,7 @@ static bool FileDelete(const QFileInfo &file)
return true;
}
+
+/*
+ * vim:ts=4:sw=4:ai:et:si:sts=4
+ */
@@ -40,6 +40,7 @@ class GalleryUtil
static bool IsImage(const QString &filePath);
static bool IsMovie(const QString &filePath);
+ static long GetNaturalRotation(const unsigned char *buffer, int size);
static long GetNaturalRotation(const QString &filePath);
static QString GetCaption(const QString &filePath);
@@ -65,6 +66,7 @@ class GalleryUtil
static bool DeleteDirectory(const QFileInfo &dir);
static bool RenameDirectory(const QString &currDir, const QString &oldName,
const QString &newName);
+ static long GetNaturalRotation(void *exifData);
};
#endif /* GALLERYUTIL_H */
@@ -334,16 +334,6 @@ void IconView::LoadThumbnail(ThumbItem *item)
.arg(ThumbGenerator::getThumbcacheDir(m_currDir))
.arg(item->GetName());
-// int rotateAngle = 0;
-//
-// rotateAngle = item->GetRotationAngle();
-//
-// if (rotateAngle != 0)
-// {
-// QMatrix matrix;
-// matrix.rotate(rotateAngle);
-// image = image.xForm(matrix);
-// }
item->SetImageFilename(imagePath);
}
@@ -26,6 +26,7 @@
#include <QDir>
#include <QEvent>
#include <QImageReader>
+#include <QSet>
// myth
#include <mythuihelper.h>
@@ -42,6 +43,11 @@
#include "exitcodes.h"
#include "mythlogging.h"
+#ifdef DCRAW_SUPPORT
+#include "../dcrawplugin/dcrawformats.h"
+#include "../dcrawplugin/dcrawhandler.h"
+#endif // DCRAW_SUPPORT
+
#ifdef EXIF_SUPPORT
#include <libexif/exif-data.h>
#include <libexif/exif-entry.h>
@@ -335,6 +341,27 @@ void ThumbGenerator::loadFile(QImage& image, const QFileInfo& fi)
return;
#endif
+#ifdef DCRAW_SUPPORT
+ QString extension = fi.suffix();
+ QSet<QString> dcrawFormats = DcrawFormats::getFormats();
+ int rotateAngle;
+
+ if (dcrawFormats.contains(extension) &&
+ (rotateAngle = DcrawHandler::loadThumbnail(&image,
+ fi.absoluteFilePath())) != -1 &&
+ image.width() > m_width && image.height() > m_height)
+ {
+ if (rotateAngle != 0)
+ {
+ QMatrix matrix;
+ matrix.rotate(rotateAngle);
+ image = image.transformed(matrix);
+ }
+
+ return;
+ }
+#endif
+
image.load(fi.absoluteFilePath());
}
}
@@ -373,3 +400,7 @@ QString ThumbGenerator::getThumbcacheDir(const QString& inDir)
return aPath;
}
+
+/*
+ * vim:ts=4:sw=4:ai:et:si:sts=4
+ */

0 comments on commit 9cfa8a1

Please sign in to comment.