#ifndef __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#define __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#include <QItemDelegate>
#include <QTimer>
class ModelIndicesDeferredPainter;
// If an item is a byte array, wait 100 milliseconds, then load it as an image if possible.
{
Q_OBJECT
public:
// This is needed because you can't emit events from paint() since paint() is const
void SetDeferredPainter(ModelIndicesDeferredPainter *df) {deferredPainter=df;}
signals:
void paintingDeferred(const QModelIndex& index);
protected:
ModelIndicesDeferredPainter *deferredPainter;
};
class ModelIndicesDeferredPainter
: public QObject{
Q_OBJECT
public:
bool paintLater(const QModelIndex& index);
protected slots:
void paintPendingIndexes();
protected:
QModelIndexList indicesToPaint;
// Don't immediately load images. This way, if an image immediately goes out of viewable area, it is not drawn
// This is needed so we know not to just call paintLater() again from ByteArrayAsImageItemDelegate
bool isPaintingLater;
};
#endif
#ifndef __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#define __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#include <QItemDelegate>
#include <QTimer>
class ModelIndicesDeferredPainter;
// If an item is a byte array, wait 100 milliseconds, then load it as an image if possible.
class ByteArrayAsImageItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
ByteArrayAsImageItemDelegate(QWidget *parent = 0) : QItemDelegate(parent) {}
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
// This is needed because you can't emit events from paint() since paint() is const
void SetDeferredPainter(ModelIndicesDeferredPainter *df) {deferredPainter=df;}
signals:
void paintingDeferred(const QModelIndex& index);
protected:
ModelIndicesDeferredPainter *deferredPainter;
};
class ModelIndicesDeferredPainter : public QObject
{
Q_OBJECT
public:
ModelIndicesDeferredPainter(QAbstractItemView *view, QObject *parent);
bool paintLater(const QModelIndex& index);
protected slots:
void paintPendingIndexes();
protected:
QModelIndexList indicesToPaint;
QAbstractItemView *storedItemView;
// Don't immediately load images. This way, if an image immediately goes out of viewable area, it is not drawn
QTimer timer;
// This is needed so we know not to just call paintLater() again from ByteArrayAsImageItemDelegate
bool isPaintingLater;
};
#endif
To copy to clipboard, switch view to plain text mode
#include "ByteArrayAsImageItemDelegate.h"
#include <QPainter>
#include <QBuffer>
#include <QImageReader>
#include <QAbstractItemView>
#include <QPixmapCache>
// From RakNet
#include "SuperFastHash.h"
{
{
unsigned int hash = SuperFastHash(ba.data(),ba.length());
// Safer collision code commented out, in case I need it
//hashKey.sprintf("%i%i%i",index.row(),index.column(),hash);
// Faster, in case images are duplicated
hashKey.sprintf("%i",hash);
bool paintLater;
if (foundPixmap==false)
{
paintLater=deferredPainter->paintLater(index);
if (paintLater==false)
{
// Load pixmap
buffer.setBuffer(&ba);
qir.setDevice(&buffer);
if (qir.read(&image))
{
pixmap
= QPixmap::fromImage(image
);
// Save to cache
}
else
{
// Not an image?
return;
}
}
}
if (foundPixmap==true || paintLater==false)
{
// Paint
QSize imageSize
= pixmap.
size();
float scaleWidth, scaleHeight;
float drawWidth, drawHeight;
scaleWidth = (float) imageSize.width() / (float)option.rect.width();
scaleHeight = (float)imageSize.height() / (float)option.rect.height();
// Downscale by the larger of the two proportions
if (scaleHeight > scaleWidth)
{
drawWidth=(float)imageSize.width()/scaleHeight;
drawHeight=(float)imageSize.height()/scaleHeight;
}
else
{
drawWidth=(float)imageSize.width()/scaleWidth;
drawHeight=(float)imageSize.height()/scaleWidth;
}
float centerX = (float)option.rect.left() + (float)option.rect.width()/2.0;
float centerY = (float)option.rect.top() + (float)option.rect.height()/2.0;
drawRect.setLeft(centerX-drawWidth/2.0);
drawRect.setRight(centerX+drawWidth/2.0);
drawRect.setTop(centerY-drawHeight/2.0);
drawRect.setBottom(centerY+drawHeight/2.0);
painter->drawPixmap(drawRect, pixmap);
}
}
else
{
}
}
{
storedItemView = view;
timer.setSingleShot(true);
timer.setInterval(100);
connect(&timer, SIGNAL(timeout()),
this, SLOT(paintPendingIndexes()));
isPaintingLater=false;
}
bool ModelIndicesDeferredPainter::paintLater(const QModelIndex& index)
{
if (isPaintingLater)
return false;
if (indicesToPaint.contains(index)==false)
{
indicesToPaint.append(index);
timer.start();
}
return true;
}
void ModelIndicesDeferredPainter::paintPendingIndexes()
{
// This flag is needed so ByteArrayAsImageItemDelegate::paint does not just call paintLater again()
isPaintingLater=true;
foreach(const QModelIndex& index, indicesToPaint)
storedItemView->update(index);
storedItemView->repaint();
isPaintingLater=false;
indicesToPaint.clear();
}
#include "ByteArrayAsImageItemDelegate.h"
#include <QPainter>
#include <QBuffer>
#include <QImageReader>
#include <QAbstractItemView>
#include <QPixmapCache>
// From RakNet
#include "SuperFastHash.h"
void ByteArrayAsImageItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
const QVariant &v = index.data();
if (v.type()==QVariant::ByteArray)
{
QByteArray ba = v.toByteArray();
unsigned int hash = SuperFastHash(ba.data(),ba.length());
QString hashKey;
// Safer collision code commented out, in case I need it
//hashKey.sprintf("%i%i%i",index.row(),index.column(),hash);
// Faster, in case images are duplicated
hashKey.sprintf("%i",hash);
QPixmap pixmap;
bool foundPixmap = QPixmapCache::find(hashKey, pixmap);
bool paintLater;
if (foundPixmap==false)
{
paintLater=deferredPainter->paintLater(index);
if (paintLater==false)
{
// Load pixmap
QBuffer buffer;
buffer.setBuffer(&ba);
buffer.open(QIODevice::ReadOnly);
QImageReader qir;
qir.setDevice(&buffer);
QImage image;
if (qir.read(&image))
{
pixmap = QPixmap::fromImage(image);
// Save to cache
QPixmapCache::insert(hashKey,pixmap);
}
else
{
// Not an image?
QItemDelegate::paint(painter,option,index);
return;
}
}
}
if (foundPixmap==true || paintLater==false)
{
// Paint
QSize imageSize = pixmap.size();
QRect drawRect;
float scaleWidth, scaleHeight;
float drawWidth, drawHeight;
scaleWidth = (float) imageSize.width() / (float)option.rect.width();
scaleHeight = (float)imageSize.height() / (float)option.rect.height();
// Downscale by the larger of the two proportions
if (scaleHeight > scaleWidth)
{
drawWidth=(float)imageSize.width()/scaleHeight;
drawHeight=(float)imageSize.height()/scaleHeight;
}
else
{
drawWidth=(float)imageSize.width()/scaleWidth;
drawHeight=(float)imageSize.height()/scaleWidth;
}
float centerX = (float)option.rect.left() + (float)option.rect.width()/2.0;
float centerY = (float)option.rect.top() + (float)option.rect.height()/2.0;
drawRect.setLeft(centerX-drawWidth/2.0);
drawRect.setRight(centerX+drawWidth/2.0);
drawRect.setTop(centerY-drawHeight/2.0);
drawRect.setBottom(centerY+drawHeight/2.0);
painter->drawPixmap(drawRect, pixmap);
}
}
else
{
QItemDelegate::paint(painter,option,index);
}
}
ModelIndicesDeferredPainter::ModelIndicesDeferredPainter(QAbstractItemView *view, QObject *parent)
: QObject(parent)
{
storedItemView = view;
timer.setSingleShot(true);
timer.setInterval(100);
connect(&timer, SIGNAL(timeout()),
this, SLOT(paintPendingIndexes()));
isPaintingLater=false;
}
bool ModelIndicesDeferredPainter::paintLater(const QModelIndex& index)
{
if (isPaintingLater)
return false;
if (indicesToPaint.contains(index)==false)
{
indicesToPaint.append(index);
timer.start();
}
return true;
}
void ModelIndicesDeferredPainter::paintPendingIndexes()
{
// This flag is needed so ByteArrayAsImageItemDelegate::paint does not just call paintLater again()
isPaintingLater=true;
foreach(const QModelIndex& index, indicesToPaint)
storedItemView->update(index);
storedItemView->repaint();
isPaintingLater=false;
indicesToPaint.clear();
}
To copy to clipboard, switch view to plain text mode
Bookmarks