Commit 04d8cf6c authored by Agata Cacko's avatar Agata Cacko
Browse files

Fix multithreading in Histogram widget

Before this commit, Histogram widget would use a homebrew threads
to handle its multithreading.
This commit ensures that a proper stroke system will be used for that.
parent 6ddf4a12
......@@ -31,12 +31,155 @@
#include "kis_paint_device.h"
#include "KoColorSpace.h"
#include "kis_iterator_ng.h"
#include "krita_utils.h"
#include "kis_canvas2.h"
struct HistogramComputationStrokeStrategy::Private {
class ProcessData : public KisStrokeJobData
{
public:
ProcessData(QRect rect, int _jobId)
: KisStrokeJobData(CONCURRENT)
, rectToCalculate(rect)
, jobId(_jobId)
{}
QRect rectToCalculate;
int jobId; // id in the list of results
};
};
HistogramComputationStrokeStrategy::HistogramComputationStrokeStrategy(KisImageWSP image)
: KisSimpleStrokeStrategy(QLatin1String("ComputeHistogram")),
m_image(image)
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
setRequestsOtherStrokesToEnd(false);
setClearsRedoOnStart(false);
setCanForgetAboutMe(true);
}
HistogramComputationStrokeStrategy::~HistogramComputationStrokeStrategy()
{
}
void HistogramComputationStrokeStrategy::initStrokeCallback()
{
QVector<KisStrokeJobData*> jobsData;
int i = 0;
QVector<QRect> tileRects = KritaUtils::splitRectIntoPatches(m_image->bounds(), KritaUtils::optimalPatchSize());
m_results.resize(tileRects.size());
Q_FOREACH (const QRect &tileRectangle, tileRects) {
jobsData << new HistogramComputationStrokeStrategy::Private::ProcessData(tileRectangle, i);
i++;
}
addMutatedJobs(jobsData);
}
void HistogramComputationStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Private::ProcessData *d_pd = dynamic_cast<Private::ProcessData*>(data);
KIS_SAFE_ASSERT_RECOVER_RETURN(d_pd);
QRect calculate = d_pd->rectToCalculate;
KisPaintDeviceSP m_dev = m_image->projection();
QRect imageBounds = m_image->bounds();
const KoColorSpace *cs = m_dev->colorSpace();
quint32 channelCount = m_dev->channelCount();
quint32 pixelSize = m_dev->pixelSize();
quint32 imageSize = imageBounds.width() * imageBounds.height();
quint32 nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms
if (calculate.isEmpty())
return;
initiateVector(m_results[d_pd->jobId], cs);
quint32 toSkip = nSkip;
KisSequentialConstIterator it(m_dev, calculate);
int numConseqPixels = it.nConseqPixels();
while (it.nextPixels(numConseqPixels)) {
numConseqPixels = it.nConseqPixels();
const quint8* pixel = it.rawDataConst();
for (int k = 0; k < numConseqPixels; ++k) {
if (--toSkip == 0) {
for (int chan = 0; chan < (int)channelCount; ++chan) {
m_results[d_pd->jobId][chan][cs->scaleToU8(pixel, chan)]++;
}
toSkip = nSkip;
}
pixel += pixelSize;
}
}
}
void HistogramComputationStrokeStrategy::finishStrokeCallback()
{
if (!m_image) {
return;
}
HistogramData hisData;
hisData.colorSpace = m_image->projection()->colorSpace();
if (m_results.size() == 1) {
hisData.bins = m_results[0];
emit computationResultReady(hisData);
} else {
quint32 channelCount = m_image->projection()->channelCount();
initiateVector(hisData.bins, hisData.colorSpace);
for (int chan = 0; chan < (int)channelCount; chan++) {
int bsize = hisData.bins[chan].size();
for (int bi = 0; bi < bsize; bi++) {
hisData.bins[chan][bi] = 0;
for (int i = 0; i < (int)m_results.size(); i++) {
hisData.bins[chan][bi] += m_results[i][chan][bi];
}
}
}
emit computationResultReady(hisData);
}
}
void HistogramComputationStrokeStrategy::cancelStrokeCallback()
{
}
void HistogramComputationStrokeStrategy::initiateVector(HistVector &vec, const KoColorSpace *colorSpace)
{
int channelCount = colorSpace->channelCount();
vec.resize((int)channelCount);
for (auto &bin : vec) {
bin.resize(std::numeric_limits<quint8>::max() + 1);
}
}
HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f)
: QLabel(parent, f), m_colorSpace(0), m_smoothHistogram(true)
{
setObjectName(name);
qRegisterMetaType<HistogramData>();
}
HistogramDockerWidget::~HistogramDockerWidget()
......@@ -57,19 +200,31 @@ void HistogramDockerWidget::updateHistogram(KisCanvas2* canvas)
m_devClone->makeCloneFrom(paintDevice, bounds);
HistogramComputationThread *workerThread = new HistogramComputationThread(m_devClone, bounds);
connect(workerThread, &HistogramComputationThread::resultReady, this, &HistogramDockerWidget::receiveNewHistogram);
connect(workerThread, &HistogramComputationThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
HistogramComputationStrokeStrategy* stroke;
stroke = new HistogramComputationStrokeStrategy(canvas->image());
connect(stroke, SIGNAL(computationResultReady(HistogramData)), this, SLOT(receiveNewHistogram(HistogramData)));
KisStrokeId strokeId = canvas->image()->startStroke(stroke);
canvas->image()->endStroke(strokeId);
} else {
m_histogramData.clear();
update();
}
}
void HistogramDockerWidget::receiveNewHistogram(HistVector *histogramData)
void HistogramDockerWidget::receiveNewHistogram(HistVector* data)
{
m_histogramData = *histogramData;
m_histogramData = *data;
update();
}
void HistogramDockerWidget::receiveNewHistogram(HistogramData data)
{
m_histogramData = data.bins;
m_colorSpace = data.colorSpace;
update();
}
......@@ -151,44 +306,3 @@ void HistogramDockerWidget::paintEvent(QPaintEvent *event)
}
}
void HistogramComputationThread::run()
{
const KoColorSpace *cs = m_dev->colorSpace();
quint32 channelCount = m_dev->channelCount();
quint32 pixelSize = m_dev->pixelSize();
quint32 imageSize = m_bounds.width() * m_bounds.height();
quint32 nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms
//allocate space for the histogram data
bins.resize((int)channelCount);
for (auto &bin : bins) {
bin.resize(std::numeric_limits<quint8>::max() + 1);
}
QRect bounds = m_dev->exactBounds();
if (bounds.isEmpty())
return;
quint32 toSkip = nSkip;
KisSequentialConstIterator it(m_dev, m_dev->exactBounds());
int numConseqPixels = it.nConseqPixels();
while (it.nextPixels(numConseqPixels)) {
numConseqPixels = it.nConseqPixels();
const quint8* pixel = it.rawDataConst();
for (int k = 0; k < numConseqPixels; ++k) {
if (--toSkip == 0) {
for (int chan = 0; chan < (int)channelCount; ++chan) {
bins[chan][cs->scaleToU8(pixel, chan)]++;
}
toSkip = nSkip;
}
pixel += pixelSize;
}
}
emit resultReady(&bins);
}
......@@ -26,29 +26,52 @@
#include <QThread>
#include "kis_types.h"
#include <vector>
#include <kis_simple_stroke_strategy.h>
class KisCanvas2;
class KoColorSpace;
typedef std::vector<std::vector<quint32> > HistVector; //Don't use QVector here - it's too slow for this purpose
struct HistogramData
{
HistogramData() {}
~HistogramData() {}
HistVector bins;
const KoColorSpace* colorSpace;
};
typedef QSharedPointer<HistogramData> HistogramDataSP;
Q_DECLARE_METATYPE(HistogramData)
class HistogramComputationThread : public QThread
class HistogramComputationStrokeStrategy : public QObject, public KisSimpleStrokeStrategy
{
Q_OBJECT
public:
HistogramComputationThread(KisPaintDeviceSP _dev, const QRect& _bounds) : m_dev(_dev), m_bounds(_bounds)
{}
HistogramComputationStrokeStrategy(KisImageWSP image);
~HistogramComputationStrokeStrategy() override;
void run() override;
private:
void initStrokeCallback() override;
void doStrokeCallback(KisStrokeJobData *data) override;
void finishStrokeCallback() override;
void cancelStrokeCallback() override;
void initiateVector(HistVector &vec, const KoColorSpace* colorSpace);
Q_SIGNALS:
void resultReady(HistVector*);
//Emitted when thumbnail is updated and overviewImage is fully generated.
void computationResultReady(HistogramData data);
private:
KisPaintDeviceSP m_dev;
QRect m_bounds;
HistVector bins;
struct Private;
const QScopedPointer<Private> m_d;
KisImageSP m_image;
std::vector<HistVector> m_results;
};
......@@ -72,6 +95,8 @@ public Q_SLOTS:
*/
void updateHistogram(KisCanvas2* canvas);
void receiveNewHistogram(HistVector*);
void receiveNewHistogram(HistogramData data);
private:
HistVector m_histogramData;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment