fastcolortransfer.cpp 6.27 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * This file is part of Krita
 *
 * Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
 *
 *  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 "fastcolortransfer.h"
22 23 24

#include <math.h>

25
#include <kpluginfactory.h>
26

Alexander Potashev's avatar
Alexander Potashev committed
27
#include <kundo2command.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
28

29
#include <KoColorSpaceRegistry.h>
Thomas Zander's avatar
Thomas Zander committed
30
#include <KoUpdater.h>
31

32
#include <filter/kis_filter_registry.h>
33 34
#include <kis_image.h>
#include <kis_paint_device.h>
Cyrille Berger's avatar
Cyrille Berger committed
35
#include <kis_selection.h>
36
#include <filter/kis_filter_category_ids.h>
37
#include <filter/kis_filter_configuration.h>
38
#include <kis_processing_information.h>
39 40

#include "kis_wdg_fastcolortransfer.h"
41
#include "ui_wdgfastcolortransfer.h"
42 43 44
#include <KisSequentialIteratorProgress.h>
#include <KoProgressUpdater.h>

45

Sven Langkamp's avatar
Sven Langkamp committed
46
K_PLUGIN_FACTORY_WITH_JSON(KritaFastColorTransferFactory, "kritafastcolortransfer.json", registerPlugin<FastColorTransferPlugin>();)
47 48


49
FastColorTransferPlugin::FastColorTransferPlugin(QObject *parent, const QVariantList &)
50
        : QObject(parent)
51
{
52
    KisFilterRegistry::instance()->add(new KisFilterFastColorTransfer());
53 54 55 56 57 58 59

}

FastColorTransferPlugin::~FastColorTransferPlugin()
{
}

60
KisFilterFastColorTransfer::KisFilterFastColorTransfer() : KisFilter(id(), FiltersCategoryColorId, i18n("&Color Transfer..."))
61
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
62
    setColorSpaceIndependence(FULLY_INDEPENDENT);
63 64
    setSupportsThreading(false);
    setSupportsPainting(false);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
65
    setSupportsAdjustmentLayers(false);
66 67 68
}


69
KisConfigWidget * KisFilterFastColorTransfer::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const
70
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
71
    Q_UNUSED(dev);
72
    return new KisWdgFastColorTransfer(parent);
73 74
}

75
KisFilterConfigurationSP KisFilterFastColorTransfer::factoryConfiguration() const
76
{
77
    KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 1);
78
    config->setProperty("filename", "");
79 80 81
    return config;
}

82 83
#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))

84 85
void KisFilterFastColorTransfer::processImpl(KisPaintDeviceSP device,
                                             const QRect& applyRect,
86
                                             const KisFilterConfigurationSP config,
87
                                             KoUpdater* progressUpdater) const
88
{
Cyrille Berger's avatar
Cyrille Berger committed
89
    Q_ASSERT(device != 0);
90

Boudewijn Rempt's avatar
Boudewijn Rempt committed
91
    dbgPlugins << "Start transferring color";
92

93
    // Convert ref and src to LAB
Cyrille Berger's avatar
Cyrille Berger committed
94
    const KoColorSpace* labCS = KoColorSpaceRegistry::instance()->lab16();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
95 96
    if (!labCS) {
        dbgPlugins << "The LAB colorspace is not available.";
97 98
        return;
    }
99
    
Cyrille Berger's avatar
Cyrille Berger committed
100
    dbgPlugins << "convert a copy of src to lab";
Cyrille Berger's avatar
Cyrille Berger committed
101 102
    const KoColorSpace* oldCS = device->colorSpace();
    KisPaintDeviceSP srcLAB = new KisPaintDevice(*device.data());
Cyrille Berger's avatar
Cyrille Berger committed
103
    dbgPlugins << "srcLab : " << srcLAB->extent();
104
    KUndo2Command* cmd = srcLAB->convertTo(labCS, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
105
    delete cmd;
106

107 108 109 110

    KoProgressUpdater compositeUpdater(progressUpdater, KoProgressUpdater::Unthreaded);
    KoUpdater *updaterStats = compositeUpdater.startSubtask(1);
    KoUpdater *updaterMap = compositeUpdater.startSubtask(2);
Cyrille Berger's avatar
Cyrille Berger committed
111

112
    // Compute the means and sigmas of src
Cyrille Berger's avatar
Cyrille Berger committed
113
    dbgPlugins << "Compute the means and sigmas of src";
114 115
    double meanL_src = 0., meanA_src = 0., meanB_src = 0.;
    double sigmaL_src = 0., sigmaA_src = 0., sigmaB_src = 0.;
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130
    {
        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;
        }
131
    }
132
    
Cyrille Berger's avatar
Cyrille Berger committed
133
    double totalSize = 1. / (applyRect.width() * applyRect.height());
134 135 136 137 138 139
    meanL_src *= totalSize;
    meanA_src *= totalSize;
    meanB_src *= totalSize;
    sigmaL_src *= totalSize;
    sigmaA_src *= totalSize;
    sigmaB_src *= totalSize;
140
    
Boudewijn Rempt's avatar
Boudewijn Rempt committed
141
    dbgPlugins << totalSize << "" << meanL_src << "" << meanA_src << "" << meanB_src << "" << sigmaL_src << "" << sigmaA_src << "" << sigmaB_src;
142 143 144 145 146 147 148 149
    
    double meanL_ref = config->getDouble("meanL");
    double meanA_ref = config->getDouble("meanA");
    double meanB_ref = config->getDouble("meanB");
    double sigmaL_ref = config->getDouble("sigmaL");
    double sigmaA_ref = config->getDouble("sigmaA");
    double sigmaB_ref = config->getDouble("sigmaB");
    
150
    // Transfer colors
Cyrille Berger's avatar
Cyrille Berger committed
151
    dbgPlugins << "Transfer colors";
152 153 154 155
    {
        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));
156

Cyrille Berger's avatar
Cyrille Berger committed
157 158
        quint16 labPixel[4];

159 160 161 162 163 164 165 166 167 168 169
        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);
        }
170 171
    }
}
172 173

#include "fastcolortransfer.moc"