diff --git a/libs/image/kis_edge_detection_kernel.cpp b/libs/image/kis_edge_detection_kernel.cpp index 2596140852..e9f0a4710e 100644 --- a/libs/image/kis_edge_detection_kernel.cpp +++ b/libs/image/kis_edge_detection_kernel.cpp @@ -1,428 +1,496 @@ /* * 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 #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 analog 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()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); 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(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { 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(); finalNorm[alphaPos] = 1.0; device->colorSpace()->fromNormalisedChannelsValue(f, finalNorm); memcpy(finalIt.rawData(), f, pixelSize); } } } 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()); denormalised->prepareClone(device); 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 iterator(denormalised, rect); KisSequentialIterator finalIt(device, rect); const int pixelSize = device->colorSpace()->pixelSize(); const int channels = device->colorSpace()->colorChannelCount(); QVector normalised(channels); while (iterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(iterator.rawData(), normalised); KoColor col(finalIt.rawData(), device->colorSpace()); qreal alpha = 0; for (int c = 0; ccolorSpace()->setOpacity(finalIt.rawData(), 1.0, numConseqPixels); } } } } +void KisEdgeDetectionKernel::applyCannyEdgeDetection(KisPaintDeviceSP device, + const QRect& rect, + qreal xRadius, qreal yRadius, + const QBitArray &channelFlags, + KoUpdater *progressUpdater, + bool writeToAlpha) +{ + QPoint srcTopLeft = rect.topLeft(); + KisPainter finalPainter(device); + finalPainter.setChannelFlags(channelFlags); + finalPainter.setProgress(progressUpdater); + KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); + KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); + + x_denormalised->prepareClone(device); + y_denormalised->prepareClone(device); + + + KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(xRadius, KisEdgeDetectionKernel::SobelVector); + KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(yRadius, KisEdgeDetectionKernel::SobelVector); + + 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); + const int pixelSize = device->colorSpace()->pixelSize(); + const int channels = device->colorSpace()->channelCount(); + const int alphaPos = device->colorSpace()->alphaPos(); + KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); + + QVector yNormalised(channels); + QVector xNormalised(channels); + QVector finalNorm(channels); + QVector edgeDirection(channels); + + while(yItterator.nextPixel() && xItterator.nextPixel()) { + device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); + device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); + + for (int c = 0; c channelOrder, QVector channelFlip, const QBitArray &channelFlags, KoUpdater *progressUpdater) { QPoint srcTopLeft = rect.topLeft(); KisPainter finalPainter(device); finalPainter.setChannelFlags(channelFlags); finalPainter.setProgress(progressUpdater); KisPaintDeviceSP x_denormalised = new KisPaintDevice(device->colorSpace()); KisPaintDeviceSP y_denormalised = new KisPaintDevice(device->colorSpace()); x_denormalised->prepareClone(device); y_denormalised->prepareClone(device); KisConvolutionKernelSP kernelHorizLeftRight = KisEdgeDetectionKernel::createHorizontalKernel(yRadius, type, true, !channelFlip[1]); KisConvolutionKernelSP kernelVerticalTopBottom = KisEdgeDetectionKernel::createVerticalKernel(xRadius, type, true, !channelFlip[0]); qreal horizontalCenter = qreal(kernelHorizLeftRight->width()) / 2.0; qreal verticalCenter = qreal(kernelVerticalTopBottom->height()) / 2.0; KisConvolutionPainter horizPainterLR(y_denormalised); horizPainterLR.setChannelFlags(channelFlags); horizPainterLR.setProgress(progressUpdater); horizPainterLR.applyMatrix(kernelHorizLeftRight, device, srcTopLeft - QPoint(ceil(horizontalCenter), 0), srcTopLeft - QPoint(ceil(horizontalCenter), 0), rect.size() + QSize(2 * ceil(horizontalCenter), 0), BORDER_REPEAT); KisConvolutionPainter verticalPainterTB(x_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(); const int alphaPos = device->colorSpace()->alphaPos(); KIS_SAFE_ASSERT_RECOVER_RETURN(alphaPos >= 0); QVector yNormalised(channels); QVector xNormalised(channels); QVector finalNorm(channels); while(yItterator.nextPixel() && xItterator.nextPixel() && finalIt.nextPixel()) { device->colorSpace()->normalisedChannelsValue(yItterator.rawData(), yNormalised); device->colorSpace()->normalisedChannelsValue(xItterator.rawData(), xNormalised); qreal z = 1.0; if (channelFlip[2]==true){ z=-1.0; } QVector3D normal = QVector3D((xNormalised[channelToConvert]-0.5)*2, (yNormalised[channelToConvert]-0.5)*2, z); normal.normalize(); finalNorm.fill(1.0); for (int c = 0; c<3; c++) { finalNorm[device->colorSpace()->channels().at(channelOrder[c])->displayPosition()] = (normal[channelOrder[c]]/2)+0.5; } finalNorm[alphaPos]= 1.0; quint8* pixel = finalIt.rawData(); device->colorSpace()->fromNormalisedChannelsValue(pixel, finalNorm); memcpy(finalIt.rawData(), pixel, pixelSize); } } diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h index 9b1f44e9f0..b412e5ce0a 100644 --- a/libs/image/kis_edge_detection_kernel.h +++ b/libs/image/kis_edge_detection_kernel.h @@ -1,139 +1,151 @@ /* * 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. SobelVector //Sobel 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, sobel 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 output the output mode. * @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); + /** + * @brief applyCannyEdgeDetection + * This applies the canny 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 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 applyCannyEdgeDetection(KisPaintDeviceSP device, const QRect& rect, qreal xRadius, qreal yRadius, const QBitArray &channelFlags, KoUpdater *progressUpdater, bool writeToAlpha = false); /** * @brief converToNormalMap * Convert a channel of the device to a normal map. The channel will be interpreted as a heightmap. * @param device the device * @param rect the rectangle to apply this to. * @param xRadius the xradius * @param yRadius the yradius * @param type the edge detection filter. * @param channelToConvert the channel to use as a grayscale. * @param channelOrder the order in which the xyz coordinates ought to be written to the pixels. * @param channelFlip whether to flip the channels * @param channelFlags the channel flags * @param progressUpdater */ static void convertToNormalMap(KisPaintDeviceSP device, const QRect & rect, qreal xRadius, qreal yRadius, FilterType type, int channelToConvert, QVector channelOrder, QVector channelFlip, const QBitArray &channelFlags, KoUpdater *progressUpdater); }; #endif // KIS_EDGE_DETECTION_KERNEL_H diff --git a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp index 362dae39da..6117c4d84d 100644 --- a/plugins/filters/edgedetection/kis_edge_detection_filter.cpp +++ b/plugins/filters/edgedetection/kis_edge_detection_filter.cpp @@ -1,163 +1,164 @@ /* * 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 #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(), FiltersCategoryEdgeDetectionId, 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); qreal horizontalRadius = t.scale(value.toFloat()); configuration->getProperty("vertRadius", value); qreal verticalRadius = t.scale(value.toFloat()); QBitArray channelFlags; if (configuration) { channelFlags = configuration->channelFlags(); } - + qDebug() << "It works"; if(config->getString("type") == "canny") { + KisEdgeDetectionKernel::applyCannyEdgeDetection(device, rect, horizontalRadius, verticalRadius, channelFlags, progressUpdater); return; } KisEdgeDetectionKernel::FilterType type = KisEdgeDetectionKernel::SobelVector; if (config->getString("type") == "prewitt") { type = KisEdgeDetectionKernel::Prewit; } 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::defaultConfiguration() const { KisFilterConfigurationSP config = factoryConfiguration(); 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, bool) const { Q_UNUSED(dev); return new KisWdgEdgeDetection(parent); } QRect KisEdgeDetectionFilter::neededRect(const QRect &rect, const KisFilterConfigurationSP _config, int lod) const { KisLodTransformScalar t(lod); QVariant value; /** * NOTE: integer division 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"