File indexing completed on 2024-05-19 04:26:09

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