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 }