You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libfm-qt-packaging/src/thumbnailloader.cpp

200 lines
7.1 KiB

/*
* Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "thumbnailloader.h"
#include <new>
#include <QByteArray>
#include <QScopedArrayPointer>
namespace Fm {
// FmQImageWrapper is a GObject used to wrap QImage objects and use in glib-based libfm
#define FM_TYPE_QIMAGE_WRAPPER (fm_qimage_wrapper_get_type())
#define FM_QIMAGE_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapper))
#define FM_QIMAGE_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass))
#define FM_IS_QIMAGE_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
FM_TYPE_QIMAGE_WRAPPER))
#define FM_IS_QIMAGE_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
FM_TYPE_QIMAGE_WRAPPER))
#define FM_QIMAGE_WRAPPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),\
FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass))
typedef struct _FmQImageWrapper FmQImageWrapper;
typedef struct _FmQImageWrapperClass FmQImageWrapperClass;
struct _FmQImageWrapper {
GObject parent;
QImage image;
};
struct _FmQImageWrapperClass {
GObjectClass parent_class;
};
GType fm_qimage_wrapper_get_type(void);
GObject* fm_qimage_wrapper_new(void);
static void fm_qimage_wrapper_finalize(GObject *self);
G_DEFINE_TYPE(FmQImageWrapper, fm_qimage_wrapper, G_TYPE_OBJECT)
static void fm_qimage_wrapper_class_init(FmQImageWrapperClass *klass) {
GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->finalize = fm_qimage_wrapper_finalize;
}
static void fm_qimage_wrapper_init(FmQImageWrapper *self) {
// placement new for QImage
new(&self->image) QImage();
}
static void fm_qimage_wrapper_finalize(GObject *self) {
FmQImageWrapper *wrapper = FM_QIMAGE_WRAPPER(self);
// placement delete
wrapper->image.~QImage();
}
GObject *fm_qimage_wrapper_new(QImage& image) {
FmQImageWrapper *wrapper = (FmQImageWrapper*)g_object_new(FM_TYPE_QIMAGE_WRAPPER, NULL);
wrapper->image = image;
return (GObject*)wrapper;
}
ThumbnailLoader* ThumbnailLoader::theThumbnailLoader = NULL;
bool ThumbnailLoader::localFilesOnly_ = true;
int ThumbnailLoader::maxThumbnailFileSize_ = 0;
ThumbnailLoader::ThumbnailLoader() {
// apply the settings to libfm
fm_config->thumbnail_local = localFilesOnly_;
fm_config->thumbnail_max = maxThumbnailFileSize_;
FmThumbnailLoaderBackend qt_backend = {
readImageFromFile,
readImageFromStream,
writeImage,
scaleImage,
rotateImage,
getImageWidth,
getImageHeight,
getImageText,
setImageText
};
fm_thumbnail_loader_set_backend(&qt_backend);
}
ThumbnailLoader::~ThumbnailLoader() {
}
GObject* ThumbnailLoader::readImageFromFile(const char* filename) {
QImage image;
image.load(QString(filename));
// qDebug("readImageFromFile: %s, %d", filename, image.isNull());
return image.isNull() ? NULL : fm_qimage_wrapper_new(image);
}
GObject* ThumbnailLoader::readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable) {
// qDebug("readImageFromStream: %p, %llu", stream, len);
// FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory.
QScopedArrayPointer<unsigned char> buffer(new unsigned char[len]); // allocate enough buffer
unsigned char* pbuffer = buffer.data();
unsigned int totalReadSize = 0;
while(!g_cancellable_is_cancelled(cancellable) && totalReadSize < len) {
int bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096;
gssize readSize = g_input_stream_read(stream, pbuffer, bytesToRead, cancellable, NULL);
if(readSize == 0) // end of file
break;
else if(readSize == -1) // error
return NULL;
totalReadSize += readSize;
pbuffer += readSize;
}
QImage image;
image.loadFromData(buffer.data(), totalReadSize);
return image.isNull() ? NULL : fm_qimage_wrapper_new(image);
}
gboolean ThumbnailLoader::writeImage(GObject* image, const char* filename) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
if(wrapper == NULL || wrapper->image.isNull())
return FALSE;
return (gboolean)wrapper->image.save(filename, "PNG");
}
GObject* ThumbnailLoader::scaleImage(GObject* ori_pix, int new_width, int new_height) {
// qDebug("scaleImage: %d, %d", new_width, new_height);
FmQImageWrapper* ori_wrapper = FM_QIMAGE_WRAPPER(ori_pix);
QImage scaled = ori_wrapper->image.scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
return scaled.isNull() ? NULL : fm_qimage_wrapper_new(scaled);
}
GObject* ThumbnailLoader::rotateImage(GObject* image, int degree) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
// degree values are 0, 90, 180, and 270 counterclockwise.
// In Qt, QMatrix does rotation counterclockwise as well.
// However, because the y axis of widget coordinate system is downward,
// the real effect of the coordinate transformation becomes clockwise rotation.
// So we need to use (360 - degree) here.
// Quote from QMatrix API doc:
// Note that if you apply a QMatrix to a point defined in widget
// coordinates, the direction of the rotation will be clockwise because
// the y-axis points downwards.
QImage rotated = wrapper->image.transformed(QMatrix().rotate(360 - degree));
return rotated.isNull() ? NULL : fm_qimage_wrapper_new(rotated);
}
int ThumbnailLoader::getImageWidth(GObject* image) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
return wrapper->image.width();
}
int ThumbnailLoader::getImageHeight(GObject* image) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
return wrapper->image.height();
}
char* ThumbnailLoader::getImageText(GObject* image, const char* key) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
QByteArray text = wrapper->image.text(key).toLatin1();
return (char*)g_memdup(text.constData(), text.length());
}
gboolean ThumbnailLoader::setImageText(GObject* image, const char* key, const char* val) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image);
// NOTE: we might receive image=NULL sometimes with older versions of libfm.
if(Q_LIKELY(wrapper != NULL)) {
wrapper->image.setText(key, val);
}
return TRUE;
}
QImage ThumbnailLoader::image(FmThumbnailLoader* result) {
FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(fm_thumbnail_loader_get_data(result));
if(wrapper) {
return wrapper->image;
}
return QImage();
}
} // namespace Fm