File indexing completed on 2024-05-19 04:25:04

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include <kis_global.h>
0008 #include "kis_painting_tweaks.h"
0009 
0010 #include <QPen>
0011 #include <QRegion>
0012 #include <QPainter>
0013 #include <QTransform>
0014 
0015 #include "kis_debug.h"
0016 
0017 
0018 namespace KisPaintingTweaks {
0019 
0020 QRegion safeClipRegion(const QPainter &painter)
0021 {
0022     const QTransform t = painter.transform();
0023 
0024     QRegion region = t.type() <= QTransform::TxScale ?
0025         painter.clipRegion() :
0026         QRegion(painter.clipBoundingRect().toAlignedRect());
0027 
0028     if (region.rectCount() > 1000) {
0029         qWarning() << "WARNING: KisPaintingTweaks::safeClipRegion: too many rectangles in the region!" << ppVar(region.rectCount());
0030         region = QRegion(painter.clipBoundingRect().toAlignedRect());
0031     }
0032 
0033     return region;
0034 }
0035 
0036 QRect safeClipBoundingRect(const QPainter &painter)
0037 {
0038     return painter.clipBoundingRect().toAlignedRect();
0039 }
0040 
0041 void initAntsPen(QPen *antsPen, QPen *outlinePen,
0042                  int antLength, int antSpace)
0043 {
0044     QVector<qreal> antDashPattern;
0045     antDashPattern << antLength << antSpace;
0046 
0047     *antsPen = QPen(Qt::CustomDashLine);
0048     antsPen->setDashPattern(antDashPattern);
0049     antsPen->setCosmetic(true);
0050     antsPen->setColor(Qt::black);
0051 
0052     *outlinePen = QPen(Qt::SolidLine);
0053     outlinePen->setCosmetic(true);
0054     outlinePen->setColor(Qt::white);
0055 }
0056 
0057 PenBrushSaver::PenBrushSaver(QPainter *painter)
0058     : m_painter(painter),
0059       m_pen(painter->pen()),
0060       m_brush(painter->brush())
0061 {
0062 }
0063 
0064 PenBrushSaver::PenBrushSaver(QPainter *painter, const QPen &pen, const QBrush &brush)
0065     : PenBrushSaver(painter)
0066 {
0067     m_painter->setPen(pen);
0068     m_painter->setBrush(brush);
0069 }
0070 
0071 PenBrushSaver::PenBrushSaver(QPainter *painter, const QPair<QPen, QBrush> &pair)
0072     : PenBrushSaver(painter)
0073 {
0074     m_painter->setPen(pair.first);
0075     m_painter->setBrush(pair.second);
0076 }
0077 
0078 PenBrushSaver::PenBrushSaver(QPainter *painter, const QPair<QPen, QBrush> &pair, allow_noop_t)
0079     : m_painter(painter)
0080 {
0081     if (m_painter) {
0082         m_pen = m_painter->pen();
0083         m_brush = m_painter->brush();
0084         m_painter->setPen(pair.first);
0085         m_painter->setBrush(pair.second);
0086     }
0087 }
0088 
0089 PenBrushSaver::~PenBrushSaver()
0090 {
0091     if (m_painter) {
0092         m_painter->setPen(m_pen);
0093         m_painter->setBrush(m_brush);
0094     }
0095 }
0096 
0097 QColor blendColors(const QColor &c1, const QColor &c2, qreal r1)
0098 {
0099     const qreal r2 = 1.0 - r1;
0100 
0101     return QColor::fromRgbF(
0102         c1.redF() * r1 + c2.redF() * r2,
0103         c1.greenF() * r1 + c2.greenF() * r2,
0104         c1.blueF() * r1 + c2.blueF() * r2);
0105 }
0106 
0107 qreal colorDifference(const QColor &c1, const QColor &c2)
0108 {
0109     const qreal dr = c1.redF() - c2.redF();
0110     const qreal dg = c1.greenF() - c2.greenF();
0111     const qreal db = c1.blueF() - c2.blueF();
0112 
0113     return std::sqrt(2 * pow2(dr) + 4 * pow2(dg) + 3 * pow2(db));
0114 }
0115 
0116 void dragColor(QColor *color, const QColor &baseColor, qreal threshold)
0117 {
0118     while (colorDifference(*color, baseColor) < threshold) {
0119 
0120         QColor newColor = *color;
0121 
0122         if (newColor.lightnessF() > baseColor.lightnessF()) {
0123             newColor = newColor.lighter(120);
0124         } else {
0125             newColor = newColor.darker(120);
0126         }
0127 
0128         if (newColor == *color) {
0129             break;
0130         }
0131 
0132         *color = newColor;
0133     }
0134 }
0135 
0136 // This does a simplified linearization and calculates the luma.
0137 // Krita has the ability to precisely calculate this value,
0138 // but that seems overkill when all we want to know is whether
0139 // it passes a certain gray threshold.
0140 static QMap<qreal, qreal> sRgbTRCToLinear {
0141     {0.0, 0.0},
0142     {0.1, 0.01002},
0143     {0.2, 0.0331},
0144     {0.3, 0.07324},
0145     {0.4, 0.13287},
0146     {0.5, 0.21404},
0147     {0.6, 0.31855},
0148     {0.7, 0.44799},
0149     {0.8, 0.60383},
0150     {0.9, 0.78741},
0151     {1.0, 1.0}
0152 };
0153 
0154 static QMap<qreal, qreal> linearToSRGBTRC {
0155     {0.0, 0.0},
0156     {0.01002, 0.1},
0157     {0.0331, 0.2},
0158     {0.07324, 0.3},
0159     {0.13287, 0.4},
0160     {0.21404, 0.5},
0161     {0.31855, 0.6},
0162     {0.44799, 0.7},
0163     {0.60383, 0.8},
0164     {0.78741, 0.9},
0165     {1.0, 1.0}
0166 };
0167 
0168 qreal luminosityCoarse(const QColor &c, bool sRGBtrc)
0169 {
0170     qreal r = c.redF();
0171     qreal g = c.greenF();
0172     qreal b = c.blueF();
0173     if (sRGBtrc) {
0174         if (r < 1.0) {
0175             r = sRgbTRCToLinear.upperBound(r).value();
0176         }
0177         if (g < 1.0) {
0178             g = sRgbTRCToLinear.upperBound(g).value();
0179         }
0180         if (b < 1.0) {
0181             b = sRgbTRCToLinear.upperBound(b).value();
0182         }
0183     }
0184     qreal lumi = (r * .2126) + (g * .7152) + (b * .0722);
0185     if (sRGBtrc && lumi < 1.0) {
0186         lumi = linearToSRGBTRC.lowerBound(lumi).value();
0187     }
0188     return lumi;
0189 }
0190 
0191 }