File indexing completed on 2024-12-22 04:13:14

0001 /* 
0002  * SPDX-FileCopyrightText: 2015 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0003  *
0004  * Based on the Digikam CIE Tongue widget
0005  * SPDX-FileCopyrightText: 2006-2013 Gilles Caulier <caulier dot gilles at gmail dot com>
0006  *
0007  * Any source code are inspired from lprof project and
0008  * SPDX-FileCopyrightText: 1998-2001 Marti Maria
0009  *
0010  * SPDX-License-Identifier: GPL-2.0-or-later
0011  **/
0012 #include <QPointF>
0013 #include <QPolygonF>
0014 #include <QPainter>
0015 #include <QPainterPath>
0016 #include <QPaintEvent>
0017 #include <QImage>
0018 #include <QTextStream>
0019 #include <cmath>
0020 #include <klocalizedstring.h>
0021 
0022 
0023 #include "kis_tone_curve_widget.h"
0024 
0025 class Q_DECL_HIDDEN KisToneCurveWidget::Private
0026 {
0027 public:
0028     
0029     bool            profileDataAvailable {false};
0030     bool            needUpdatePixmap {false};
0031     bool            TRCGray {true};
0032     bool            TRCRGB {false};
0033  
0034     int             xBias {0};
0035     int             yBias {0};
0036     int             pxcols {0};
0037     int             pxrows {0};
0038 
0039     QPolygonF       ToneCurveGray;
0040     QPolygonF       ToneCurveRed;
0041     QPolygonF       ToneCurveGreen;
0042     QPolygonF       ToneCurveBlue;
0043  
0044     double          gridside {0.0};
0045  
0046     QPainter        painter;
0047     QPainter        painter2;
0048     QPixmap         pixmap;
0049     QPixmap         curvemap;
0050 };
0051 
0052 KisToneCurveWidget::KisToneCurveWidget(QWidget *parent) :
0053     QWidget(parent), d(new Private)
0054 {
0055     /*this is a tone curve widget*/
0056 }
0057 
0058 KisToneCurveWidget::~KisToneCurveWidget()
0059 {
0060     delete d;
0061 }
0062 
0063 void KisToneCurveWidget::setGreyscaleCurve(QPolygonF poly)
0064 {
0065     d->ToneCurveGray = poly;
0066     d->TRCGray = true;
0067     d->TRCRGB = false;
0068     d->profileDataAvailable = true;
0069     d->needUpdatePixmap = true;
0070 }
0071 
0072 void KisToneCurveWidget::setRGBCurve(QPolygonF red, QPolygonF green, QPolygonF blue)
0073 {
0074     d->ToneCurveRed = red;
0075     d->ToneCurveGreen = green;
0076     d->ToneCurveBlue = blue;
0077     d->profileDataAvailable = true;
0078     d->TRCGray = false;
0079     d->TRCRGB = true;
0080     d->needUpdatePixmap = true;
0081 }
0082 void KisToneCurveWidget::setCMYKCurve(QPolygonF cyan, QPolygonF magenta, QPolygonF yellow, QPolygonF key)
0083 {
0084     d->ToneCurveRed = cyan;
0085     d->ToneCurveGreen = magenta;
0086     d->ToneCurveBlue = yellow;
0087     d->ToneCurveGray = key;
0088     d->profileDataAvailable = true;
0089     d->TRCGray = false;
0090     d->TRCRGB = false;
0091     d->needUpdatePixmap = true;
0092 }
0093 void KisToneCurveWidget::setProfileDataAvailable(bool dataAvailable)
0094 {
0095     d->profileDataAvailable = dataAvailable;
0096 }
0097 int KisToneCurveWidget::grids(double val) const
0098 {
0099     return (int) floor(val * d->gridside + 0.5);
0100 }
0101 
0102 void KisToneCurveWidget::mapPoint(QPointF & xy)
0103 {
0104     QPointF dummy = xy;
0105     xy.setX( (int) floor((dummy.x() * (d->pxcols - 1)) + .5) + d->xBias);
0106     xy.setY( (int) floor(((d->pxrows - 1) - dummy.y() * (d->pxrows - 1)) + .5) );
0107 }
0108 
0109 void KisToneCurveWidget::biasedLine(int x1, int y1, int x2, int y2)
0110 {
0111     d->painter.drawLine(x1 + d->xBias, y1, x2 + d->xBias, y2);
0112 }
0113  
0114 void KisToneCurveWidget::biasedText(int x, int y, const QString& txt)
0115 {
0116     d->painter.drawText(QPoint(d->xBias + x, y), txt);
0117 }
0118 
0119 void KisToneCurveWidget::drawGrid()
0120 {
0121     d->painter.setOpacity(1.0);
0122     d->painter.setPen(qRgb(255, 255, 255));
0123     biasedLine(0, 0,           0,           d->pxrows - 1);
0124     biasedLine(0, d->pxrows-1, d->pxcols-1, d->pxrows - 1);
0125     
0126     QFont font;
0127     font.setPointSize(6);
0128     d->painter.setFont(font);
0129     
0130     for (int y = 1; y <= 9; y += 1)
0131     {
0132         QString s;
0133         int xstart = (y * (d->pxcols - 1)) / 10;
0134         int ystart = (y * (d->pxrows - 1)) / 10;
0135  
0136         QTextStream(&s) << y;
0137         biasedLine(xstart, d->pxrows - grids(1), xstart,   d->pxrows - grids(4));
0138         biasedText(xstart - grids(11), d->pxrows + grids(15), s);
0139  
0140         QTextStream(&s) << 10 - y;
0141         biasedLine(0, ystart, grids(3), ystart);
0142         biasedText(grids(-25), ystart + grids(5), s);
0143     }
0144     
0145     d->painter.setPen(qRgb(128, 128, 128));
0146     d->painter.setOpacity(0.5);
0147  
0148     for (int y = 1; y <= 9; y += 1)
0149     {
0150         int xstart =  (y * (d->pxcols - 1)) / 10;
0151         int ystart =  (y * (d->pxrows - 1)) / 10;
0152  
0153         biasedLine(xstart, grids(4), xstart,   d->pxrows - grids(4) - 1);
0154         biasedLine(grids(7), ystart, d->pxcols-1-grids(7), ystart);
0155     }
0156     d->painter.setOpacity(1.0);
0157     d->painter.setOpacity(1.0);
0158 }
0159 
0160 void KisToneCurveWidget::updatePixmap()
0161 {
0162     d->needUpdatePixmap = false;
0163     d->pixmap = QPixmap(size() * devicePixelRatioF());
0164     d->pixmap.setDevicePixelRatio(devicePixelRatioF());
0165     d->curvemap = QPixmap(size() * devicePixelRatioF());
0166     d->curvemap.setDevicePixelRatio(devicePixelRatioF());
0167     d->pixmap.fill(Qt::black);
0168     d->curvemap.fill(Qt::transparent);
0169 
0170     d->painter.begin(&d->pixmap);
0171 
0172     int pixcols =
0173         static_cast<int>(d->pixmap.width() / d->pixmap.devicePixelRatioF());
0174     int pixrows =
0175         static_cast<int>(d->pixmap.height() / d->pixmap.devicePixelRatioF());
0176 
0177     d->gridside = (qMin(pixcols, pixrows)) / 512.0;
0178     d->xBias    = grids(32);
0179     d->yBias    = grids(20);
0180     d->pxcols   = pixcols - d->xBias;
0181     d->pxrows   = pixrows - d->yBias;
0182 
0183     d->painter.setBackground(QBrush(qRgb(0, 0, 0)));
0184     QPointF start;
0185     drawGrid();
0186     d->painter.setRenderHint(QPainter::Antialiasing);
0187     if (d->TRCGray && d->ToneCurveGray.size()>0){
0188         QPainterPath path;
0189         start = d->ToneCurveGray.at(0);
0190         mapPoint(start);
0191         path.moveTo(start);
0192         foreach (QPointF Point, d->ToneCurveGray) {
0193             mapPoint(Point);
0194             path.lineTo(Point);
0195         }
0196         d->painter.setPen(qRgb(255, 255, 255));
0197         d->painter.drawPath(path);
0198     } else if (d->TRCRGB && d->ToneCurveRed.size()>0 && d->ToneCurveGreen.size()>0 && d->ToneCurveBlue.size()>0){
0199         d->painter.save();
0200         d->painter.setCompositionMode(QPainter::CompositionMode_Screen);
0201         QPainterPath path;
0202         start = d->ToneCurveRed.at(0);
0203         mapPoint(start);
0204         path.moveTo(start);
0205         foreach (QPointF Point, d->ToneCurveRed) {
0206             mapPoint(Point);
0207             path.lineTo(Point);
0208         }
0209         d->painter.setPen(qRgb(255, 0, 0));
0210         d->painter.drawPath(path);
0211         QPainterPath path2;
0212         start = d->ToneCurveGreen.at(0);
0213         mapPoint(start);
0214         path2.moveTo(start);
0215         foreach (QPointF Point, d->ToneCurveGreen) {
0216             mapPoint(Point);
0217             path2.lineTo(Point);
0218         }
0219         d->painter.setPen(qRgb(0, 255, 0));
0220         d->painter.drawPath(path2);
0221         QPainterPath path3;
0222         start = d->ToneCurveBlue.at(0);
0223         mapPoint(start);
0224         path3.moveTo(start);
0225         foreach (QPointF Point, d->ToneCurveBlue) {
0226             mapPoint(Point);
0227             path3.lineTo(Point);
0228         }
0229         d->painter.setPen(qRgb(0, 0, 255));
0230         d->painter.drawPath(path3);
0231         d->painter.restore();
0232     } else {
0233         d->painter2.begin(&d->curvemap);
0234         d->painter2.setRenderHint(QPainter::Antialiasing);
0235         //d->painter2.setCompositionMode(QPainter::CompositionMode_Multiply);
0236         QPainterPath path;
0237         start = d->ToneCurveRed.at(0);
0238         mapPoint(start);
0239         path.moveTo(start);
0240         foreach (QPointF Point, d->ToneCurveRed) {
0241             mapPoint(Point);
0242             path.lineTo(Point);
0243         }
0244         d->painter2.setPen(qRgb(0, 255, 255));
0245         d->painter2.drawPath(path);
0246         QPainterPath path2;
0247         start = d->ToneCurveGreen.at(0);
0248         mapPoint(start);
0249         path2.moveTo(start);
0250         foreach (QPointF Point, d->ToneCurveGreen) {
0251             mapPoint(Point);
0252             path2.lineTo(Point);
0253         }
0254         d->painter2.setPen(qRgb(255, 0, 255));
0255         d->painter2.drawPath(path2);
0256         QPainterPath path3;
0257         start = d->ToneCurveBlue.at(0);
0258         mapPoint(start);
0259         path3.moveTo(start);
0260         foreach (QPointF Point, d->ToneCurveBlue) {
0261             mapPoint(Point);
0262             path3.lineTo(Point);
0263         }
0264         d->painter2.setPen(qRgb(255, 255, 0));
0265         d->painter2.drawPath(path3);
0266         QPainterPath path4;
0267         start = d->ToneCurveGray.at(0);
0268         mapPoint(start);
0269         path4.moveTo(start);
0270         foreach (QPointF Point, d->ToneCurveGray) {
0271             mapPoint(Point);
0272             path4.lineTo(Point);
0273         }
0274         d->painter2.setPen(qRgb(80, 80, 80));
0275         d->painter2.drawPath(path4);
0276         d->painter2.end();
0277         d->painter.drawPixmap(d->xBias, 0, d->curvemap);
0278     }
0279     d->painter.end();
0280 }
0281 
0282 void KisToneCurveWidget::paintEvent(QPaintEvent*)
0283 {
0284     QPainter p(this);
0285  
0286     // Widget is disable : drawing grayed frame.
0287  
0288     if ( !isEnabled() )
0289     {
0290         p.fillRect(0, 0, width(), height(),
0291                    palette().color(QPalette::Disabled, QPalette::Window));
0292  
0293         QPen pen(palette().color(QPalette::Disabled, QPalette::WindowText));
0294         pen.setStyle(Qt::SolidLine);
0295         pen.setWidth(1);
0296  
0297         p.setPen(pen);
0298         p.drawRect(0, 0, width(), height());
0299  
0300         return;
0301     }
0302 
0303  
0304     // No profile data to show, or RAW file
0305  
0306     if (!d->profileDataAvailable)
0307     {
0308         p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Window));
0309         QPen pen(palette().color(QPalette::Active, QPalette::Text));
0310         pen.setStyle(Qt::SolidLine);
0311         pen.setWidth(1);
0312  
0313         p.setPen(pen);
0314         p.drawRect(0, 0, width(), height());
0315  
0316         p.setPen(Qt::red);
0317         p.drawText(0, 0, width(), height(), Qt::AlignCenter,
0318         i18n("No tone curve available..."));
0319  
0320         return;
0321     }
0322  
0323     // Create CIE tongue if needed
0324     if (d->needUpdatePixmap)
0325     {
0326         updatePixmap();
0327     }
0328  
0329     // draw prerendered tongue
0330     p.drawPixmap(0, 0, d->pixmap);
0331 }
0332 
0333 void KisToneCurveWidget::resizeEvent(QResizeEvent* event)
0334 {
0335     QWidget::resizeEvent(event);
0336     d->needUpdatePixmap = true;
0337 }