Commit 2a1987a8 authored by Eugene Ingerman's avatar Eugene Ingerman

Add antialiasing to createThumbnailDevice

Summary:
1. Added oversampling/antialising to createThumbnailDevice. Interpolation is done using KisTransformWorker with bilinear interpolation. Added parameter that controls oversampling ratio.
2. Added benchmark for new thumbnail code. Results for 6Kx8K image turned into 640px thumbnail see below. About 2x hit in time for 2x oversampling. Quality with 2x oversampling is much better than no oversampling. 4x oversampling slightly better, but not dramatically. See below.
3. Changed oversampling for overview widget to 2x.
4. Fixed caching of oversampled thumbnails.
5. Fixed up functions calls to createThumbnail.

{F156583}
No Oversampling

{F156585}
2x Oversampling

{F156586}
4x Oversampling

PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnail()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnail():
     161 msecs per iteration (total: 161, iterations: 1)
PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnailCached()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnailCached():
     0.000059 msecs per iteration (total: 62, iterations: 1048576)
PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQ()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQ():
     3,962 msecs per iteration (total: 3,962, iterations: 1)
PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample2x()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample2x():
     269 msecs per iteration (total: 269, iterations: 1)
PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample3x()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample3x():
     489 msecs per iteration (total: 489, iterations: 1)
PASS   : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample4x()
RESULT : KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample4x():
     701 msecs per iteration (total: 701, iterations: 1)

Test Plan: Run KisThumbnailBenchmark.

Reviewers: rempt, dkazakov, woltherav

Reviewed By: rempt, woltherav

Subscribers: woltherav

Differential Revision: https://phabricator.kde.org/D1979
parent 2b0c5372
......@@ -38,6 +38,7 @@ set(kis_mask_generator_benchmark_SRCS kis_mask_generator_benchmark.cpp)
set(kis_low_memory_benchmark_SRCS kis_low_memory_benchmark.cpp)
set(kis_filter_selections_benchmark_SRCS kis_filter_selections_benchmark.cpp)
set(kis_composition_benchmark_SRCS kis_composition_benchmark.cpp)
set(kis_thumbnail_benchmark_SRCS kis_thumbnail_benchmark.cpp)
krita_add_benchmark(KisDatamanagerBenchmark TESTNAME krita-benchmarks-KisDataManager ${kis_datamanager_benchmark_SRCS})
krita_add_benchmark(KisHLineIteratorBenchmark TESTNAME krita-benchmarks-KisHLineIterator ${kis_hiterator_benchmark_SRCS})
......@@ -56,6 +57,7 @@ krita_add_benchmark(KisMaskGeneratorBenchmark TESTNAME krita-benchmarks-KisMaskG
krita_add_benchmark(KisLowMemoryBenchmark TESTNAME krita-benchmarks-KisLowMemory ${kis_low_memory_benchmark_SRCS})
krita_add_benchmark(KisFilterSelectionsBenchmark TESTNAME krita-image-KisFilterSelectionsBenchmark ${kis_filter_selections_benchmark_SRCS})
krita_add_benchmark(KisCompositionBenchmark TESTNAME krita-benchmarks-KisComposition ${kis_composition_benchmark_SRCS})
krita_add_benchmark(KisThumbnailBenchmark TESTNAME krita-benchmarks-KisThumbnail ${kis_thumbnail_benchmark_SRCS})
target_link_libraries(KisDatamanagerBenchmark kritaimage Qt5::Test)
target_link_libraries(KisHLineIteratorBenchmark kritaimage Qt5::Test)
......@@ -77,3 +79,5 @@ if(HAVE_VC)
set_property(TARGET KisCompositionBenchmark APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}")
endif()
target_link_libraries(KisMaskGeneratorBenchmark kritaimage Qt5::Test)
target_link_libraries(KisThumbnailBenchmark kritaimage Qt5::Test)
/*
* Copyright (c) 2016 Eugene Ingerman geneing 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 of the License, 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_thumbnail_benchmark.h"
#include "kis_benchmark_values.h"
#include <QTest>
#include <QImage>
#include "kis_iterator_ng.h"
#include "kis_paint_device.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoCompositeOpRegistry.h"
#include "KoColor.h"
#include "kis_image.h"
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_sequential_iterator.h"
#include "kis_transform_worker.h"
#include <cmath>
#define SAVE_OUTPUT
const int THUMBNAIL_WIDTH = 64;
const int THUMBNAIL_HEIGHT = 64;
const int IMAGE_WIDTH = 8000;
const int IMAGE_HEIGHT = 6000;
const int OVERSAMPLE = 4;
void KisThumbnailBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_dev = new KisPaintDevice(m_colorSpace);
KoColor color(m_colorSpace);
color.fromQColor(Qt::white);
m_dev->clear();
m_dev->fill(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, color.data());
color.fromQColor(Qt::black);
KisPainter painter(m_dev);
painter.setPaintColor(color);
float radius = std::min(IMAGE_WIDTH, IMAGE_HEIGHT);
const float angle = 2 * 3.1415926 / 360.;
const float endWidth = 30;
for (float i = 0; i < 90; i += 5) {
painter.drawThickLine(QPointF(0, 0), QPointF(radius * std::sin(angle * i), radius * std::cos(angle * i)), 1, endWidth);
painter.drawThickLine(QPointF(IMAGE_WIDTH, IMAGE_HEIGHT),
QPointF(IMAGE_WIDTH - radius * std::sin(angle * i), IMAGE_HEIGHT - radius * std::cos(angle * i)), 1, endWidth);
}
#ifdef SAVE_OUTPUT
m_dev->convertToQImage(m_colorSpace->profile()).save("ThumbFullImage.png");
#endif
}
void KisThumbnailBenchmark::cleanupTestCase()
{
}
void KisThumbnailBenchmark::benchmarkCreateThumbnail()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect() );
//m_dev->setDirty();
}
image.save("createThumbnail.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailCached()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, 2. );
}
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQ()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(OVERSAMPLE * THUMBNAIL_WIDTH, OVERSAMPLE * THUMBNAIL_HEIGHT);
image = image.scaled(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_dev->setDirty();
}
image.save("createThumbnailHiQ.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample2x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 2,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample2x.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample3x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 3,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample3x.png");
}
void KisThumbnailBenchmark::benchmarkCreateThumbnailHiQcreateThumbOversample4x()
{
QImage image;
QBENCHMARK{
image = m_dev->createThumbnail(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QRect(), 4,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
m_dev->setDirty();
}
image.save("createThumbnailHiQcreateThumbOversample4x.png");
}
QTEST_MAIN(KisThumbnailBenchmark)
/*
* Copyright (c) 2016 Eugene Ingerman geneing 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 of the License, 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_THUMBNAIL_BENCHMARK_H
#define KIS_THUMBNAIL_BENCHMARK_H
#include <QtTest>
#include "kis_paint_device.h"
class KoColor;
class KoColorSpace;
class KisThumbnailBenchmark : public QObject
{
Q_OBJECT
private:
const KoColorSpace * m_colorSpace;
KisPaintDeviceSP m_dev;
QVector<QImage> m_thumbnails;
QSize m_thumbnailSizeLimit;
int m_oversampleRatio;
int m_skipCount;
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void benchmarkCreateThumbnail();
void benchmarkCreateThumbnailCached();
void benchmarkCreateThumbnailHiQ();
void benchmarkCreateThumbnailHiQcreateThumbOversample2x();
void benchmarkCreateThumbnailHiQcreateThumbOversample3x();
void benchmarkCreateThumbnailHiQcreateThumbOversample4x();
};
#endif
......@@ -48,8 +48,8 @@ struct KisIdleWatcher::Private
};
KisIdleWatcher::KisIdleWatcher(int delay)
: m_d(new Private(delay))
KisIdleWatcher::KisIdleWatcher(int delay, QObject *parent)
: QObject(parent), m_d(new Private(delay))
{
connect(&m_d->imageModifiedCompressor, SIGNAL(timeout()), SLOT(startIdleCheck()));
connect(&m_d->idleCheckTimer, SIGNAL(timeout()), SLOT(slotIdleCheckTick()));
......
......@@ -32,7 +32,7 @@ class KRITAIMAGE_EXPORT KisIdleWatcher : public QObject
{
Q_OBJECT
public:
KisIdleWatcher(int delay);
KisIdleWatcher(int delay, QObject* parent = 0);
~KisIdleWatcher();
bool isIdle() const;
......@@ -40,6 +40,8 @@ public:
void setTrackedImages(const QVector<KisImageSP> &images);
void setTrackedImage(KisImageSP image);
//Force to image modified state and start countdown to event
void startCountdown(void) { slotImageModified(); }
Q_SIGNALS:
void startedIdleMode();
......
......@@ -788,7 +788,7 @@ QImage KisLayer::createThumbnail(qint32 w, qint32 h)
KisPaintDeviceSP originalDevice = original();
return originalDevice ?
originalDevice->createThumbnail(w, h,
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) : QImage();
}
......
......@@ -343,7 +343,7 @@ QImage KisMask::createThumbnail(qint32 w, qint32 h)
selection() ? selection()->projection() : 0;
return originalDevice ?
originalDevice->createThumbnail(w, h,
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) : QImage();
}
......
This diff is collapsed.
......@@ -526,7 +526,8 @@ public:
* @param rect: only this rect will be used for the thumbnail
*
*/
KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect()) const;
KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect(), QRect outputRect = QRect()) const;
KisPaintDeviceSP createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect = QRect(), QRect outputRect = QRect()) const;
/**
* Creates a thumbnail of the paint device, retaining the aspect ratio.
......@@ -536,15 +537,16 @@ public:
* @param maxw: maximum width
* @param maxh: maximum height
* @param rect: only this rect will be used for the thumbnail
* @param oversample: ratio used for antialiasing
*/
QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect,
QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, qreal oversample = 1,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
/**
* Cached version of createThumbnail(qint32 maxw, qint32 maxh, const KisSelection *selection, QRect rect)
*/
QImage createThumbnail(qint32 maxw, qint32 maxh,
QImage createThumbnail(qint32 maxw, qint32 maxh, qreal oversample = 1,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
......@@ -796,8 +798,7 @@ public:
QRect calculateExactBounds(bool nonDefaultOnly) const;
public:
struct MemoryReleaseObject : public QObject
{
struct MemoryReleaseObject : public QObject {
~MemoryReleaseObject();
};
......
......@@ -82,11 +82,11 @@ public:
return m_regionCache.getValue();
}
QImage createThumbnail(qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
QImage createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) {
QImage thumbnail;
if(m_thumbnailsValid) {
thumbnail = findThumbnail(w, h);
thumbnail = findThumbnail(w, h, oversample);
}
else {
m_thumbnails.clear();
......@@ -94,8 +94,8 @@ public:
}
if(thumbnail.isNull()) {
thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), renderingIntent, conversionFlags);
cacheThumbnail(w, h, thumbnail);
thumbnail = m_paintDevice->createThumbnail(w, h, QRect(), oversample, renderingIntent, conversionFlags);
cacheThumbnail(w, h, oversample, thumbnail);
}
Q_ASSERT(!thumbnail.isNull() || m_paintDevice->extent().isEmpty());
......@@ -103,16 +103,16 @@ public:
}
private:
inline QImage findThumbnail(qint32 w, qint32 h) {
inline QImage findThumbnail(qint32 w, qint32 h, qreal oversample) {
QImage resultImage;
if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h)) {
resultImage = m_thumbnails[w][h];
if (m_thumbnails.contains(w) && m_thumbnails[w].contains(h) && m_thumbnails[w][h].contains(oversample)) {
resultImage = m_thumbnails[w][h][oversample];
}
return resultImage;
}
inline void cacheThumbnail(qint32 w, qint32 h, QImage image) {
m_thumbnails[w][h] = image;
inline void cacheThumbnail(qint32 w, qint32 h, qreal oversample, QImage image) {
m_thumbnails[w][h][oversample] = image;
}
private:
......@@ -153,7 +153,7 @@ private:
RegionCache m_regionCache;
bool m_thumbnailsValid;
QMap<int, QMap<int, QImage> > m_thumbnails;
QMap<int, QMap<int, QMap<qreal,QImage> > > m_thumbnails;
};
#endif /* __KIS_PAINT_DEVICE_CACHE_H */
......@@ -285,7 +285,7 @@ QImage KisSelectionBasedLayer::createThumbnail(qint32 w, qint32 h)
KisPaintDeviceSP originalDevice = original();
return originalDevice && originalSelection ?
originalDevice->createThumbnail(w, h,
originalDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()) :
QImage();
......
......@@ -224,7 +224,7 @@ void KisUpdateTimeMonitor::reportJobFinished(void *key, const QVector<QRect> &re
QMutexLocker locker(&m_d->mutex);
StrokeTicket *ticket = m_d->preliminaryTickets.take(key);
if (ticket) {
if( ticket ){
ticket->jobCompleted();
Q_FOREACH (const QRect &rect, rects) {
......
......@@ -167,7 +167,7 @@ void KisCustomPattern::createPattern()
}
QString dir = KoResourceServerProvider::instance()->patternServer()->saveLocation();
m_pattern = new KoPattern(dev->createThumbnail(size.width(), size.height(), rc,
m_pattern = new KoPattern(dev->createThumbnail(size.width(), size.height(), rc, /*oversample*/ 1,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags()), name, dir);
}
......
......@@ -21,4 +21,4 @@ add_subdirectory(colorslider)
add_subdirectory(animation)
add_subdirectory(presethistory)
add_subdirectory(shapedockers)
#add_subdirectory(histogram)
add_subdirectory(histogram)
......@@ -131,7 +131,7 @@ void KisCommonColors::recalculate()
KisImageWSP kisImage = m_canvas->image();
QImage image = kisImage->projection()->createThumbnail(1024, 1024, kisImage->bounds(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QImage image = kisImage->projection()->createThumbnail(1024, 1024, kisImage->bounds(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
KisCommonColorsRecalculationRunner* runner = new KisCommonColorsRecalculationRunner(image, patchCount(), this);
QThreadPool::globalInstance()->start(runner);
......
......@@ -32,37 +32,90 @@
#include <kis_group_layer.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include "kis_signal_compressor.h"
#include <KisView.h>
#include <kis_idle_watcher.h>
ChannelDockerDock::ChannelDockerDock( ) : QDockWidget(i18n("Channels")), m_canvas(0)
ChannelDockerDock::ChannelDockerDock( ) :
QDockWidget(i18n("Channels")),
m_imageIdleWatcher(new KisIdleWatcher(250, this)),
m_canvas(0)
{
m_channelTable = new QTableView(this);
m_model = new ChannelModel(this);
m_channelTable->setModel(m_model);
m_channelTable->setShowGrid(false);
m_channelTable->horizontalHeader()->setStretchLastSection(true);
m_channelTable->verticalHeader()->setVisible(false);
m_channelTable->horizontalHeader()->setVisible(false);
m_channelTable->setSelectionBehavior( QAbstractItemView::SelectRows );
setWidget(m_channelTable);
connect(m_channelTable,&QTableView::activated, m_model, &ChannelModel::rowActivated);
}
void ChannelDockerDock::setCanvas(KoCanvasBase * canvas)
{
if(m_canvas == canvas)
return;
setEnabled(canvas != 0);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_canvas->image()->disconnect(this);
}
if (!canvas) {
m_canvas = 0;
return;
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (m_canvas && m_canvas->imageView() && m_canvas->imageView()->image()) {
QPointer<KisView> view = m_canvas->imageView();
m_model->slotLayerActivated(view->image()->rootLayer());
KisPaintDeviceSP dev = view->image()->projection();
if ( m_canvas && m_canvas->image() ) {
m_model->slotSetCanvas(m_canvas);
KisPaintDeviceSP dev = m_canvas->image()->projection();
m_imageIdleWatcher->setTrackedImage(m_canvas->image());
connect(m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &ChannelDockerDock::updateChannelTable);
connect(dev, SIGNAL(colorSpaceChanged(const KoColorSpace*)), m_model, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
connect(dev, SIGNAL(colorSpaceChanged(const KoColorSpace*)), m_canvas, SLOT(channelSelectionChanged()));
connect(m_model, SIGNAL(channelFlagsChanged()), m_canvas, SLOT(channelSelectionChanged()));
m_imageIdleWatcher->startCountdown();
}
connect(m_model, SIGNAL(channelFlagsChanged()), m_canvas, SLOT(channelSelectionChanged()));
}
void ChannelDockerDock::unsetCanvas()
{
setEnabled(false);
m_canvas = 0;
m_model->unsetCanvas();
}
void ChannelDockerDock::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
m_imageIdleWatcher->startCountdown();
}
void ChannelDockerDock::startUpdateCanvasProjection()
{
m_imageIdleWatcher->startCountdown();
}
void ChannelDockerDock::updateChannelTable()
{
if (isVisible() && m_canvas && m_canvas->image()){
m_model->updateData(m_canvas);
m_channelTable->resizeRowsToContents();
m_channelTable->resizeColumnsToContents();
}
}
......@@ -24,15 +24,28 @@
class ChannelModel;
class QTableView;
class KisCanvas2;
class KisSignalCompressor;
class KisIdleWatcher;
class ChannelDockerDock : public QDockWidget, public KoCanvasObserverBase {
Q_OBJECT
public:
ChannelDockerDock();
QString observerName() { return "ChannelDockerDock"; }
virtual void setCanvas(KoCanvasBase *canvas);
virtual void unsetCanvas() { m_canvas = 0; setEnabled(false);}
void setCanvas(KoCanvasBase *canvas);
void unsetCanvas();
void showEvent(QShowEvent *event);
public Q_SLOTS:
void startUpdateCanvasProjection();
private Q_SLOTS:
void updateChannelTable(void);
private:
KisIdleWatcher* m_imageIdleWatcher;
KisSignalCompressor *m_compressor;
KisCanvas2 *m_canvas;
QTableView *m_channelTable;
ChannelModel *m_model;
......
......@@ -16,14 +16,23 @@
*/
#include "channelmodel.h"
#include <QImage>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_group_layer.h>
#include <kis_paint_device.h>
#include <kis_iterator_ng.h>
#include <kis_default_bounds.h>
#include <kis_canvas2.h>
ChannelModel::ChannelModel(QObject* parent): QAbstractTableModel(parent), m_currentLayer(0)
ChannelModel::ChannelModel(QObject* parent):
QAbstractTableModel(parent),
m_canvas(nullptr), m_oversampleRatio(2), m_channelCount(0)
{
setThumbnailSizeLimit(QSize(64, 64));
}
ChannelModel::~ChannelModel()
......@@ -32,27 +41,36 @@ ChannelModel::~ChannelModel()
QVariant ChannelModel::data(const QModelIndex& index, int role) const
{
if (m_currentLayer.isValid() && index.isValid())
{
QList<KoChannelInfo*> channels = m_currentLayer->colorSpace()->channels();
int channelIndex = KoChannelInfo::displayPositionToChannelIndex(index.row(), channels);
if (m_canvas && index.isValid()) {
KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer();
const KoColorSpace* cs = rootLayer->colorSpace();
QList<KoChannelInfo*> channels = cs->channels();
int channelIndex = index.row();
switch (role) {
case Qt::DisplayRole:
{
case Qt::DisplayRole: {
if (index.column() == 2) {
return channels.at(channelIndex)->name();
}
return QVariant();
}
case Qt::DecorationRole: {
if (index.column() == 1) {
Q_ASSERT(m_thumbnails.count() > index.row());
return QVariant(m_thumbnails.at(index.row()));
}
return QVariant();
}
case Qt::CheckStateRole: {
Q_ASSERT(index.row() < rowCount());
Q_ASSERT(index.column() < columnCount());
if (index.column() == 0) {
QBitArray flags = m_currentLayer->channelFlags();
QBitArray flags = rootLayer->channelFlags();
return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Checked : Qt::Unchecked;
}
QBitArray flags = dynamic_cast<const KisPaintLayer*>(m_currentLayer.data())->channelLockFlags();
return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Unchecked : Qt::Checked;
return QVariant();
}
}
}
......@@ -61,82 +79,176 @@ QVariant ChannelModel::data(const QModelIndex& index, int role) const
QVariant ChannelModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
if(section == 0)
return i18n("Enabled");
return i18n("Locked");
}
return QAbstractItemModel::headerData(section, orientation, role);
Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(role);
return QVariant();
}
int ChannelModel::rowCount(const QModelIndex& /*parent*/) const
{
if (!m_currentLayer) return 0;
if (!m_canvas) return 0;
return m_currentLayer.isValid() ? m_currentLayer->colorSpace()->channelCount() : 0;
return m_channelCount;
}
int ChannelModel::columnCount(const QModelIndex& /*parent*/) const
{
if (!m_currentLayer) return 0;
if (!m_canvas) return 0;
return dynamic_cast<const KisPaintLayer*>(m_currentLayer.data()) ? 2 : 1;
//columns are: checkbox, thumbnail, channel name
return 3;
}
bool ChannelModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (m_currentLayer.isValid() && index.isValid())
{
QList<KoChannelInfo*> channels = m_currentLayer->colorSpace()->channels();
int channelIndex = KoChannelInfo::displayPositionToChannelIndex(index.row(), channels);
if (m_canvas && m_canvas->image()) {
KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer();
const KoColorSpace* cs = rootLayer->colorSpace();
QList<KoChannelInfo*> channels = cs->channels();
Q_ASSERT(index.row() <= channels.count());
if (role == Qt::CheckStateRole) {
Q_ASSERT(index.row() < rowCount());
Q_ASSERT(index.column() < columnCount());
int channelIndex = index.row();
if (index.column() == 0) {
QBitArray flags = m_currentLayer->channelFlags();
if (role == Qt::CheckStateRole) {
QBitArray flags = rootLayer->channelFlags();
flags = flags.isEmpty() ? cs->channelFlags(true, true) : flags;
Q_ASSERT(!flags.isEmpty());
flags = flags.isEmpty() ? m_currentLayer->colorSpace()->channelFlags(true, true) : flags;
flags.setBit(channelIndex, value.toInt() == Qt::Checked);
m_currentLayer->setChannelFlags(flags);
}
else { //if (index.column() == 1)
KisPaintLayer* paintLayer = dynamic_cast<KisPaintLayer*>(m_currentLayer.data());
QBitArray flags = paintLayer->channelLockFlags();
flags = flags.isEmpty() ? m_currentLayer