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

0001 /*
0002  *  SPDX-FileCopyrightText: 2005, 2008 Cyrille Berger <cberger@cberger.net>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_convolution_kernel.h"
0008 
0009 #include <math.h>
0010 
0011 #include <QImage>
0012 #include <kis_mask_generator.h>
0013 
0014 struct Q_DECL_HIDDEN KisConvolutionKernel::Private {
0015     qreal offset;
0016     qreal factor;
0017     Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> data;
0018 };
0019 
0020 KisConvolutionKernel::KisConvolutionKernel(quint32 _width, quint32 _height, qreal _offset, qreal _factor) : d(new Private)
0021 {
0022     d->offset = _offset;
0023     d->factor = _factor;
0024     setSize(_width, _height);
0025 }
0026 
0027 KisConvolutionKernel::~KisConvolutionKernel()
0028 {
0029     delete d;
0030 }
0031 
0032 quint32 KisConvolutionKernel::width() const
0033 {
0034     return d->data.cols();
0035 }
0036 
0037 quint32 KisConvolutionKernel::height() const
0038 {
0039     return d->data.rows();
0040 }
0041 
0042 void KisConvolutionKernel::setSize(quint32 width, quint32 height)
0043 {
0044     d->data.resize(height, width);
0045 }
0046 
0047 
0048 qreal KisConvolutionKernel::offset() const
0049 {
0050     return d->offset;
0051 }
0052 
0053 qreal KisConvolutionKernel::factor() const
0054 {
0055     return d->factor;
0056 }
0057 
0058 void KisConvolutionKernel::setFactor(qreal factor)
0059 {
0060     d->factor = factor;
0061 }
0062 
0063 Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>& KisConvolutionKernel::data()
0064 {
0065     return d->data;
0066 }
0067 
0068 const Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>* KisConvolutionKernel::data() const
0069 {
0070     return &(d->data);
0071 }
0072 
0073 KisConvolutionKernelSP KisConvolutionKernel::fromQImage(const QImage& image)
0074 {
0075     KisConvolutionKernelSP kernel = new KisConvolutionKernel(image.width(), image.height(), 0, 0);
0076 
0077     Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>& data = kernel->data();
0078     const quint8* itImage = image.constBits();
0079     qreal factor = 0;
0080 
0081     for (int r = 0; r < image.height(); r++) {
0082         for (int c = 0; c < image.width(); c++, itImage += 4)
0083         {
0084             uint value = 255 - (*itImage + *(itImage + 1) + *(itImage + 2)) / 3;
0085             data(r, c) = value;
0086             factor += value;
0087         }
0088     }
0089 
0090     kernel->setFactor(factor);
0091     return kernel;
0092 }
0093 
0094 KisConvolutionKernelSP KisConvolutionKernel::fromMaskGenerator(KisMaskGenerator* kmg, qreal angle)
0095 {
0096     Q_UNUSED(angle);
0097 
0098     qint32 width = (int)(kmg->width() + 0.5);
0099     qint32 height = (int)(kmg->height() + 0.5);
0100 
0101     KisConvolutionKernelSP kernel = new KisConvolutionKernel(width, height, 0, 0);
0102 
0103     qreal cosa = cos(angle);
0104     qreal sina = sin(angle);
0105     qreal xc = 0.5 * width - 0.5;
0106     qreal yc = 0.5 * height - 0.5;
0107 
0108     Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>& data = kernel->data();
0109     qreal factor = 0;
0110 
0111 //     dbgImage << ppVar(xc) << ppVar(yc);
0112     for (int r = 0; r < height; ++r) {
0113         for (int c = 0; c < width; ++c) {
0114             qreal x_ = (c - xc);
0115             qreal y_ = (r - yc);
0116             qreal x = cosa * x_ - sina * y_;
0117             qreal y = sina * x_ + cosa * y_;
0118 //             dbgImage << ppVar(x) << ppVar(y) << ppVar(x_) << ppVar(y_) << ppVar( kmg->interpolatedValueAt( x,y) );
0119             uint value = 255 - kmg->valueAt(x, y);
0120             data(r, c) = value;
0121             factor += value;
0122         }
0123     }
0124     kernel->setFactor(factor);
0125     return kernel;
0126 }
0127 
0128 KisConvolutionKernelSP KisConvolutionKernel::fromMatrix(Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> matrix, qreal offset, qreal factor)
0129 {
0130     KisConvolutionKernelSP kernel = new KisConvolutionKernel(matrix.cols(), matrix.rows(), offset, factor);
0131     kernel->data() = matrix;
0132 
0133     return kernel;
0134 }
0135 
0136 
0137 
0138 
0139 #if 0
0140 double xr = (x /*- m_xcenter*/);
0141 double yr = (y /*- m_ycenter*/);
0142 double n = norme(xr * m_xcoef, yr * m_ycoef);
0143 if (n > 1)
0144 {
0145     return 255;
0146 } else
0147 {
0148     double normeFade = norme(xr * m_xfadecoef, yr * m_yfadecoef);
0149     if (normeFade > 1) {
0150         double xle, yle;
0151         // xle stands for x-coordinate limit exterior
0152         // yle stands for y-coordinate limit exterior
0153         // we are computing the coordinate on the external ellipse in order to compute
0154         // the fade value
0155         if (xr == 0) {
0156             xle = 0;
0157             yle = yr > 0 ? 1 / m_ycoef : -1 / m_ycoef;
0158         } else {
0159             double c = yr / (double)xr;
0160             xle = sqrt(1 / norme(m_xcoef, c * m_ycoef));
0161             xle = xr > 0 ? xle : -xle;
0162             yle = xle * c;
0163         }
0164         // On the internal limit of the fade area, normeFade is equal to 1
0165         double normeFadeLimitE = norme(xle * m_xfadecoef, yle * m_yfadecoef);
0166         return (uchar)(255 *(normeFade - 1) / (normeFadeLimitE - 1));
0167     } else {
0168         return 0;
0169     }
0170 }
0171 #endif
0172 
0173 #include "kis_debug.h"
0174 
0175 QDebug operator<<(QDebug debug, const KisConvolutionKernel &c)
0176 {
0177     debug.nospace() << "[" << c.width() << "," << c.height() << "]{";
0178     for (unsigned int i = 0; i < c.width(); ++i) {
0179         debug.nospace() << " {";
0180         for (unsigned int j = 0; j < c.height(); ++j) {
0181             debug.nospace() << (*(c.data()))(j, i) << " ";
0182         }
0183         debug.nospace() << " }";
0184     }
0185     debug.nospace() << c.factor() << " " << c.offset() <<  " }";
0186     return debug.space();
0187 }