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 }