summaryrefslogtreecommitdiffstats
path: root/src/tdeioslave/digikamthumbnail.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tdeioslave/digikamthumbnail.cpp')
-rw-r--r--src/tdeioslave/digikamthumbnail.cpp635
1 files changed, 635 insertions, 0 deletions
diff --git a/src/tdeioslave/digikamthumbnail.cpp b/src/tdeioslave/digikamthumbnail.cpp
new file mode 100644
index 00000000..3dd4df28
--- /dev/null
+++ b/src/tdeioslave/digikamthumbnail.cpp
@@ -0,0 +1,635 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2003-01-15
+ * Description : digiKam TDEIO slave to get image thumbnails.
+ * This tdeio-slave support this freedesktop
+ * specification about thumbnails mamagement:
+ * http://jens.triq.net/thumbnail-spec
+ *
+ * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
+ * Copyright (C) 2003-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+#define XMD_H
+#define PNG_BYTES_TO_CHECK 4
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+
+// TQt includes.
+
+#include <tqcstring.h>
+#include <tqstring.h>
+#include <tqimage.h>
+#include <tqdatastream.h>
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqdir.h>
+#include <tqwmatrix.h>
+#include <tqregexp.h>
+#include <tqapplication.h>
+
+// KDE includes.
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <kinstance.h>
+#include <kimageio.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kstandarddirs.h>
+#include <kmdcodec.h>
+#include <tdetempfile.h>
+#include <ktrader.h>
+#include <klibloader.h>
+#include <kmimetype.h>
+#include <kprocess.h>
+#include <tdeio/global.h>
+#include <tdeio/thumbcreator.h>
+#include <tdefilemetainfo.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+#if KDCRAW_VERSION < 0x000106
+#include <libkdcraw/dcrawbinary.h>
+#endif
+
+// Local includes.
+
+#include "dimg.h"
+#include "dmetadata.h"
+#include "jpegutils.h"
+#include "digikamthumbnail.h"
+#include "digikam_export.h"
+
+// C Ansi includes.
+
+extern "C"
+{
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <png.h>
+}
+
+using namespace TDEIO;
+using namespace Digikam;
+
+tdeio_digikamthumbnailProtocol::tdeio_digikamthumbnailProtocol(int argc, char** argv)
+ : SlaveBase("tdeio_digikamthumbnail", argv[2], argv[3])
+{
+ argc_ = argc;
+ argv_ = argv;
+ app_ = 0;
+ digiKamFingerPrint = TQString("Digikam Thumbnail Generator");
+ createThumbnailDirs();
+}
+
+tdeio_digikamthumbnailProtocol::~tdeio_digikamthumbnailProtocol()
+{
+}
+
+void tdeio_digikamthumbnailProtocol::get(const KURL& url)
+{
+ int size = metaData("size").toInt();
+ bool exif = (metaData("exif") == "yes");
+
+ cachedSize_ = (size <= 128) ? 128 : 256;
+
+ if (cachedSize_ <= 0)
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("No or invalid size specified"));
+ kdWarning() << "No or invalid size specified" << endl;
+ return;
+ }
+
+ // generate the thumbnail path
+ TQString uri = KURL(url.path()).url(); // "file://" uri
+ KMD5 md5( TQFile::encodeName(uri).data() );
+ TQString thumbPath = (cachedSize_ == 128) ? smallThumbPath_ : bigThumbPath_;
+ thumbPath += TQFile::encodeName( md5.hexDigest() + ".png" ).data();
+
+ TQImage img;
+ bool regenerate = true;
+
+ // stat the original file
+ struct stat st;
+ if (::stat(TQFile::encodeName(url.path(-1)), &st) != 0)
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("File does not exist"));
+ return;
+ }
+
+ // NOTE: if thumbnail have not been generated by digiKam (konqueror for example),
+ // force to recompute it, else we use it.
+
+ img = loadPNG(thumbPath);
+ if (!img.isNull())
+ {
+ if (img.text("Thumb::MTime") == TQString::number(st.st_mtime) &&
+ img.text("Software") == digiKamFingerPrint)
+ regenerate = false;
+ }
+
+ if (regenerate)
+ {
+ // To speed-up thumb extraction, we trying to load image using the file extension.
+ if ( !loadByExtension(img, url.path()) )
+ {
+ // Try JPEG loading : JPEG files without using Exif Thumb.
+ if ( !loadJPEG(img, url.path()) )
+ {
+ // Try to load with dcraw : RAW files.
+ if (!KDcrawIface::KDcraw::loadDcrawPreview(img, url.path()) )
+ {
+ // Try to load with DImg : TIFF, PNG, etc.
+ if (!loadDImg(img, url.path()) )
+ {
+ // Try to load with KDE thumbcreators : video files and others stuff.
+ loadKDEThumbCreator(img, url.path());
+ }
+ }
+ }
+ }
+
+ if (img.isNull())
+ {
+ error(TDEIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1")
+ .arg(url.prettyURL()));
+ kdWarning() << "Cannot create thumbnail for " << url.path() << endl;
+ return;
+ }
+
+ if (TQMAX(img.width(),img.height()) != cachedSize_)
+ img = img.smoothScale(cachedSize_, cachedSize_, TQImage::ScaleMin);
+
+ if (img.depth() != 32)
+ img = img.convertDepth(32);
+
+ if (exif)
+ exifRotate(url.path(), img);
+
+ img.setText(TQString("Thumb::URI").latin1(), 0, uri);
+ img.setText(TQString("Thumb::MTime").latin1(), 0, TQString::number(st.st_mtime));
+ img.setText(TQString("Software").latin1(), 0, digiKamFingerPrint);
+
+ KTempFile temp(thumbPath + "-digikam-", ".png");
+ if (temp.status() == 0)
+ {
+ img.save(temp.name(), "PNG", 0);
+ ::rename(TQFile::encodeName(temp.name()),
+ TQFile::encodeName(thumbPath));
+ }
+ }
+
+ img = img.smoothScale(size, size, TQImage::ScaleMin);
+
+ if (img.isNull())
+ {
+ error(TDEIO::ERR_INTERNAL, "Thumbnail is null");
+ return;
+ }
+
+ TQByteArray imgData;
+ TQDataStream stream( imgData, IO_WriteOnly );
+
+ const TQString shmid = metaData("shmid");
+ if (shmid.isEmpty())
+ {
+ stream << img;
+ }
+ else
+ {
+ void *shmaddr = shmat(shmid.toInt(), 0, 0);
+
+ if (shmaddr == (void *)-1)
+ {
+ error(TDEIO::ERR_INTERNAL, "Failed to attach to shared memory segment " + shmid);
+ kdWarning() << "Failed to attach to shared memory segment " << shmid << endl;
+ return;
+ }
+
+ if (img.width() * img.height() > cachedSize_ * cachedSize_)
+ {
+ error(TDEIO::ERR_INTERNAL, "Image is too big for the shared memory segment");
+ kdWarning() << "Image is too big for the shared memory segment" << endl;
+ shmdt((char*)shmaddr);
+ return;
+ }
+
+ stream << img.width() << img.height() << img.depth();
+ memcpy(shmaddr, img.bits(), img.numBytes());
+ shmdt((char*)shmaddr);
+ }
+
+ data(imgData);
+ finished();
+}
+
+bool tdeio_digikamthumbnailProtocol::loadByExtension(TQImage& image, const TQString& path)
+{
+ TQFileInfo fileInfo(path);
+ if (!fileInfo.exists())
+ return false;
+
+ // Try to use embedded preview image from metadata.
+ DMetadata metadata(path);
+ if (metadata.getImagePreview(image))
+ {
+ kdDebug() << "Use Exif/Iptc preview extraction. Size of image: "
+ << image.width() << "x" << image.height() << endl;
+ return true;
+ }
+
+ // Else, use the right way depending of image file extension.
+ TQString ext = fileInfo.extension(false).upper();
+#if KDCRAW_VERSION < 0x000106
+ TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+
+ if (!ext.isEmpty())
+ {
+ if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE"))
+ return (loadJPEG(image, path));
+ else if (ext == TQString("PNG"))
+ return (loadDImg(image, path));
+ else if (ext == TQString("TIFF") || ext == TQString("TIF"))
+ return (loadDImg(image, path));
+ else if (rawFilesExt.upper().contains(ext))
+ return (KDcrawIface::KDcraw::loadDcrawPreview(image, path));
+ }
+
+ return false;
+}
+
+bool tdeio_digikamthumbnailProtocol::loadJPEG(TQImage& image, const TQString& path)
+{
+ return Digikam::loadJPEGScaled(image, path, cachedSize_);
+}
+
+void tdeio_digikamthumbnailProtocol::exifRotate(const TQString& filePath, TQImage& thumb)
+{
+ // Rotate thumbnail based on metadata orientation information
+
+ DMetadata metadata(filePath);
+ DMetadata::ImageOrientation orientation = metadata.getImageOrientation();
+
+ if (orientation == DMetadata::ORIENTATION_NORMAL ||
+ orientation == DMetadata::ORIENTATION_UNSPECIFIED)
+ return;
+
+ TQWMatrix matrix;
+
+ switch (orientation)
+ {
+ case DMetadata::ORIENTATION_NORMAL:
+ case DMetadata::ORIENTATION_UNSPECIFIED:
+ break;
+
+ case DMetadata::ORIENTATION_HFLIP:
+ matrix.scale(-1, 1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_180:
+ matrix.rotate(180);
+ break;
+
+ case DMetadata::ORIENTATION_VFLIP:
+ matrix.scale(1, -1);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_HFLIP:
+ matrix.scale(-1, 1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90:
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_90_VFLIP:
+ matrix.scale(1, -1);
+ matrix.rotate(90);
+ break;
+
+ case DMetadata::ORIENTATION_ROT_270:
+ matrix.rotate(270);
+ break;
+ }
+
+ // transform accordingly
+ thumb = thumb.xForm(matrix);
+}
+
+TQImage tdeio_digikamthumbnailProtocol::loadPNG(const TQString& path)
+{
+ png_uint_32 w32, h32;
+ int w, h;
+ bool has_alpha;
+ bool has_grey;
+ FILE *f;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ int bit_depth, color_type, interlace_type;
+
+ has_alpha = 0;
+ has_grey = 0;
+
+ TQImage qimage;
+
+ f = fopen(path.latin1(), "rb");
+ if (!f)
+ return qimage;
+
+ unsigned char buf[PNG_BYTES_TO_CHECK];
+
+ fread(buf, 1, PNG_BYTES_TO_CHECK, f);
+ if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK))
+ {
+ fclose(f);
+ return qimage;
+ }
+ rewind(f);
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ fclose(f);
+ return qimage;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ png_init_io(png_ptr, f);
+ png_read_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32),
+ (png_uint_32 *) (&h32), &bit_depth, &color_type,
+ &interlace_type, NULL, NULL);
+
+ w = w32;
+ h = h32;
+
+ qimage.create(w, h, 32);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ has_alpha = 1;
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ has_alpha = 1;
+ has_grey = 1;
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ has_grey = 1;
+
+ unsigned char **lines;
+ int i;
+
+ if (has_alpha)
+ png_set_expand(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian)
+ {
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+ png_set_bgr(png_ptr);
+ }
+ else
+ {
+ png_set_swap_alpha(png_ptr);
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
+ }
+
+ /* 16bit color -> 8bit color */
+ if ( bit_depth == 16 )
+ png_set_strip_16(png_ptr);
+
+ /* pack all pixels to byte boundaires */
+
+ png_set_packing(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+
+ lines = (unsigned char **)malloc(h * sizeof(unsigned char *));
+ if (!lines)
+ {
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ return qimage;
+ }
+
+ if (has_grey)
+ {
+ png_set_gray_to_rgb(png_ptr);
+ if (png_get_bit_depth(png_ptr, info_ptr) < 8)
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+
+ int sizeOfUint = sizeof(unsigned int);
+ for (i = 0 ; i < h ; i++)
+ lines[i] = ((unsigned char *)(qimage.bits())) + (i * w * sizeOfUint);
+
+ png_read_image(png_ptr, lines);
+ free(lines);
+
+ png_textp text_ptr;
+ int num_text=0;
+ png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
+ while (num_text--)
+ {
+ qimage.setText(text_ptr->key,0,text_ptr->text);
+ text_ptr++;
+ }
+
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+
+ return qimage;
+}
+
+// -- Load using DImg ---------------------------------------------------------------------
+
+bool tdeio_digikamthumbnailProtocol::loadDImg(TQImage& image, const TQString& path)
+{
+ Digikam::DImg dimg_im;
+
+ // to disable raw loader - does not work from ioslave
+ dimg_im.setAttribute("noeventloop", true);
+
+ if (!dimg_im.load(path))
+ {
+ return false;
+ }
+
+ image = dimg_im.copyTQImage();
+ org_width_ = image.width();
+ org_height_ = image.height();
+
+ if ( TQMAX(org_width_, org_height_) != cachedSize_ )
+ {
+ TQSize sz(dimg_im.width(), dimg_im.height());
+ sz.scale(cachedSize_, cachedSize_, TQSize::ScaleMin);
+ image.scale(sz.width(), sz.height());
+ }
+
+ new_width_ = image.width();
+ new_height_ = image.height();
+
+ image.setAlphaBuffer(true) ;
+
+ return true;
+}
+
+// -- Load using KDE API ---------------------------------------------------------------------
+
+bool tdeio_digikamthumbnailProtocol::loadKDEThumbCreator(TQImage& image, const TQString& path)
+{
+ // this sucks royally. some of the thumbcreators need an instance of
+ // app running so that they can use pixmap. till they get their
+ // code fixed, we will have to create a qapp instance.
+ if (!app_)
+ app_ = new TQApplication(argc_, argv_);
+
+ TQString mimeType = KMimeType::findByURL(path)->name();
+ if (mimeType.isEmpty())
+ {
+ kdDebug() << "Mimetype not found" << endl;
+ return false;
+ }
+
+ TQString mimeTypeAlt = mimeType.replace(TQRegExp("/.*"), "/*");
+
+ TQString plugin;
+
+ TDETrader::OfferList plugins = TDETrader::self()->query("ThumbCreator");
+ for (TDETrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ {
+ TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
+ for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
+ {
+ if ((*mt) == mimeType || (*mt) == mimeTypeAlt)
+ {
+ plugin=(*it)->library();
+ break;
+ }
+ }
+
+ if (!plugin.isEmpty())
+ break;
+ }
+
+ if (plugin.isEmpty())
+ {
+ kdDebug() << "No relevant plugin found " << endl;
+ return false;
+ }
+
+ KLibrary *library = KLibLoader::self()->library(TQFile::encodeName(plugin));
+ if (!library)
+ {
+ kdDebug() << "Plugin library not found " << plugin << endl;
+ return false;
+ }
+
+ ThumbCreator *creator = 0;
+ newCreator create = (newCreator)library->symbol("new_creator");
+ if (create)
+ creator = create();
+
+ if (!creator)
+ {
+ kdDebug() << "Cannot load ThumbCreator " << plugin << endl;
+ return false;
+ }
+
+ if (!creator->create(path, cachedSize_, cachedSize_, image))
+ {
+ kdDebug() << "Cannot create thumbnail for " << path << endl;
+ delete creator;
+ return false;
+ }
+
+ delete creator;
+ return true;
+}
+
+void tdeio_digikamthumbnailProtocol::createThumbnailDirs()
+{
+ TQString path = TQDir::homeDirPath() + "/.thumbnails/";
+
+ smallThumbPath_ = path + "normal/";
+ bigThumbPath_ = path + "large/";
+
+ TDEStandardDirs::makeDir(smallThumbPath_, 0700);
+ TDEStandardDirs::makeDir(bigThumbPath_, 0700);
+}
+
+// -- TDEIO slave registration ---------------------------------------------------------------------
+
+extern "C"
+{
+ DIGIKAM_EXPORT int kdemain(int argc, char **argv)
+ {
+ TDELocale::setMainCatalogue("digikam");
+ TDEInstance instance( "tdeio_digikamthumbnail" );
+ ( void ) TDEGlobal::locale();
+
+ if (argc != 4)
+ {
+ kdDebug() << "Usage: tdeio_digikamthumbnail protocol domain-socket1 domain-socket2"
+ << endl;
+ exit(-1);
+ }
+
+ KImageIO::registerFormats();
+
+ tdeio_digikamthumbnailProtocol slave(argc, argv);
+ slave.dispatchLoop();
+
+ return 0;
+ }
+}