File indexing completed on 2024-06-09 04:27:22

0001 /*
0002  * This file is part of the KDE project
0003  *
0004  * SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "threshold.h"
0010 #include <stdlib.h>
0011 #include <vector>
0012 
0013 #include <QPoint>
0014 #include <QTime>
0015 
0016 #include <klocalizedstring.h>
0017 
0018 #include <kis_debug.h>
0019 #include <kpluginfactory.h>
0020 
0021 #include <filter/kis_filter_category_ids.h>
0022 #include <filter/kis_filter_registry.h>
0023 #include <kis_global.h>
0024 #include "KisLevelsSlider.h"
0025 #include "kis_histogram.h"
0026 #include <kis_layer.h>
0027 #include "kis_paint_device.h"
0028 #include "kis_painter.h"
0029 #include <kis_processing_information.h>
0030 #include <kis_selection.h>
0031 #include <kis_types.h>
0032 #include <KisSequentialIteratorProgress.h>
0033 #include <kis_signals_blocker.h>
0034 
0035 #include <KoBasicHistogramProducers.h>
0036 #include "KoColorModelStandardIds.h"
0037 #include <KoColorSpace.h>
0038 #include <KoColorTransformation.h>
0039 #include <KoUpdater.h>
0040 #include <KisGlobalResourcesInterface.h>
0041 
0042 K_PLUGIN_FACTORY_WITH_JSON(KritaThresholdFactory, "kritathreshold.json", registerPlugin<KritaThreshold>();)
0043 
0044 KritaThreshold::KritaThreshold(QObject *parent, const QVariantList &)
0045     : QObject(parent)
0046 {
0047     KisFilterRegistry::instance()->add(new KisFilterThreshold());
0048 }
0049 
0050 KritaThreshold::~KritaThreshold()
0051 {
0052 }
0053 
0054 KisFilterThreshold::KisFilterThreshold()
0055     : KisFilter(id(), FiltersCategoryAdjustId, i18n("&Threshold..."))
0056 {
0057     setColorSpaceIndependence(FULLY_INDEPENDENT);
0058 
0059     setSupportsPainting(false);
0060     setShowConfigurationWidget(true);
0061     setSupportsLevelOfDetail(true);
0062     setSupportsAdjustmentLayers(true);
0063     setSupportsThreading(true);
0064 }
0065 
0066 void KisFilterThreshold::processImpl(KisPaintDeviceSP device,
0067                  const QRect& applyRect,
0068                  const KisFilterConfigurationSP config,
0069                  KoUpdater *progressUpdater) const
0070 {
0071     Q_ASSERT(!device.isNull());
0072 
0073     const int threshold = config->getInt("threshold");
0074 
0075     KoColor white(Qt::white, device->colorSpace());
0076     KoColor black(Qt::black, device->colorSpace());
0077 
0078     KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
0079     const int pixelSize = device->colorSpace()->pixelSize();
0080 
0081     while (it.nextPixel()) {
0082         if (device->colorSpace()->intensity8(it.oldRawData()) > threshold) {
0083             white.setOpacity(device->colorSpace()->opacityU8(it.oldRawData()));
0084             memcpy(it.rawData(), white.data(), pixelSize);
0085         }
0086         else {
0087             black.setOpacity(device->colorSpace()->opacityU8(it.oldRawData()));
0088             memcpy(it.rawData(), black.data(), pixelSize);
0089         }
0090     }
0091 
0092 }
0093 
0094 
0095 KisFilterConfigurationSP KisFilterThreshold::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
0096 {
0097     KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
0098     config->setProperty("threshold", 128);
0099     return config;
0100 }
0101 
0102 KisConfigWidget *KisFilterThreshold::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool) const
0103 {
0104     return new KisThresholdConfigWidget(parent, dev);
0105 }
0106 
0107 KisThresholdConfigWidget::KisThresholdConfigWidget(QWidget * parent, KisPaintDeviceSP dev)
0108     : KisConfigWidget(parent)
0109 {
0110     Q_ASSERT(dev);
0111     m_page.setupUi(this);
0112 
0113     m_page.thresholdGradient->setThreshold(0.5);
0114     m_page.intThreshold->setValue(128);
0115 
0116     connect(m_page.intThreshold, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged()));
0117     connect(m_page.intThreshold, QOverload<int>::of(&QSpinBox::valueChanged),
0118         [this](int value)
0119         {
0120             KisSignalsBlocker blocker(m_page.thresholdGradient);
0121             m_page.thresholdGradient->setThreshold(static_cast<qreal>(value) / 255.0);
0122         }
0123     );
0124     connect(m_page.thresholdGradient, SIGNAL(thresholdChanged(qreal)), SIGNAL(sigConfigurationItemChanged()));
0125     connect(m_page.thresholdGradient, &KisThresholdSlider::thresholdChanged,
0126         [this](qreal value)
0127         {
0128             KisSignalsBlocker blocker(m_page.intThreshold);
0129             m_page.intThreshold->setValue(static_cast<int>(qRound(value * 255.0)));
0130         }
0131     );
0132 
0133     connect((QObject*)(m_page.chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(slotDrawHistogram(bool)));
0134 
0135     KoHistogramProducer *producer = new KoGenericLabHistogramProducer();
0136     m_histogram.reset( new KisHistogram(dev, dev->exactBounds(), producer, LINEAR) );
0137     m_histlog = false;
0138     m_page.histview->resize(288,100);
0139     slotDrawHistogram();
0140 
0141 }
0142 
0143 KisThresholdConfigWidget::~KisThresholdConfigWidget()
0144 {
0145 }
0146 
0147 void KisThresholdConfigWidget::slotDrawHistogram(bool logarithmic)
0148 {
0149     int wHeight = m_page.histview->height();
0150     int wHeightMinusOne = wHeight - 1;
0151     int wWidth = m_page.histview->width();
0152 
0153     if (m_histlog != logarithmic) {
0154         // Update the m_histogram
0155         if (logarithmic)
0156             m_histogram->setHistogramType(LOGARITHMIC);
0157         else
0158             m_histogram->setHistogramType(LINEAR);
0159         m_histlog = logarithmic;
0160     }
0161 
0162     QPalette appPalette = QApplication::palette();
0163     QPixmap pix(wWidth-100, wHeight);
0164 
0165     pix.fill(QColor(appPalette.color(QPalette::Base)));
0166     QPainter p(&pix);
0167 
0168     p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
0169 
0170     double highest = (double)m_histogram->calculations().getHighest();
0171     qint32 bins = m_histogram->producer()->numberOfBins();
0172 
0173     // use nearest neighbour interpolation
0174     if (m_histogram->getHistogramType() == LINEAR) {
0175         double factor = (double)(wHeight - wHeight / 5.0) / highest;
0176         for (int i = 0; i < wWidth; i++) {
0177             int binNo = qRound((double)i / wWidth * (bins - 1));
0178             if ((int)m_histogram->getValue(binNo) != 0)
0179                 p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - (int)m_histogram->getValue(binNo) * factor);
0180         }
0181     } else {
0182         double factor = (double)(wHeight - wHeight / 5.0) / (double)log(highest);
0183         for (int i = 0; i < wWidth; i++) {
0184             int binNo = qRound((double)i / wWidth * (bins - 1)) ;
0185             if ((int)m_histogram->getValue(binNo) != 0)
0186                 p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - log((double)m_histogram->getValue(binNo)) * factor);
0187         }
0188     }
0189 
0190     m_page.histview->setPixmap(pix);
0191 }
0192 
0193 void KisThresholdConfigWidget::slotSetThreshold(int limit)
0194 {
0195     m_page.intThreshold->setMaximum(limit - 1);
0196 }
0197 
0198 KisPropertiesConfigurationSP KisThresholdConfigWidget::configuration() const
0199 {
0200     KisFilterConfigurationSP config = new KisFilterConfiguration("threshold", 1, KisGlobalResourcesInterface::instance());
0201     config->setProperty("threshold", m_page.intThreshold->value());
0202     return config;
0203 }
0204 
0205 void KisThresholdConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
0206 {
0207     QVariant value;
0208     if (config->getProperty("threshold", value)) {
0209         KisSignalsBlocker blocker(m_page.intThreshold, m_page.thresholdGradient);
0210         m_page.intThreshold->setValue(value.toUInt());
0211         m_page.thresholdGradient->setThreshold(static_cast<qreal>(value.toUInt()) / 255.0);
0212     }
0213     emit sigConfigurationItemChanged();
0214 }
0215 
0216 
0217 #include "threshold.moc"