diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -140,6 +140,7 @@ kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp + kis_edge_detection_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_base.cpp diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h new file mode 100644 --- /dev/null +++ b/libs/image/kis_edge_detection_kernel.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 KIS_EDGE_DETECTION_KERNEL_H +#define KIS_EDGE_DETECTION_KERNEL_H + +#include "kritaimage_export.h" +#include "kis_types.h" + +#include + +class QRect; + +class KRITAIMAGE_EXPORT KisEdgeDetectionKernel +{ +public: + KisEdgeDetectionKernel(); + + enum FilterType { + Simple, //A weird simple method used in our old sobel filter + Prewit, //The simpler prewitt detection, which doesn't smooth. + SobolVector //Sobol does smooth. The creation of bigger kernels is based on an approach regarding vectors. + }; + + enum FilterOutput { + pythagorean, + xGrowth, + xFall, + yGrowth, + yFall, + radian + }; + + /** + * @brief createHorizontalMatrix + * @param radius the radius. 1 makes a 3x3 kernel. + * @param type One of the entries in the enum Filtertype + * @param reverse which direction the gradient goes. + * The horizontal gradient by default detects the rightmost edges. + * Reversed it selects the leftmost edges. + * @return + */ + + static Eigen::Matrix + createHorizontalMatrix(qreal radius, FilterType type, bool reverse = false); + /** + * @brief createVerticalMatrix + * @param radius the radius. 1 makes a 3x3 kernel. + * @param type One of the entries in the enum Filtertype + * @param reverse which direction the gradient goes. + * The vertical gradient by default detects the topmost edges. + * Reversed it selects the bottommost edges. + * @return + */ + static Eigen::Matrix + createVerticalMatrix(qreal radius, FilterType type, bool reverse = false); + + static KisConvolutionKernelSP + createHorizontalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); + + static KisConvolutionKernelSP + createVerticalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false); + + static int kernelSizeFromRadius(qreal radius); + static qreal sigmaFromRadius(qreal radius); + + /** + * @brief applyEdgeDetection + * This applies the edge detection filter to the device. + * @param device the device to apply to. + * @param rect the affected rect. + * @param xRadius the radius of the horizontal sampling, radius of 0 is effectively disabling it. + * @param yRadius the radius of the vertical sampling, refius of 0 is effectively disabling it. + * @param type the type can be prewitt, sobol or simple, each of which + * have a different sampling for the eventual edge detection. + * @param channelFlags the affected channels. + * @param progressUpdater the progress updater if it exists. + * @param writeToAlpha whether or not to have the result applied to the transparency than the color channels, + * this is useful for fringe effects. + */ + static void applyEdgeDetection(KisPaintDeviceSP device, + const QRect& rect, + qreal xRadius, qreal yRadius, + FilterType type, + const QBitArray &channelFlags, + KoUpdater *progressUpdater, + FilterOutput output = pythagorean, + bool writeToAlpha = false); +}; + +#endif // KIS_EDGE_DETECTION_KERNEL_H diff --git a/libs/image/kis_edge_detection_kernel.cpp b/libs/image/kis_edge_detection_kernel.cpp new file mode 100644 --- /dev/null +++ b/libs/image/kis_edge_detection_kernel.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "kis_edge_detection_kernel.h" +#include "kis_global.h" +#include "kis_convolution_kernel.h" +#include +#include +#include +#include +#include + +KisEdgeDetectionKernel::KisEdgeDetectionKernel() +{ + +} +/* + * This code is very similar to the gaussian kernel code, except unlike the gaussian code, + * edge-detection kernels DO use the diagonals. + * Except for the simple mode. We implement the simple mode because it is an analogue to + * the old sobel filter. + */ + +Eigen::Matrix KisEdgeDetectionKernel::createHorizontalMatrix(qreal radius, + FilterType type, + bool reverse) +{ + int kernelSize = kernelSizeFromRadius(radius); + Eigen::Matrix matrix(kernelSize, kernelSize); + + KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); + const int center = kernelSize / 2; + + if (type==Prewit) { + for (int x = 0; x < kernelSize; x++) { + for (int y=0; y KisEdgeDetectionKernel::createVerticalMatrix(qreal radius, + FilterType type, + bool reverse) +{ + int kernelSize = kernelSizeFromRadius(radius); + + Eigen::Matrix matrix(kernelSize, kernelSize); + KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1); + const int center = kernelSize / 2; + + if (type==Prewit) { + for (int y = 0; y < kernelSize; y++) { + for (int x=0; x matrix = createHorizontalMatrix(radius, type, reverse); + if (denormalize) { + return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); + } else { + return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); + } +} + +KisConvolutionKernelSP KisEdgeDetectionKernel::createVerticalKernel(qreal radius, + KisEdgeDetectionKernel::FilterType type, + bool denormalize, + bool reverse) +{ + Eigen::Matrix matrix = createVerticalMatrix(radius, type, reverse); + if (denormalize) { + return KisConvolutionKernel::fromMatrix(matrix, 0.5, 1); + } else { + return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum()); + } +} + +int KisEdgeDetectionKernel::kernelSizeFromRadius(qreal radius) +{ + return qMax((int)(2 * ceil(sigmaFromRadius(radius)) + 1), 3); +} + +qreal KisEdgeDetectionKernel::sigmaFromRadius(qreal radius) +{ + return 0.3 * radius + 0.3; +} + +void KisEdgeDetectionKernel::applyEdgeDetection(KisPaintDeviceSP device, + const QRect &rect, + qreal xRadius, + qreal yRadius, + KisEdgeDetectionKernel::FilterType type, + const QBitArray &channelFlags, + KoUpdater *progressUpdater, + FilterOutput output, + bool writeToAlpha) +{ + QPoint srcTopLeft = rect.topLeft(); + KisPainter finalPainter(device); + finalPainter.setChannelFlags(channelFlags); + finalPainter.setProgress(progressUpdater); + if (output == pythagorean || output == radian) { + KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); + KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); + + KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type); + KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type); + + qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; + qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; + + KisConvolutionPainter horizPainterLR(x_denormalised); + horizPainterLR.setChannelFlags(channelFlags); + horizPainterLR.setProgress(progressUpdater); + horizPainterLR.applyMatrix(kernelHorizLeftRight, device, + srcTopLeft - QPoint(0, ceil(horizontalCenter)), + srcTopLeft - QPoint(0, ceil(horizontalCenter)), + rect.size() + QSize(0, 2 * ceil(horizontalCenter)), BORDER_REPEAT); + + + KisConvolutionPainter verticalPainterTB(y_denormalised); + verticalPainterTB.setChannelFlags(channelFlags); + verticalPainterTB.setProgress(progressUpdater); + verticalPainterTB.applyMatrix(kernelVerticalTopBottom, device, + srcTopLeft - QPoint(0, ceil(verticalCenter)), + srcTopLeft - QPoint(0, ceil(verticalCenter)), + rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT); + + KisSequentialIterator yItterator(y_denormalised, rect); + KisSequentialIterator xItterator(x_denormalised, rect); + KisSequentialIterator finalIt(device, rect); + const int pixelSize = device->colorSpace()->pixelSize(); + const int channels = device->colorSpace()->channelCount(); + QVector yNormalised(channels); + QVector xNormalised(channels); + QVector finalNorm(channels); + + do { + device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); + device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); + device->colorSpace()->normalisedChannelsValue(finalIt.rawData(), finalNorm); + + if (output == pythagorean) { + for (int c = 0; ccolorSpace()); + qreal alpha = 0; + + for (int c = 0; c<(channels-1); c++) { + alpha = alpha+finalNorm[c]; + } + + alpha = qMin(alpha/(channels-1), col.opacityF()); + col.setOpacity(alpha); + memcpy(finalIt.rawData(), col.data(), pixelSize); + } else { + quint8* f = finalIt.rawData(); + device->colorSpace()->fromNormalisedChannelsValue(f, finalNorm); + memcpy(finalIt.rawData(), f, pixelSize); + } + + } while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()); + } else { + KisConvolutionKernelSP kernel; + qreal center = 0; + bool denormalize = !writeToAlpha; + if (output == xGrowth) { + kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize); + center = qreal(kernel->width()) / 2.0; + } else if (output == xFall) { + kernel = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, type, denormalize, true); + center = qreal(kernel->width()) / 2.0; + } else if (output == yGrowth) { + kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize); + center = qreal(kernel->height()) / 2.0; + } else { //yFall + kernel = KisEdgeDetectionKernel::createVerticalKernel(yRadius, type, denormalize, true); + center = qreal(kernel->height()) / 2.0; + } + + if (writeToAlpha) { + KisPaintDeviceSP denormalised = new KisPaintDevice(device->colorSpace()); + KisConvolutionPainter kernelP(denormalised); + kernelP.setChannelFlags(channelFlags); + kernelP.setProgress(progressUpdater); + kernelP.applyMatrix(kernel, device, + srcTopLeft - QPoint(0, ceil(center)), + srcTopLeft - QPoint(0, ceil(center)), + rect.size() + QSize(0, 2 * ceil(center)), BORDER_REPEAT); + KisSequentialIterator itterator(denormalised, rect); + KisSequentialIterator finalIt(device, rect); + const int pixelSize = device->colorSpace()->pixelSize(); + const int channels = device->colorSpace()->colorChannelCount(); + QVector normalised(channels); + do { + device->colorSpace()->normalisedChannelsValue(itterator.rawData(), normalised); + KoColor col(finalIt.rawData(), device->colorSpace()); + qreal alpha = 0; + for (int c = 0; c + * + * 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 KIS_EDGE_DETECTION_FILTER_H +#define KIS_EDGE_DETECTION_FILTER_H + +#include "filter/kis_filter.h" + +#include + +class KritaEdgeDetectionFilter : public QObject +{ + Q_OBJECT +public: + KritaEdgeDetectionFilter(QObject *parent, const QVariantList &); + ~KritaEdgeDetectionFilter() override; +}; + +class KisEdgeDetectionFilter : public KisFilter +{ +public: + KisEdgeDetectionFilter(); + void processImpl(KisPaintDeviceSP device, + const QRect& rect, + const KisFilterConfigurationSP config, + KoUpdater* progressUpdater + ) const override; + static inline KoID id() { + return KoID("edge detection", i18n("Edge Detection")); + } + + KisFilterConfigurationSP factoryConfiguration() const override; +public: + KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; + QRect neededRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override; + QRect changedRect(const QRect & rect, const KisFilterConfigurationSP _config, int lod) const override; +}; + +#endif // KIS_EDGE_DETECTION_FILTER_H diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp new file mode 100644 --- /dev/null +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "kis_edge_detection_filter.h" +#include "kis_wdg_edge_detection.h" +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "kis_lod_transform.h" + +#include + +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KritaEdgeDetectionFilterFactory, "kritaedgedetection.json", registerPlugin();) + +KritaEdgeDetectionFilter::KritaEdgeDetectionFilter(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KisFilterRegistry::instance()->add(KisFilterSP(new KisEdgeDetectionFilter())); +} + +KritaEdgeDetectionFilter::~KritaEdgeDetectionFilter() +{ +} + +KisEdgeDetectionFilter::KisEdgeDetectionFilter(): KisFilter(id(), categoryEdgeDetection(), i18n("&Edge Detection...")) +{ + setSupportsPainting(true); + setSupportsAdjustmentLayers(true); + setSupportsLevelOfDetail(true); + setColorSpaceIndependence(FULLY_INDEPENDENT); + setShowConfigurationWidget(true); +} + +void KisEdgeDetectionFilter::processImpl(KisPaintDeviceSP device, const QRect &rect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const +{ + Q_ASSERT(device != 0); + + KisFilterConfigurationSP configuration = config ? config : new KisFilterConfiguration(id().id(), 1); + + KisLodTransformScalar t(device); + + QVariant value; + configuration->getProperty("horizRadius", value); + float horizontalRadius = t.scale(value.toFloat()); + configuration->getProperty("vertRadius", value); + float verticalRadius = t.scale(value.toFloat()); + + QBitArray channelFlags; + if (configuration) { + channelFlags = configuration->channelFlags(); + } + if (channelFlags.isEmpty() || !configuration) { + channelFlags = device->colorSpace()->channelFlags(); + } + + KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobolVector; + if (config->getString("type") == "prewitt") { + type = KisEdgeDetectionKernel::Prewit; + } else if (config->getString("type") == "simple") { + type = KisEdgeDetectionKernel::Simple; + } + + KisEdgeDetectionKernel::FilterOutput output = KisEdgeDetectionKernel::pythagorean; + if (config->getString("output") == "xGrowth") { + output = KisEdgeDetectionKernel::xGrowth; + } else if (config->getString("output") == "xFall") { + output = KisEdgeDetectionKernel::xFall; + } else if (config->getString("output") == "yGrowth") { + output = KisEdgeDetectionKernel::yGrowth; + } else if (config->getString("output") == "yFall") { + output = KisEdgeDetectionKernel::yFall; + } else if (config->getString("output") == "radian") { + output = KisEdgeDetectionKernel::radian; + } + + KisEdgeDetectionKernel::applyEdgeDetection(device, + rect, + horizontalRadius, + verticalRadius, + type, + channelFlags, + progressUpdater, + output, + config->getBool("transparency", false)); +} + +KisFilterConfigurationSP KisEdgeDetectionFilter::factoryConfiguration() const +{ + KisFilterConfigurationSP config = new KisFilterConfiguration(id().id(), 1); + config->setProperty("horizRadius", 1); + config->setProperty("vertRadius", 1); + config->setProperty("type", "prewitt"); + config->setProperty("output", "pythagorean"); + config->setProperty("lockAspect", true); + config->setProperty("transparency", false); + + return config; +} + +KisConfigWidget *KisEdgeDetectionFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const +{ + return new KisWdgEdgeDetection(parent); +} + +QRect KisEdgeDetectionFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const +{ + KisLodTransformScalar t(lod); + + QVariant value; + /** + * NOTE: integer devision by two is done on purpose, + * because the kernel size is always odd + */ + const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + + return rect.adjusted(-halfWidth * 2, -halfHeight * 2, halfWidth * 2, halfHeight * 2); +} + +QRect KisEdgeDetectionFilter::changedRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const +{ + KisLodTransformScalar t(lod); + + QVariant value; + + const int halfWidth = _config->getProperty("horizRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + const int halfHeight = _config->getProperty("vertRadius", value) ? KisEdgeDetectionKernel::kernelSizeFromRadius(t.scale(value.toFloat())) / 2 : 5; + + return rect.adjusted( -halfWidth, -halfHeight, halfWidth, halfHeight); +} + +#include "kis_edge_detection_filter.moc" diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.h b/plugins/filters/edgedetection/kis_wdg_edge_detection.h new file mode 100644 --- /dev/null +++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 KIS_WDG_EDGE_DETECTION_H +#define KIS_WDG_EDGE_DETECTION_H + +#include +#include +#include "ui_wdg_edge_detection.h" + +class Ui_WidgetEdgeDetection; + + +class KisWdgEdgeDetection : public KisConfigWidget +{ + Q_OBJECT + +public: + explicit KisWdgEdgeDetection(QWidget *parent); + ~KisWdgEdgeDetection(); + + KisPropertiesConfigurationSP configuration() const override; + void setConfiguration(const KisPropertiesConfigurationSP config) override; + +private: + Ui_WidgetEdgeDetection *ui; + QStringList m_types; + QStringList m_types_translatable; + QStringList m_output; + QStringList m_output_translatable; + +private Q_SLOTS: + void horizontalRadiusChanged(qreal r); + void verticalRadiusChanged(qreal r); + void aspectLockChanged(bool v); +}; + +#endif // KIS_WDG_EDGE_DETECTION_H diff --git a/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp new file mode 100644 --- /dev/null +++ b/plugins/filters/edgedetection/kis_wdg_edge_detection.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 Wolthera van Hövell tot Westerflier + * + * 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 "kis_wdg_edge_detection.h" + +#include +#include +#include + +KisWdgEdgeDetection::KisWdgEdgeDetection(QWidget *parent) : + KisConfigWidget(parent), + ui(new Ui_WidgetEdgeDetection) +{ + ui->setupUi(this); + + m_types << "prewitt"<< "sobol"<< "simple"; + m_types_translatable << i18n("Prewitt") << i18n("Sobol") << i18n("Simple"); + m_output << "pythagorean" << "xGrowth" << "xFall" << "yGrowth" << "yFall" << "radian"; + m_output_translatable << i18n("All sides") + << i18n("Top Edge") + << i18n("Bottom Edge") + << i18n("Right Edge") + << i18n("Left Edge") + << i18n("Direction in Radians"); + + ui->cmbType->addItems(m_types_translatable); + ui->cmbOutput->addItems(m_output_translatable); + + ui->btnAspect->setKeepAspectRatio(false); + ui->sldHorizontalRadius->setRange(1.0, 100.0, 2); + ui->sldHorizontalRadius->setPrefix(i18n("Horizontal Radius:")); + connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(horizontalRadiusChanged(qreal))); + + ui->sldVerticalRadius->setRange(1.0, 100.0, 2); + ui->sldVerticalRadius->setPrefix(i18n("Vertical Radius:")); + connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SLOT(verticalRadiusChanged(qreal))); + + + connect(ui->btnAspect, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectLockChanged(bool))); + connect(ui->cmbType, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->cmbOutput, SIGNAL(currentIndexChanged(int)), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->sldHorizontalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->sldVerticalRadius, SIGNAL(valueChanged(qreal)), this, SIGNAL(sigConfigurationItemChanged())); + connect(ui->chkTransparent, SIGNAL(clicked()), this, SIGNAL(sigConfigurationItemChanged())); +} + +KisWdgEdgeDetection::~KisWdgEdgeDetection() +{ + delete ui; +} + +KisPropertiesConfigurationSP KisWdgEdgeDetection::configuration() const +{ + KisFilterConfigurationSP config = new KisFilterConfiguration("edge detection", 1); + config->setProperty("horizRadius", ui->sldHorizontalRadius->value()); + config->setProperty("vertRadius", ui->sldVerticalRadius->value()); + config->setProperty("type", m_types.at(ui->cmbType->currentIndex())); + config->setProperty("output", m_output.at(ui->cmbOutput->currentIndex())); + config->setProperty("lockAspect", ui->btnAspect->keepAspectRatio()); + config->setProperty("transparency", ui->chkTransparent->isChecked()); + + return config; +} + +void KisWdgEdgeDetection::setConfiguration(const KisPropertiesConfigurationSP config) +{ + ui->sldHorizontalRadius->setValue(config->getFloat("horizRadius", 1.0)); + ui->sldVerticalRadius->setValue(config->getFloat("vertRadius", 1.0)); + int index = 0; + if (m_types.contains(config->getString("type", "prewitt"))){ + index = m_types.indexOf(config->getString("type", "prewitt")); + } + ui->cmbType->setCurrentIndex(index); + index = 0; + if (m_output.contains(config->getString("output", "pythagorean"))){ + index = m_output.indexOf(config->getString("output", "pythagorean")); + } + ui->cmbOutput->setCurrentIndex(index); + ui->chkTransparent->setChecked(config->getBool("transparency", false)); + ui->btnAspect->setKeepAspectRatio(config->getBool("lockAspect", false)); + +} + +void KisWdgEdgeDetection::horizontalRadiusChanged(qreal r) +{ + ui->sldHorizontalRadius->blockSignals(true); + ui->sldHorizontalRadius->setValue(r); + ui->sldHorizontalRadius->blockSignals(false); + + if (ui->btnAspect->keepAspectRatio()) { + ui->sldVerticalRadius->blockSignals(true); + ui->sldVerticalRadius->setValue(r); + ui->sldVerticalRadius->blockSignals(false); + } +} + +void KisWdgEdgeDetection::verticalRadiusChanged(qreal r) +{ + ui->sldVerticalRadius->blockSignals(true); + ui->sldVerticalRadius->setValue(r); + ui->sldVerticalRadius->blockSignals(false); + + if (ui->btnAspect->keepAspectRatio()) { + ui->sldHorizontalRadius->blockSignals(true); + ui->sldHorizontalRadius->setValue(r); + ui->sldHorizontalRadius->blockSignals(false); + } +} + +void KisWdgEdgeDetection::aspectLockChanged(bool v) +{ + if (v) { + ui->sldVerticalRadius->setValue( ui->sldHorizontalRadius->value() ); + } +} diff --git a/plugins/filters/edgedetection/kritaedgedetection.json b/plugins/filters/edgedetection/kritaedgedetection.json new file mode 100644 --- /dev/null +++ b/plugins/filters/edgedetection/kritaedgedetection.json @@ -0,0 +1,9 @@ +{ + "Id": "Edge Detection Filter", + "Type": "Service", + "X-KDE-Library": "kritaedgedetection", + "X-KDE-ServiceTypes": [ + "Krita/Filter" + ], + "X-Krita-Version": "40" +} diff --git a/plugins/filters/edgedetection/wdg_edge_detection.ui b/plugins/filters/edgedetection/wdg_edge_detection.ui new file mode 100644 --- /dev/null +++ b/plugins/filters/edgedetection/wdg_edge_detection.ui @@ -0,0 +1,124 @@ + + + WidgetEdgeDetection + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Output decides what type of information you want from the lines. + + + + + + + The formula decides how the kernel is created, so different formulas choose different values in the kernel, and thus give subtly different results. + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + This will take a desaturated result and use it to determine what is transparent. This is useful for creating edge and fringe effects. + + + Apply result to alpha channel + + + + + + + Formula: + + + + + + + + + + Output: + + + + + + + + 0 + 0 + + + + + 5 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + KoAspectButton + QWidget +
KoAspectButton.h
+ 1 +
+ + KisDoubleSliderSpinBox + QWidget +
kis_slider_spin_box.h
+ 1 +
+
+ + +