File indexing completed on 2024-05-12 15:58:12

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 Cyrille Berger <cberger@cberger.net>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_convolution_painter.h"
0008 
0009 #include <stdlib.h>
0010 #include <string.h>
0011 #include <cfloat>
0012 
0013 #include <QBrush>
0014 #include <QColor>
0015 #include <QPen>
0016 #include <QMatrix>
0017 #include <QImage>
0018 #include <QMap>
0019 #include <QPainter>
0020 #include <QRect>
0021 #include <QString>
0022 #include <QVector>
0023 
0024 #include <kis_debug.h>
0025 #include <klocalizedstring.h>
0026 
0027 #include "kis_convolution_kernel.h"
0028 #include "kis_global.h"
0029 #include "kis_image.h"
0030 #include "kis_layer.h"
0031 #include "kis_paint_device.h"
0032 #include "kis_painter.h"
0033 #include "KoColorSpace.h"
0034 #include <KoChannelInfo.h>
0035 #include "kis_types.h"
0036 
0037 #include "kis_selection.h"
0038 
0039 #include "kis_convolution_worker.h"
0040 #include "kis_convolution_worker_spatial.h"
0041 
0042 #include "config_convolution.h"
0043 
0044 #ifdef HAVE_FFTW3
0045 #include "kis_convolution_worker_fft.h"
0046 #endif
0047 
0048 
0049 bool KisConvolutionPainter::useFFTImplementation(const KisConvolutionKernelSP kernel) const
0050 {
0051     bool result = false;
0052 
0053 #ifdef HAVE_FFTW3
0054     #define THRESHOLD_SIZE 5
0055 
0056     result =
0057         m_enginePreference == FFTW ||
0058         (m_enginePreference == NONE &&
0059          (kernel->width() > THRESHOLD_SIZE ||
0060           kernel->height() > THRESHOLD_SIZE));
0061 #else
0062     Q_UNUSED(kernel);
0063 #endif
0064 
0065     return result;
0066 }
0067 
0068 template<class factory>
0069 KisConvolutionWorker<factory>* KisConvolutionPainter::createWorker(const KisConvolutionKernelSP kernel,
0070                                                                    KisPainter *painter,
0071                                                                    KoUpdater *progress)
0072 {
0073     KisConvolutionWorker<factory> *worker;
0074 
0075 #ifdef HAVE_FFTW3
0076     if (useFFTImplementation(kernel)) {
0077         worker = new KisConvolutionWorkerFFT<factory>(painter, progress);
0078     } else {
0079         worker = new KisConvolutionWorkerSpatial<factory>(painter, progress);
0080     }
0081 #else
0082     Q_UNUSED(kernel);
0083     worker = new KisConvolutionWorkerSpatial<factory>(painter, progress);
0084 #endif
0085 
0086     return worker;
0087 }
0088 
0089 
0090 bool KisConvolutionPainter::supportsFFTW()
0091 {
0092 #ifdef HAVE_FFTW3
0093     return true;
0094 #else
0095     return false;
0096 #endif
0097 }
0098 
0099 
0100 KisConvolutionPainter::KisConvolutionPainter()
0101     : KisPainter(),
0102       m_enginePreference(NONE)
0103 {
0104 }
0105 
0106 KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device)
0107     : KisPainter(device),
0108       m_enginePreference(NONE)
0109 {
0110 }
0111 
0112 KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device, KisSelectionSP selection)
0113     : KisPainter(device, selection),
0114       m_enginePreference(NONE)
0115 {
0116 }
0117 
0118 KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device, EnginePreference enginePreference)
0119     : KisPainter(device),
0120       m_enginePreference(enginePreference)
0121 {
0122 }
0123 
0124 void KisConvolutionPainter::setEnginePreference(EnginePreference value)
0125 {
0126     m_enginePreference = value;
0127 }
0128 
0129 void KisConvolutionPainter::applyMatrix(const KisConvolutionKernelSP kernel, const KisPaintDeviceSP src, QPoint srcPos, QPoint dstPos, QSize areaSize, KisConvolutionBorderOp borderOp)
0130 {
0131     /**
0132      * Force BORDER_IGNORE op for the wraparound mode,
0133      * because the paint device has its own special
0134      * iterators, which do everything for us.
0135      */
0136     if (src->defaultBounds()->wrapAroundMode()) {
0137         borderOp = BORDER_IGNORE;
0138     }
0139 
0140     // Determine whether we convolve border pixels, or not.
0141     switch (borderOp) {
0142     case BORDER_REPEAT: {
0143         /**
0144          * We don't use defaultBounds->topLevelWrapRect(), because
0145          * the main purpose of this wrapping is "getting expected
0146          * results when applying to the the layer". If a mask is bigger
0147          * than the image, then it should be wrapped around the mask
0148          * instead.
0149          */
0150         const QRect boundsRect = src->defaultBounds()->bounds();
0151         const QRect requestedRect = QRect(srcPos, areaSize);
0152         QRect dataRect = requestedRect | boundsRect;
0153 
0154         KIS_SAFE_ASSERT_RECOVER(boundsRect != KisDefaultBounds().bounds()) {
0155             dataRect = requestedRect | src->exactBounds();
0156         }
0157 
0158         /**
0159          * FIXME: Implementation can return empty destination device
0160          * on faults and has no way to report this. This will cause a crash
0161          * on sequential convolutions inside iteratiors.
0162          *
0163          * o implementation should do it's work or assert otherwise
0164          *   (or report the issue somehow)
0165          * o check other cases of the switch for the vulnerability
0166          */
0167 
0168         if(dataRect.isValid()) {
0169             KisConvolutionWorker<RepeatIteratorFactory> *worker;
0170             worker = createWorker<RepeatIteratorFactory>(kernel, this, progressUpdater());
0171             worker->execute(kernel, src, srcPos, dstPos, areaSize, dataRect);
0172             delete worker;
0173         }
0174         break;
0175     }
0176     case BORDER_IGNORE:
0177     default: {
0178         KisConvolutionWorker<StandardIteratorFactory> *worker;
0179         worker = createWorker<StandardIteratorFactory>(kernel, this, progressUpdater());
0180         worker->execute(kernel, src, srcPos, dstPos, areaSize, QRect());
0181         delete worker;
0182     }
0183     }
0184 }
0185 
0186 bool KisConvolutionPainter::needsTransaction(const KisConvolutionKernelSP kernel) const
0187 {
0188     return !useFFTImplementation(kernel);
0189 }