QImage NemoThumbnailProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    setupCache();

    // needed for stupid things like gallery model, which pass us a url
    if (id.startsWith("file://")) {
//        qWarning() << Q_FUNC_INFO << "Removing file:// prefix, before: " << id;
        QString &nid = const_cast<QString &>(id);
        nid = nid.remove(0, 7);
    }

    TDEBUG() << Q_FUNC_INFO << "Requested image: " << id << " with size " << requestedSize;

    // sourceSize should indicate what size thumbnail you want. i.e. if you want a 120x120px thumbnail,
    // set sourceSize: Qt.size(120, 120).
    if (!requestedSize.isValid())
        qFatal("You must request a sourceSize whenever you use nemoThumbnail");

    if (size)
        *size = requestedSize;

    QByteArray hashData = cacheKey(id, requestedSize);
    QImage img = attemptCachedServe(id, hashData);
    if (!img.isNull()) {
        TDEBUG() << Q_FUNC_INFO << "Read " << id << " from cache";
        return img;
    }

    return generateThumbnail(id, hashData, requestedSize);
}
QImage FileThumbnailImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    setupCache();

    // needed for stupid things like gallery model, which pass us a url
    if (id.startsWith("file://")) {
        qWarning() << Q_FUNC_INFO << "Removing file:// prefix, before: " << id;
        QString &nid = const_cast<QString &>(id);
        nid = nid.remove(0, 7);
    }

    qDebug() << Q_FUNC_INFO << "Requested image: " << id << " with size " << requestedSize;

    // sourceSize should indicate what size thumbnail you want. i.e. if you want a 120x120px thumbnail,
    // set sourceSize: Qt.size(120, 120).
    if (!requestedSize.isValid())
        qFatal("You must request a sourceSize whenever you use nemoThumbnail");

    if (size)
        *size = requestedSize;

    QByteArray hashData = cacheKey(id, requestedSize);
    QImage img = attemptCachedServe(hashData);
    if (!img.isNull()) {
        qDebug() << Q_FUNC_INFO << "Read " << id << " from cache";
        return img;
    }

    // cache couldn't satisfy us.
    // step 1: read source image in
    QImageReader ir(id);
    img = ir.read();

    // don't pollute the cache with false positives
    if (img.isNull())
        return QImage();

    if (img.size() == requestedSize)
        return img;

    // step 2: scale to target size
    if (img.height() == img.width()) {
        // in the case of a squared image, there's no need to crop
        img = img.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
    } else  if (img.height() >= requestedSize.height() && img.width() >= requestedSize.width()) {
        // if the image is larger than the desired size (on both dimensions)
        // then crop, and scale down
        if (img.width() < img.height()) {
            int cropPosition = (img.height() - img.width()) / 2;
            img = img.copy(0, cropPosition, img.width(), img.width());
            img = img.scaledToWidth(requestedSize.width(), Qt::SmoothTransformation);
        } else {
            int cropPosition = (img.width() - img.height()) / 2;
            img = img.copy(cropPosition, 0, img.height(), img.height());
            img = img.scaledToHeight(requestedSize.height(), Qt::SmoothTransformation);
        }
    } else if ((img.width() <= requestedSize.width() && img.height() >= requestedSize.height()) ||
               (img.width() >= requestedSize.width() && img.height() <= requestedSize.height())) {
        // if the image is smaller than the desired size on one dimension, scale it up,
        // then crop down to thumbnail size.
        if (img.width() <= requestedSize.width() && img.height() >= requestedSize.height()) {
            img = img.scaledToWidth(requestedSize.width(), Qt::SmoothTransformation);
            int cropPosition = (img.height() - img.width()) / 2;
            img = img.copy(0, cropPosition, img.width(), img.width());
        } else {
            img = img.scaledToHeight(requestedSize.height(), Qt::SmoothTransformation);
            int cropPosition = (img.width() - img.height()) / 2;
            img = img.copy(cropPosition, 0, img.height(), img.height());
        }
    } else {
        // if the image is smaller on both dimensions, scale it up, and use the requested
        // size to do the cropping
        if (img.width() < img.height()) {
            img = img.scaledToWidth(requestedSize.width(), Qt::SmoothTransformation);
            int cropPosition = (img.height() - requestedSize.height()) / 2;
            img = img.copy(0, cropPosition, img.width(), img.width());
        } else {
            img = img.scaledToHeight(requestedSize.height(), Qt::SmoothTransformation);
            int cropPosition = (img.width() - requestedSize.width()) / 2;
            img = img.copy(cropPosition, 0, img.height(), img.height());
        }
    }

    // step 3: write to cache for next time
    writeCacheFile(hashData, img);
    qDebug() << Q_FUNC_INFO << "Wrote " << id << " to cache";
    return img;
}