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

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