Commit 907f88b9 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix filters slowdown due to progress reporting

Now we have a special type of a sequential iterator
(KisSequentialIteratorProgress), which can also handle
progress reporting (report on every new line).

This patch also refactors a few filters to use sequential
iterator and support multithreading/instant preview.

BUG:390463
parent 2b05aba4
/*
* Copyright (c) 2018 Dmitry Kazakov <dimula73@gmail.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 KISSEQUENTIALITERATORPROGRESS_H
#define KISSEQUENTIALITERATORPROGRESS_H
#include "kis_sequential_iterator.h"
#include <KoProgressProxy.h>
struct ProxyBasedProgressPolicy
{
ProxyBasedProgressPolicy(KoProgressProxy *proxy)
: m_proxy(proxy)
{
}
void setRange(int minimum, int maximum)
{
m_proxy->setRange(minimum, maximum);
}
void setValue(int value)
{
m_proxy->setValue(value);
}
void setFinished()
{
m_proxy->setValue(m_proxy->maximum());
}
private:
KoProgressProxy *m_proxy;
};
typedef KisSequentialIteratorBase<ReadOnlyIteratorPolicy<>, DevicePolicy, ProxyBasedProgressPolicy> KisSequentialConstIteratorProgress;
typedef KisSequentialIteratorBase<WritableIteratorPolicy<>, DevicePolicy, ProxyBasedProgressPolicy> KisSequentialIteratorProgress;
#endif // KISSEQUENTIALITERATORPROGRESS_H
......@@ -29,7 +29,7 @@
#ifndef NDEBUG
#include <QTime>
#endif
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
#include "kis_color_transformation_configuration.h"
KisColorTransformationFilter::KisColorTransformationFilter(const KoID& id, const KoID & category, const QString & entry) : KisFilter(id, category, entry)
......@@ -49,10 +49,6 @@ void KisColorTransformationFilter::processImpl(KisPaintDeviceSP device,
{
Q_ASSERT(!device.isNull());
if (progressUpdater) {
progressUpdater->setRange(0, applyRect.height() * applyRect.width());
}
const KoColorSpace * cs = device->colorSpace();
KoColorTransformation * colorTransformation = 0;
// Ew, casting
......@@ -65,15 +61,12 @@ void KisColorTransformationFilter::processImpl(KisPaintDeviceSP device,
}
if (!colorTransformation) return;
KisSequentialIterator it(device, applyRect);
int p = 0;
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
int conseq = it.nConseqPixels();
while (it.nextPixels(conseq)) {
conseq = it.nConseqPixels();
colorTransformation->transform(it.oldRawData(), it.rawData(), conseq);
if (progressUpdater) progressUpdater->setValue(p += conseq);
}
if (!colorTransformationConfiguration) {
......
......@@ -29,6 +29,8 @@
#include "kis_selection.h"
#include "kis_types.h"
#include <kis_painter.h>
#include <KoUpdater.h>
KoID KisFilter::categoryAdjust()
{
......@@ -126,6 +128,15 @@ void KisFilter::process(const KisPaintDeviceSP src,
}
try {
QScopedPointer<KoUpdater> fakeUpdater;
if (!progressUpdater) {
// TODO: remove dependency on KoUpdater, depend on KoProgressProxy,
// it is more lightweight
fakeUpdater.reset(new KoDummyUpdater());
progressUpdater = fakeUpdater.data();
}
processImpl(temporary, applyRect, config, progressUpdater);
}
catch (std::bad_alloc) {
......
......@@ -29,10 +29,9 @@
#include <resources/KoPattern.h>
#include "kis_selection.h"
#include "kis_iterator_ng.h"
#include <KisSequentialIteratorProgress.h>
#include "kis_image.h"
#include "kis_random_accessor_ng.h"
#include "kis_progress_update_helper.h"
#include "kis_gradient_shape_strategy.h"
#include "kis_polygonal_gradient_shape_strategy.h"
#include "kis_cached_gradient_shape_strategy.h"
......@@ -712,9 +711,7 @@ bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
CachedGradient cachedGradient(gradient(), qMax(processRect.width(), processRect.height()), colorSpace);
KisSequentialIterator it(dev, processRect);
const int rightCol = processRect.right();
KisProgressUpdateHelper progressHelper(progressUpdater(), 100, processRect.height());
KisSequentialIteratorProgress it(dev, processRect, progressUpdater());
while (it.nextPixel()) {
double t = shapeStrategy->valueAt(it.x(), it.y());
......@@ -725,10 +722,6 @@ bool KisGradientPainter::paintGradient(const QPointF& gradientVectorStart,
}
memcpy(it.rawData(), cachedGradient.cachedAt(t), pixelSize);
if (it.x() == rightCol) {
progressHelper.step();
}
}
bitBlt(processRect.topLeft(), dev, processRect);
......
......@@ -41,17 +41,8 @@ public:
m_levelOfDetail = levelOfDetail;
}
#ifdef Q_CC_MSVC
static double log2( double n )
{
// log(n)/log(2) is log2.
return log( n ) / log( 2 );
}
#endif
static int scaleToLod(qreal scale, int maxLod) {
return qMin(maxLod, qMax(0, qFloor(log2(1.0 / scale))));
return qMin(maxLod, qMax(0, qFloor(std::log2(1.0 / scale))));
}
static qreal lodToScale(int levelOfDetail) {
......
......@@ -29,7 +29,8 @@ public:
m_baseProgress(0),
m_portion(portion),
m_currentStep(0),
m_numSteps(numSteps)
m_numSteps(numSteps),
m_lastReportedLocalProgress(-1)
{
if (m_progressUpdater) {
m_baseProgress = m_progressUpdater->progress();
......@@ -46,7 +47,8 @@ public:
int localProgress = m_numSteps ?
m_portion * (++m_currentStep) / m_numSteps : m_portion;
if (m_progressUpdater) {
if (m_progressUpdater && m_lastReportedLocalProgress != localProgress) {
m_lastReportedLocalProgress = localProgress;
m_progressUpdater->setProgress(m_baseProgress + localProgress);
}
// TODO: handle interrupted processing (connect to other layers, i.e. undo)
......@@ -58,6 +60,7 @@ private:
int m_portion;
int m_currentStep;
int m_numSteps;
int m_lastReportedLocalProgress;
};
#endif /* __KIS_PROGRESS_UPDATE_HELPER_H */
......@@ -107,6 +107,21 @@ private:
const quint8 *m_oldRawData;
};
struct NoProgressPolicy
{
ALWAYS_INLINE void setRange(int /* minimum */, int /* maximum */)
{
}
ALWAYS_INLINE void setValue(int /* value */)
{
}
ALWAYS_INLINE void setFinished()
{
}
};
/**
* Sequential iterator is supposed to be used when you need to
* read/write a rect of the image and you don't want to think about
......@@ -170,12 +185,13 @@ private:
* const.
*/
template <class IteratorPolicy, class SourcePolicy = DevicePolicy>
template <class IteratorPolicy, class SourcePolicy = DevicePolicy, class ProgressPolicy = NoProgressPolicy>
class KisSequentialIteratorBase
{
public:
KisSequentialIteratorBase(SourcePolicy source, const QRect &rect)
KisSequentialIteratorBase(SourcePolicy source, const QRect &rect, ProgressPolicy progressPolicy = ProgressPolicy())
: m_policy(source, rect),
m_progressPolicy(progressPolicy),
m_pixelSize(source.pixelSize()),
m_rowsLeft(rect.height() - 1),
m_columnOffset(0),
......@@ -189,6 +205,13 @@ public:
m_policy.updatePointersCache();
m_iteratorX = m_policy.m_iter ? m_policy.m_iter->x() : 0;
m_iteratorY = m_policy.m_iter ? m_policy.m_iter->y() : 0;
m_progressPolicy.setRange(rect.top(), rect.top() + rect.height());
m_progressPolicy.setValue(rect.top());
}
~KisSequentialIteratorBase() {
m_progressPolicy.setFinished();
}
inline int nConseqPixels() const {
......@@ -228,7 +251,12 @@ public:
m_columnOffset = 0;
m_columnsLeft = m_numConseqPixels = m_policy.m_iter->nConseqPixels();
m_policy.updatePointersCache();
m_progressPolicy.setValue(m_policy.m_iter->y());
} else if (m_rowsLeft == 0) {
// report that we have completed iteration
m_progressPolicy.setValue(m_policy.m_iter->y() + 1);
}
m_iteratorX = m_policy.m_iter->x();
m_iteratorY = m_policy.m_iter->y();
}
......@@ -261,6 +289,7 @@ public:
private:
Q_DISABLE_COPY(KisSequentialIteratorBase)
IteratorPolicy m_policy;
ProgressPolicy m_progressPolicy;
const int m_pixelSize;
int m_rowsLeft;
......
......@@ -57,11 +57,12 @@ namespace KritaUtils
QVector<QRect> patches;
qint32 firstCol = divideFloor(rc.x(), patchSize.width());
qint32 firstRow = divideFloor(rc.y(), patchSize.height());
const qint32 firstCol = divideFloor(rc.x(), patchSize.width());
const qint32 firstRow = divideFloor(rc.y(), patchSize.height());
qint32 lastCol = divideFloor(rc.x() + rc.width(), patchSize.width());
qint32 lastRow = divideFloor(rc.y() + rc.height(), patchSize.height());
// TODO: check if -1 is needed here
const qint32 lastCol = divideFloor(rc.x() + rc.width(), patchSize.width());
const qint32 lastRow = divideFloor(rc.y() + rc.height(), patchSize.height());
for(qint32 i = firstRow; i <= lastRow; i++) {
for(qint32 j = firstCol; j <= lastCol; j++) {
......
......@@ -32,6 +32,7 @@
#include "kis_paint_device.h"
#include <kis_iterator_ng.h>
#include "kis_global.h"
#include "testutil.h"
void KisIteratorTest::allCsApplicator(void (KisIteratorTest::* funcPtr)(const KoColorSpace*cs))
......@@ -444,6 +445,46 @@ void KisIteratorTest::sequentialIter()
allCsApplicator(&KisIteratorTest::sequentialIter);
}
#include <KisSequentialIteratorProgress.h>
void KisIteratorTest::sequentialIteratorWithProgress()
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
const QRect rc(10,10,200,200);
TestUtil::TestProgressBar proxy;
KisSequentialConstIteratorProgress it (dev, rc, &proxy);
while (it.nextPixel()) {
QCOMPARE(proxy.min(), rc.top());
QCOMPARE(proxy.max(), rc.top() + rc.height());
QCOMPARE(proxy.value(), it.y());
}
QCOMPARE(proxy.max(), rc.top() + rc.height());
QCOMPARE(proxy.value(), proxy.max());
}
void KisIteratorTest::sequentialIteratorWithProgressIncomplete()
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
const QRect rc(10,10,100,100);
TestUtil::TestProgressBar proxy;
{
KisSequentialConstIteratorProgress it (dev, rc, &proxy);
QCOMPARE(proxy.max(), rc.top() + rc.height());
QCOMPARE(proxy.value(), rc.top());
}
// on desruction, iterator automatically completes progress reporting
QCOMPARE(proxy.max(), rc.top() + rc.height());
QCOMPARE(proxy.value(), proxy.max());
}
void KisIteratorTest::hLineIter()
{
allCsApplicator(&KisIteratorTest::hLineIter);
......
......@@ -45,6 +45,8 @@ private Q_SLOTS:
void writeBytes();
void fill();
void sequentialIter();
void sequentialIteratorWithProgress();
void sequentialIteratorWithProgressIncomplete();
void hLineIter();
void randomAccessor();
};
......
......@@ -51,9 +51,12 @@ void KoUpdater::cancel()
void KoUpdater::setProgress(int percent)
{
m_progressPercent = percent;
const bool percentChanged = m_progressPercent != percent;
emit sigProgress( percent );
if (percentChanged || percent == 0 || percent == 100) {
m_progressPercent = percent;
emit sigProgress( percent );
}
}
int KoUpdater::progress() const
......@@ -69,7 +72,7 @@ bool KoUpdater::interrupted() const
int KoUpdater::maximum() const
{
return 100;
return max;
}
void KoUpdater::setValue( int value )
......@@ -83,7 +86,7 @@ void KoUpdater::setValue( int value )
m_progressPercent = max;
emit sigProgress(max);
} else {
setProgress((100 * (value - min)) / (max - min));
setProgress((100 * (value - min)) / range);
}
}
......
......@@ -34,6 +34,7 @@
#include "ui_wdgcolortoalphabase.h"
#include "kis_wdg_color_to_alpha.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
KisFilterColorToAlpha::KisFilterColorToAlpha()
: KisFilter(id(), categoryColors(), i18n("&Color to Alpha..."))
......@@ -72,9 +73,8 @@ inline void inverseOver(const int numChannels, const int *channelIndex,
template<typename channel_type, typename composite_type>
void applyToIterator(const int numChannels, const int *channelIndex,
KisSequentialIterator &it, KoColor baseColor,
int threshold, const KoColorSpace *cs,
KisProgressUpdateHelper &progressHelper)
KisSequentialIteratorProgress &it, KoColor baseColor,
int threshold, const KoColorSpace *cs)
{
qreal thresholdF = threshold;
quint8 *baseColorData_uint8 = baseColor.data();
......@@ -95,8 +95,6 @@ void applyToIterator(const int numChannels, const int *channelIndex,
inverseOver<channel_type, composite_type>(numChannels, channelIndex,
dst, baseColorData,
newOpacity);
progressHelper.step();
}
}
......@@ -116,8 +114,7 @@ void KisFilterColorToAlpha::processImpl(KisPaintDeviceSP device,
const KoColorSpace * cs = device->colorSpace();
KisProgressUpdateHelper progressHelper(progressUpdater, 100, rect.width() * rect.height());
KisSequentialIterator it(device, rect);
KisSequentialIteratorProgress it(device, rect, progressUpdater);
KoColor baseColor(cTA, cs);
QVector<int> channelIndex;
......@@ -149,28 +146,28 @@ void KisFilterColorToAlpha::processImpl(KisPaintDeviceSP device,
case KoChannelInfo::UINT8:
applyToIterator<quint8, qint16>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
case KoChannelInfo::UINT16:
applyToIterator<quint16, qint32>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
case KoChannelInfo::UINT32:
applyToIterator<quint32, qint64>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
case KoChannelInfo::FLOAT32:
applyToIterator<float, float>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
case KoChannelInfo::FLOAT64:
applyToIterator<double, double>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
case KoChannelInfo::FLOAT16:
#include <KoConfig.h>
......@@ -178,7 +175,7 @@ void KisFilterColorToAlpha::processImpl(KisPaintDeviceSP device,
#include <half.h>
applyToIterator<half, half>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs, progressHelper);
threshold, cs);
break;
#endif
......
......@@ -25,7 +25,8 @@
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
typedef void (*funcMaxMin)(const quint8* , quint8* , uint);
......@@ -82,9 +83,6 @@ void KisFilterMax::processImpl(KisPaintDeviceSP device,
Q_UNUSED(config);
Q_ASSERT(device != 0);
int pixelsProcessed = 0;
int totalCost = rect.width() * rect.height() / 100;
const KoColorSpace * cs = device->colorSpace();
qint32 nC = cs->colorChannelCount();
......@@ -100,11 +98,9 @@ void KisFilterMax::processImpl(KisPaintDeviceSP device,
return;
}
KisSequentialIterator it(device, rect);
KisSequentialIteratorProgress it(device, rect, progressUpdater);
while (it.nextPixel()) {
F(it.oldRawData(), it.rawData(), nC);
if (progressUpdater) progressUpdater->setProgress((++pixelsProcessed) / totalCost);
}
}
......@@ -124,10 +120,6 @@ void KisFilterMin::processImpl(KisPaintDeviceSP device,
Q_UNUSED(config);
Q_ASSERT(device != 0);
int pixelsProcessed = 0;
int totalCost = rect.width() * rect.height() / 100;
if (totalCost == 0) totalCost = 1;
const KoColorSpace * cs = device->colorSpace();
qint32 nC = cs->colorChannelCount();
......@@ -143,10 +135,9 @@ void KisFilterMin::processImpl(KisPaintDeviceSP device,
return;
}
KisSequentialIterator it(device, rect);
KisSequentialIteratorProgress it(device, rect, progressUpdater);
while (it.nextPixel()) {
F(it.oldRawData(), it.rawData(), nC);
if (progressUpdater) progressUpdater->setProgress((++pixelsProcessed) / totalCost);
}
}
......@@ -53,7 +53,7 @@
#include <KoUpdater.h>
#include <KoColorSpaceConstants.h>
#include <KoCompositeOp.h>
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
#include "kis_hsv_adjustment_filter.h"
......@@ -156,20 +156,14 @@ void KisAutoContrast::processImpl(KisPaintDeviceSP device,
// apply
KoColorTransformation *adj = device->colorSpace()->createBrightnessContrastAdjustment(transfer);
KisSequentialIterator it(device, applyRect);
qint32 totalCost = (applyRect.width() * applyRect.height()) / 100;
if (totalCost == 0) totalCost = 1;
qint32 pixelsProcessed = 0;
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
quint32 npix = it.nConseqPixels();
while(it.nextPixels(npix) && !(progressUpdater && progressUpdater->interrupted())) {
while(it.nextPixels(npix)) {
// adjust
npix = it.nConseqPixels();
adj->transform(it.oldRawData(), it.rawData(), npix);
pixelsProcessed += npix;
if (progressUpdater) progressUpdater->setProgress(pixelsProcessed / totalCost);
}
delete[] transfer;
......
......@@ -48,7 +48,8 @@
#include <kis_processing_information.h>
#include "widgets/kis_multi_integer_filter_widget.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
KisEmbossFilter::KisEmbossFilter() : KisFilter(id(), categoryEmboss(), i18n("&Emboss with Variable Depth..."))
{
......@@ -99,11 +100,7 @@ void KisEmbossFilter::processImpl(KisPaintDeviceSP device,
int Width = applyRect.width();
int Height = applyRect.height();
if (progressUpdater) {
progressUpdater->setRange(0, Height);
}
KisSequentialIterator it(device, applyRect);
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
QColor color1;
QColor color2;
KisRandomConstAccessorSP acc = device->createRandomAccessorNG(srcTopLeft.x(), srcTopLeft.y());
......@@ -122,7 +119,6 @@ void KisEmbossFilter::processImpl(KisPaintDeviceSP device,
Gray = CLAMP((R + G + B) / 3, 0, quint8_MAX);
device->colorSpace()->fromQColor(QColor(Gray, Gray, Gray, color1.alpha()), it.rawData());
if (progressUpdater) { progressUpdater->setValue(it.y()); if(progressUpdater->interrupted()) return; }
}
}
......
......@@ -38,7 +38,9 @@
#include "kis_wdg_fastcolortransfer.h"
#include "ui_wdgfastcolortransfer.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
#include <KoProgressUpdater.h>
K_PLUGIN_FACTORY_WITH_JSON(KritaFastColorTransferFactory, "kritafastcolortransfer.json", registerPlugin<FastColorTransferPlugin>();)
......@@ -101,30 +103,30 @@ void KisFilterFastColorTransfer::processImpl(KisPaintDeviceSP device,
KUndo2Command* cmd = srcLAB->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
delete cmd;
if (progressUpdater) {
progressUpdater->setRange(0, 2 * applyRect.width() * applyRect.height());
}
int count = 0;
KoProgressUpdater compositeUpdater(progressUpdater, KoProgressUpdater::Unthreaded);
KoUpdater *updaterStats = compositeUpdater.startSubtask(1);
KoUpdater *updaterMap = compositeUpdater.startSubtask(2);
// Compute the means and sigmas of src
dbgPlugins << "Compute the means and sigmas of src";
double meanL_src = 0., meanA_src = 0., meanB_src = 0.;
double sigmaL_src = 0., sigmaA_src = 0., sigmaB_src = 0.;
KisSequentialConstIterator srcIt(srcLAB, applyRect);
while (srcIt.nextPixel() && !(progressUpdater && progressUpdater->interrupted())) {
const quint16* data = reinterpret_cast<const quint16*>(srcIt.oldRawData());
quint32 L = data[0];
quint32 A = data[1];
quint32 B = data[2];
meanL_src += L;
meanA_src += A;
meanB_src += B;
sigmaL_src += L * L;
sigmaA_src += A * A;
sigmaB_src += B * B;
if (progressUpdater) progressUpdater->setValue(++count);
{
KisSequentialConstIteratorProgress srcIt(srcLAB, applyRect, updaterStats);
while (srcIt.nextPixel()) {
const quint16* data = reinterpret_cast<const quint16*>(srcIt.oldRawData());
quint32 L = data[0];
quint32 A = data[1];
quint32 B = data[2];
meanL_src += L;
meanA_src += A;
meanB_src += B;
sigmaL_src += L * L;
sigmaA_src += A * A;
sigmaB_src += B * B;
}
}
double totalSize = 1. / (applyRect.width() * applyRect.height());
......@@ -144,31 +146,26 @@ void KisFilterFastColorTransfer::processImpl(KisPaintDeviceSP device,
double sigmaA_ref = config->getDouble("sigmaA");
double sigmaB_ref = config->getDouble("sigmaB");
// Transfer colors
dbgPlugins << "Transfer colors";
{
double coefL = sqrt((sigmaL_ref - meanL_ref * meanL_ref) / (sigmaL_src - meanL_src * meanL_src));
double coefA = sqrt((sigmaA_ref - meanA_ref * meanA_ref) / (sigmaA_src - meanA_src * meanA_src));
double coefB = sqrt((sigmaB_ref - meanB_ref * meanB_ref) / (sigmaB_src - meanB_src * meanB_src));
KisHLineConstIteratorSP srcLABIt = srcLAB->createHLineConstIteratorNG(applyRect.x(), applyRect.y(), applyRect.width());
KisHLineIteratorSP dstIt = device->createHLineIteratorNG(applyRect.x(), applyRect.y(), applyRect.width());
quint16 labPixel[4];
for (int y = 0; y < applyRect.height() && !(progressUpdater && progressUpdater->interrupted()); ++y) {
do {
const quint16* data = reinterpret_cast<const quint16*>(srcLABIt->oldRawData());
labPixel[0] = (quint16)CLAMP(((double)data[0] - meanL_src) * coefL + meanL_ref, 0., 65535.);
labPixel[1] = (quint16)CLAMP(((double)data[1] - meanA_src) * coefA + meanA_ref, 0., 65535.);
labPixel[2] = (quint16)CLAMP(((double)data[2] - meanB_src) * coefB + meanB_ref, 0., 65535.);
labPixel[3] = data[3];
oldCS->fromLabA16(reinterpret_cast<const quint8*>(labPixel), dstIt->rawData(), 1);
if (progressUpdater) progressUpdater->setValue(++count);
srcLABIt->nextPixel();
} while(dstIt->nextPixel());
dstIt->nextRow();
srcLABIt->nextRow();
}
KisSequentialConstIteratorProgress srcLabIt(srcLAB, applyRect, updaterMap);
KisSequentialIterator dstIt(device, applyRect);
while (srcLabIt.nextPixel() && dstIt.nextPixel()) {
const quint16* data = reinterpret_cast<const quint16*>(srcLabIt.oldRawData());
labPixel[0] = (quint16)CLAMP(((double)data[0] - meanL_src) * coefL + meanL_ref, 0., 65535.);
labPixel[1] = (quint16)CLAMP(((double)data[1] - meanA_src) * coefA + meanA_ref, 0., 65535.);
labPixel[2] = (quint16)CLAMP(((double)data[2] - meanB_src) * coefB + meanB_ref, 0., 65535.);
labPixel[3] = data[3];
oldCS->fromLabA16(reinterpret_cast<const quint8*>(labPixel), dstIt.rawData(), 1);
}
}