File indexing completed on 2024-05-19 04:24:17

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisColorfulBrush.h"
0008 
0009 
0010 KisColorfulBrush::KisColorfulBrush(const QString &filename)
0011     : KisScalingSizeBrush(filename)
0012 {
0013 }
0014 
0015 #include <KoColorSpaceMaths.h>
0016 #include <KoColorSpaceTraits.h>
0017 
0018 namespace {
0019 
0020 qreal estimateImageAverage(const QImage &image) {
0021     qint64 lightnessSum = 0;
0022     qint64 alphaSum = 0;
0023 
0024     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image.format() == QImage::Format_ARGB32, 0.5);
0025 
0026     for (int y = 0; y < image.height(); ++y) {
0027         const QRgb *pixel = reinterpret_cast<const QRgb*>(image.scanLine(y));
0028 
0029         for (int i = 0; i < image.width(); ++i) {
0030             lightnessSum += qRound(qGray(*pixel) * qAlpha(*pixel) / 255.0);
0031             alphaSum += qAlpha(*pixel);
0032             pixel++;
0033         }
0034     }
0035 
0036     if (alphaSum == 0) {
0037         return 0;
0038     }
0039     return 255.0 * qreal(lightnessSum) / alphaSum;
0040 }
0041 
0042 }
0043 
0044 qreal KisColorfulBrush::estimatedSourceMidPoint() const
0045 {
0046     return estimateImageAverage(KisBrush::brushTipImage());
0047 }
0048 
0049 qreal KisColorfulBrush::adjustedMidPoint() const
0050 {
0051     return estimateImageAverage(this->brushTipImage());
0052 }
0053 
0054 bool KisColorfulBrush::autoAdjustMidPoint() const
0055 {
0056     return m_autoAdjustMidPoint;
0057 }
0058 
0059 void KisColorfulBrush::setAutoAdjustMidPoint(bool autoAdjustMidPoint)
0060 {
0061     m_autoAdjustMidPoint = autoAdjustMidPoint;
0062 }
0063 
0064 QImage KisColorfulBrush::brushTipImage() const
0065 {
0066     QImage image = KisBrush::brushTipImage();
0067     if (isImageType() && brushApplication() != IMAGESTAMP) {
0068 
0069         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(image.format() == QImage::Format_ARGB32, image);
0070 
0071         const qreal adjustmentMidPoint =
0072                 m_autoAdjustMidPoint ?
0073                 estimateImageAverage(image) :
0074                 m_adjustmentMidPoint;
0075 
0076         if (qAbs(adjustmentMidPoint - 127.0) > 0.1 ||
0077             !qFuzzyIsNull(m_brightnessAdjustment) ||
0078             !qFuzzyIsNull(m_contrastAdjustment)) {
0079 
0080             const int half = KoColorSpaceMathsTraits<quint8>::halfValue;
0081             const int unit = KoColorSpaceMathsTraits<quint8>::unitValue;
0082 
0083             const qreal midX = adjustmentMidPoint;
0084             const qreal midY = m_brightnessAdjustment > 0 ?
0085                         KoColorSpaceMaths<qreal>::blend(unit, half, m_brightnessAdjustment) :
0086                         KoColorSpaceMaths<qreal>::blend(0, half, -m_brightnessAdjustment);
0087 
0088             qreal loA = 0.0;
0089             qreal hiA = 0.0;
0090 
0091             qreal loB = 0.0;
0092             qreal hiB = 255.0;
0093 
0094             if (!qFuzzyCompare(m_contrastAdjustment, 1.0)) {
0095                 if (m_contrastAdjustment > 0.0) {
0096                     loA = midY / (1.0 - m_contrastAdjustment) / midX;
0097                     hiA = (unit - midY) / (1.0 - m_contrastAdjustment) / (unit - midX);
0098                 } else {
0099                     loA = midY * (1.0 + m_contrastAdjustment) / midX;
0100                     hiA = (unit - midY) * (1.0 + m_contrastAdjustment) / (unit - midX);
0101                 }
0102 
0103                 loB = midY - midX * loA;
0104                 hiB = midY - midX * hiA;
0105             }
0106 
0107             for (int y = 0; y < image.height(); y++) {
0108                 QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
0109                 for (int x = 0; x < image.width(); x++) {
0110                     QRgb c = pixel[x];
0111 
0112                     int v = qGray(c);
0113 
0114                     if (v >= midX) {
0115                         v = qMin(unit, qRound(hiA * v + hiB));
0116                     } else {
0117                         v = qMax(0, qRound(loA * v + loB));
0118                     }
0119 
0120                     pixel[x] = qRgba(v, v, v, qAlpha(c));
0121                 }
0122             }
0123         } else {
0124             for (int y = 0; y < image.height(); y++) {
0125                 QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
0126                 for (int x = 0; x < image.width(); x++) {
0127                     QRgb c = pixel[x];
0128 
0129                     int v = qGray(c);
0130                     pixel[x] = qRgba(v, v, v, qAlpha(c));
0131                 }
0132             }
0133         }
0134     }
0135     return image;
0136 }
0137 
0138 void KisColorfulBrush::setAdjustmentMidPoint(quint8 value)
0139 {
0140     if (m_adjustmentMidPoint != value) {
0141         m_adjustmentMidPoint = value;
0142         clearBrushPyramid();
0143     }
0144 }
0145 
0146 void KisColorfulBrush::setBrightnessAdjustment(qreal value)
0147 {
0148     if (m_brightnessAdjustment != value) {
0149         m_brightnessAdjustment = value;
0150         clearBrushPyramid();
0151     }
0152 }
0153 
0154 void KisColorfulBrush::setContrastAdjustment(qreal value)
0155 {
0156     if (m_contrastAdjustment != value) {
0157         m_contrastAdjustment = value;
0158         clearBrushPyramid();
0159     }
0160 }
0161 
0162 bool KisColorfulBrush::isImageType() const
0163 {
0164     return brushType() == IMAGE || brushType() == PIPE_IMAGE;
0165 }
0166 
0167 quint8 KisColorfulBrush::adjustmentMidPoint() const
0168 {
0169     return m_adjustmentMidPoint;
0170 }
0171 
0172 qreal KisColorfulBrush::brightnessAdjustment() const
0173 {
0174     return m_brightnessAdjustment;
0175 }
0176 
0177 qreal KisColorfulBrush::contrastAdjustment() const
0178 {
0179     return m_contrastAdjustment;
0180 }
0181 
0182 #include <QDomElement>
0183 
0184 void KisColorfulBrush::toXML(QDomDocument& d, QDomElement& e) const
0185 {
0186     // legacy setting, now 'brushApplication' is used instead
0187     e.setAttribute("ColorAsMask", QString::number((int)(brushApplication() != IMAGESTAMP)));
0188 
0189     e.setAttribute("AdjustmentMidPoint", QString::number(m_adjustmentMidPoint));
0190     e.setAttribute("BrightnessAdjustment", QString::number(m_brightnessAdjustment));
0191     e.setAttribute("ContrastAdjustment", QString::number(m_contrastAdjustment));
0192     e.setAttribute("AutoAdjustMidPoint", QString::number(m_autoAdjustMidPoint));
0193     e.setAttribute("AdjustmentVersion", QString::number(2));
0194     KisBrush::toXML(d, e);
0195 }
0196 
0197 void KisColorfulBrush::setHasColorAndTransparency(bool value)
0198 {
0199     m_hasColorAndTransparency = value;
0200 }
0201 
0202 bool KisColorfulBrush::hasColorAndTransparency() const
0203 {
0204     return m_hasColorAndTransparency;
0205 }