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 }