File indexing completed on 2025-02-02 04:22:08

0001 /*
0002  *  SPDX-FileCopyrightText: 2004, 2007, 2009 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_auto_brush_widget.h"
0009 
0010 #include "kis_image_config.h"
0011 #include <math.h>
0012 #include <kis_debug.h>
0013 #include <QSpinBox>
0014 #include <QToolButton>
0015 #include <QImage>
0016 #include <QComboBox>
0017 #include <QLabel>
0018 #include <QPixmap>
0019 #include <QResizeEvent>
0020 
0021 #include <kis_fixed_paint_device.h>
0022 #include <kis_mask_generator.h>
0023 #include <kis_slider_spin_box.h>
0024 #include "kis_signals_blocker.h"
0025 #include "kis_signal_compressor.h"
0026 #include "kis_aspect_ratio_locker.h"
0027 #include <KisAngleSelector.h>
0028 #include <KisSpinBoxI18nHelper.h>
0029 #include <KisWidgetConnectionUtils.h>
0030 #include <kis_cubic_curve.h>
0031 #include <kis_auto_brush_factory.h>
0032 #include <KisGlobalResourcesInterface.h>
0033 #include <KisAutoBrushModel.h>
0034 
0035 
0036 struct KisAutoBrushWidget::Private {
0037     Private(KisAutoBrushModel *_model)
0038         : model(_model),
0039           previewCompressor(200, KisSignalCompressor::FIRST_ACTIVE)
0040     {
0041     }
0042 
0043     KisAutoBrushModel *model {0};
0044     KisSignalCompressor previewCompressor;
0045 };
0046 
0047 
0048 
0049 KisAutoBrushWidget::KisAutoBrushWidget(int maxBrushSize,
0050                                        KisAutoBrushModel *model,
0051                                        QWidget *parent, const char* name)
0052     : KisWdgAutoBrush(parent, name)
0053     , m_fadeAspectLocker(new KisAspectRatioLocker())
0054     , m_d(new Private(model))
0055 
0056 {
0057     connectControl(comboBoxShape, m_d->model, "shape");
0058     connectControl(comboBoxMaskType, m_d->model, "type");
0059 
0060     inputRadius->setRange(0.01, maxBrushSize, 2);
0061     inputRadius->setExponentRatio(3.0);
0062     inputRadius->setSingleStep(1);
0063     inputRadius->setValue(5);
0064     inputRadius->setSuffix(i18n(" px"));
0065     inputRadius->setBlockUpdateSignalOnDrag(true);
0066     connectControl(inputRadius, m_d->model, "diameter");
0067 
0068     inputRatio->setRange(0.01, 1.0, 2);
0069     inputRatio->setSingleStep(0.01);
0070     inputRatio->setValue(1.0);
0071     inputRatio->setBlockUpdateSignalOnDrag(true);
0072     connectControl(inputRatio, m_d->model, "ratio");
0073 
0074     inputHFade->setRange(0.0, 1.0, 2);
0075     inputHFade->setSingleStep(0.01);
0076     inputHFade->setValue(0.5);
0077 
0078     inputVFade->setRange(0.0, 1.0, 2);
0079     inputVFade->setSingleStep(0.01);
0080     inputVFade->setValue(0.5);
0081 
0082     aspectButton->setKeepAspectRatio(true);
0083 
0084     m_fadeAspectLocker->connectSpinBoxes(inputHFade, inputVFade, aspectButton);
0085     m_fadeAspectLocker->setBlockUpdateSignalOnDrag(false);
0086 
0087     connect(m_fadeAspectLocker.data(), &KisAspectRatioLocker::sliderValueChanged,
0088             [this] () {
0089                 m_d->model->sethorizontalFade(inputHFade->value());
0090                 m_d->model->setverticalFade(inputVFade->value());
0091             });
0092 
0093     m_d->model->LAGER_QT(horizontalFade).bind([this] (qreal value) {
0094         KisSignalsBlocker b(inputHFade);
0095         inputHFade->setValue(value);
0096         m_fadeAspectLocker->updateAspect();
0097     });
0098 
0099     m_d->model->LAGER_QT(verticalFade).bind([this] (qreal value) {
0100         KisSignalsBlocker b(inputVFade);
0101         inputVFade->setValue(value);
0102         m_fadeAspectLocker->updateAspect();
0103     });
0104 
0105     inputSpikes->setRange(2, 20);
0106     inputSpikes->setValue(2);
0107     inputSpikes->setBlockUpdateSignalOnDrag(true);
0108     connectControl(inputSpikes, m_d->model, "spikes");
0109 
0110     inputRandomness->setRange(0, 100);
0111     inputRandomness->setValue(0);
0112     inputRandomness->setBlockUpdateSignalOnDrag(true);
0113     connectControl(inputRandomness, m_d->model, "randomness");
0114 
0115     inputAngle->setDecimals(0);
0116     connectControl(inputAngle, m_d->model, "angle");
0117     connectControl(spacingWidget, m_d->model, "aggregatedSpacing");
0118 
0119     density->setRange(0, 100, 0);
0120     density->setSingleStep(1);
0121     density->setValue(100);
0122     KisSpinBoxI18nHelper::setText(density, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
0123     density->setBlockUpdateSignalOnDrag(true);
0124     connectControl(density, m_d->model, "density");
0125 
0126     connect(softnessCurve, &KisCurveWidget::modified, this, &KisAutoBrushWidget::slotCurveWidgetChanged);
0127     connect(m_d->model, &KisAutoBrushModel::curveStringChanged, this, &KisAutoBrushWidget::slotCurvePropertyChanged);
0128     m_d->model->LAGER_QT(curveString).nudge();
0129 
0130     QList<KoID> ids = KisMaskGenerator::maskGeneratorIds();
0131     for (int i = 0; i < ids.size(); i++) {
0132         comboBoxMaskType->insertItem(i, ids[i].name());
0133     }
0134 
0135     connect(m_d->model, &KisAutoBrushModel::typeChanged, this, &KisAutoBrushWidget::setStackedWidget);
0136     setStackedWidget(m_d->model->type());
0137 
0138     brushPreview->setIconSize(QSize(100, 100));
0139 
0140     connectControl(btnAntialiasing, m_d->model, "antialiasEdges");
0141 
0142     lager::watch(m_d->model->m_commonData, std::bind(&KisSignalCompressor::start, &m_d->previewCompressor));
0143     lager::watch(m_d->model->m_autoBrushData, std::bind(&KisSignalCompressor::start, &m_d->previewCompressor));
0144     lager::watch(m_d->model->m_commonBrushSizeData, std::bind(&KisSignalCompressor::start, &m_d->previewCompressor));
0145 
0146     connect(&m_d->previewCompressor, &KisSignalCompressor::timeout, this, &KisAutoBrushWidget::slotUpdateBrushPreview);
0147 
0148     slotUpdateBrushPreview();
0149 }
0150 
0151 KisAutoBrushWidget::~KisAutoBrushWidget()
0152 {
0153 }
0154 
0155 void KisAutoBrushWidget::resizeEvent(QResizeEvent *)
0156 {
0157     brushPreview->setMinimumHeight(brushPreview->width()); // dirty hack !
0158     brushPreview->setMaximumHeight(brushPreview->width()); // dirty hack !
0159 }
0160 
0161 void KisAutoBrushWidget::setStackedWidget(int index)
0162 {
0163     if (index == 1) {
0164         stackedWidget->setCurrentIndex(1);
0165     }
0166     else {
0167         stackedWidget->setCurrentIndex(0);
0168     }
0169 }
0170 
0171 void KisAutoBrushWidget::slotCurveWidgetChanged()
0172 {
0173     m_d->model->setcurveString(softnessCurve->curve().toString());
0174 }
0175 
0176 void KisAutoBrushWidget::slotCurvePropertyChanged(const QString &value)
0177 {
0178     KisCubicCurve curve;
0179 
0180     if (!value.isEmpty()) {
0181         curve = KisCubicCurve(value);
0182     } else {
0183         curve.setPoint(0, QPointF(0.0, 1.0));
0184         curve.setPoint(1, QPointF(1.0, 0.0));
0185     }
0186 
0187     KisSignalsBlocker b(softnessCurve);
0188     softnessCurve->setCurve(curve);
0189 }
0190 
0191 void KisAutoBrushWidget::slotUpdateBrushPreview()
0192 {
0193     KisAutoBrushFactory factory;
0194 
0195     QSharedPointer<KisAutoBrush> brush =
0196         factory.createBrush(*m_d->model->m_commonData,
0197                             m_d->model->bakedOptionData(),
0198                             KisGlobalResourcesInterface::instance())
0199             .resource<KisAutoBrush>();
0200 
0201     QImage pi(brush->image());
0202 
0203     double coeff = 1.0;
0204     int bPw = brushPreview->width() - 3;
0205     if (pi.width() > bPw) {
0206         coeff =  bPw / (double)pi.width();
0207     }
0208     int bPh = brushPreview->height() - 3;
0209     if (pi.height() > coeff * bPh) {
0210         coeff = bPh / (double)pi.height();
0211     }
0212     if (coeff < 1.0) {
0213         pi = pi.scaled((int)(coeff * pi.width()) , (int)(coeff * pi.height()),  Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0214     }
0215 
0216     QPixmap p = QPixmap::fromImage(pi);
0217     brushPreview->setIcon(QIcon(p));
0218 }
0219 
0220 KisBrushSP KisAutoBrushWidget::brush()
0221 {
0222     KisAutoBrushFactory factory;
0223 
0224     QSharedPointer<KisAutoBrush> brush =
0225         factory.createBrush(*m_d->model->m_commonData,
0226                             m_d->model->bakedOptionData(),
0227                             KisGlobalResourcesInterface::instance())
0228             .resource<KisAutoBrush>();
0229 
0230     return brush;
0231 }