]> git.sesse.net Git - kdenlive/blob - src/colorcorrection/rgbparadegenerator.cpp
Histogram: Fixed max display
[kdenlive] / src / colorcorrection / rgbparadegenerator.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com)      *
3  *   This file is part of kdenlive. See www.kdenlive.org.                  *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10
11 #include "rgbparadegenerator.h"
12
13 #include <QColor>
14 #include <QPainter>
15 #include <QPoint>
16 #include <QTime>
17
18 #define CHOP255(a) ((255) < (a) ? (255) : (a))
19
20 const QColor RGBParadeGenerator::colHighlight(255, 245, 235, 255);
21 const QColor RGBParadeGenerator::colLight(200, 200, 200, 255);
22 const QColor RGBParadeGenerator::colSoft(150, 150, 150, 255);
23
24 RGBParadeGenerator::RGBParadeGenerator()
25 {
26 }
27
28 QImage RGBParadeGenerator::calculateRGBParade(const QSize &paradeSize, const QImage &image,
29                                               const RGBParadeGenerator::PaintMode paintMode, const bool &drawAxis, 
30                                               const bool &drawGradientRef, const uint &accelFactor)
31 {
32     Q_ASSERT(accelFactor >= 1);
33
34     if (paradeSize.width() <= 0 || paradeSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
35         return QImage();
36
37     } else {
38         QImage parade(paradeSize, QImage::Format_ARGB32);
39         parade.fill(qRgba(0,0,0,0));
40
41         QRgb *col;
42         QRgb paradeCol;
43         QPoint paradePoint;
44         QPainter davinci(&parade);
45
46         double dx, dy;
47
48         const uint ww = paradeSize.width();
49         const uint wh = paradeSize.height();
50         const uint iw = image.bytesPerLine();
51         const uint ih = image.height();
52         const uint byteCount = iw*ih;
53
54         const uchar offset = 10;
55         const uchar right = 40;
56         const uchar bottom = 40;
57         const int partW = (ww - 2*offset - right) / 3;
58         const int partH = wh - bottom;
59
60         // To get constant brightness, independant of acceleration factor and input image size
61         // Must be a float because the acceleration factor can be high, leading to <1 expected px per px.
62         const float avgPxPerPx = ((float)(image.width() * image.height()) / (500*partW*accelFactor));
63         const float weaken = (avgPxPerPx == 0) ? 1 : (float)4/avgPxPerPx;
64         const int vh = weaken*27;
65         const int vm = weaken*18;
66         const int vl = weaken*9;
67
68         // Divide by 3 because of the 3 components
69         const float brightnessAdjustment = accelFactor * ((float) ww*wh/(byteCount>>3)) / 3;
70
71         uchar minR = 255, minG = 255, minB = 255, maxR = 0, maxG = 0, maxB = 0, r, g, b;
72 //        qDebug() << "Expecting about " << avgPxPerPx << " pixels per pixel in the RGB parade. Weakening by " << weaken
73 //                << " with an acceleration factor of " << accelFactor;
74
75
76         QImage unscaled(ww-right, 256, QImage::Format_ARGB32);
77         unscaled.fill(qRgba(0, 0, 0, 0));
78
79         const float wPrediv = (float)(partW-1)/(iw-1);
80
81         const uchar *bits = image.bits();
82         const uint stepsize = 4*accelFactor;
83
84         for (uint i = 0, x = 0; i < byteCount; i += stepsize) {
85
86             col = (QRgb *)bits;
87             r = qRed(*col);
88             g = qGreen(*col);
89             b = qBlue(*col);
90
91             dx = x*wPrediv;
92
93             paradePoint = QPoint((int)dx, r);
94             paradeCol = QRgb(unscaled.pixel(paradePoint));
95             switch(paintMode) {
96             case PaintMode_RGB2:
97                 unscaled.setPixel(paradePoint, qRgba(CHOP255(vh + qRed(paradeCol)), CHOP255(vm + qGreen(paradeCol)),
98                                                      CHOP255(vl + qBlue(paradeCol)), 255));
99                 break;
100             default:
101                 unscaled.setPixel(paradePoint, qRgba(255,0,0, CHOP255(brightnessAdjustment*16 + qAlpha(paradeCol))));
102                 break;
103             }
104
105             paradePoint = QPoint((int) (dx + partW + offset), g);
106             paradeCol = QRgb(unscaled.pixel(paradePoint));
107             switch(paintMode) {
108             case PaintMode_RGB2:
109                 unscaled.setPixel(paradePoint, qRgba(CHOP255(vl + qRed(paradeCol)), CHOP255(vh + qGreen(paradeCol)),
110                                                CHOP255(vm + qBlue(paradeCol)), 255));
111                 break;
112             default:
113                 unscaled.setPixel(paradePoint, qRgba(0,255,0, CHOP255(brightnessAdjustment*16 + qAlpha(paradeCol))));
114                 break;
115             }
116
117             paradePoint = QPoint((int) (dx + 2*partW + 2*offset), b);
118             paradeCol = QRgb(unscaled.pixel(paradePoint));
119             switch(paintMode) {
120             case PaintMode_RGB2:
121                 unscaled.setPixel(paradePoint, qRgba(CHOP255(vm + qRed(paradeCol)), CHOP255(vl + qGreen(paradeCol)),
122                                                CHOP255(vh + qBlue(paradeCol)), 255));
123                 break;
124             default:
125                 unscaled.setPixel(paradePoint, qRgba(0,0,255, CHOP255(brightnessAdjustment*16 + qAlpha(paradeCol))));
126                 break;
127             }
128
129
130             if (r < minR) { minR = r; }
131             if (g < minG) { minG = g; }
132             if (b < minB) { minB = b; }
133             if (r > maxR) { maxR = r; }
134             if (g > maxG) { maxG = g; }
135             if (b > maxB) { maxB = b; }
136
137             bits += stepsize;
138             x += stepsize;
139             x %= iw; // Modulo image width, to represent the current x position in the image
140         }
141         // Scale the image to the target height. Scaling is not accomplished before because
142         // there are only 255 different values which would lead to gaps if the height is not exactly 255.
143         // Don't use bilinear transformation because the fast transformation meets the goal better.
144         davinci.drawImage(0, 0, unscaled.mirrored(false, true).scaled(unscaled.width(), partH, Qt::IgnoreAspectRatio, Qt::FastTransformation));
145
146         if (drawAxis) {
147             QRgb opx;
148             for (uint i = 0; i <= 10; i++) {
149                 dy = (float)i/10 * (partH-1);
150                 for (uint x = 0; x < ww-right; x++) {
151                     opx = parade.pixel(x, dy);
152                     parade.setPixel(x,dy, qRgba(CHOP255(150+qRed(opx)), 255,
153                                               CHOP255(200+qBlue(opx)), CHOP255(32+qAlpha(opx))));
154                 }
155             }
156         }
157         
158         if (drawGradientRef) {
159             davinci.setPen(colLight);
160             davinci.drawLine(0                 ,partH,   partW,           0);
161             davinci.drawLine(  partW +   offset,partH, 2*partW +   offset,0);
162             davinci.drawLine(2*partW + 2*offset,partH, 3*partW + 2*offset,0);
163         }
164
165
166         const int d = 50;
167
168         // Show numerical minimum
169         if (minR == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
170         davinci.drawText(0,                     wh, "min: ");
171         if (minG == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
172         davinci.drawText(partW + offset,        wh, "min: ");
173         if (minB == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
174         davinci.drawText(2*partW + 2*offset,    wh, "min: ");
175
176         // Show numerical maximum
177         if (maxR == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
178         davinci.drawText(0,                     wh-20, "max: ");
179         if (maxG == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
180         davinci.drawText(partW + offset,        wh-20, "max: ");
181         if (maxB == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
182         davinci.drawText(2*partW + 2*offset,    wh-20, "max: ");
183
184         davinci.setPen(colLight);
185         davinci.drawText(d,                        wh, QString::number(minR, 'f', 0));
186         davinci.drawText(partW + offset + d,       wh, QString::number(minG, 'f', 0));
187         davinci.drawText(2*partW + 2*offset + d,   wh, QString::number(minB, 'f', 0));
188
189         davinci.drawText(d,                        wh-20, QString::number(maxR, 'f', 0));
190         davinci.drawText(partW + offset + d,       wh-20, QString::number(maxG, 'f', 0));
191         davinci.drawText(2*partW + 2*offset + d,   wh-20, QString::number(maxB, 'f', 0));
192
193         davinci.drawText(ww-right+5,            10,      "255");
194         davinci.drawText(ww-right+5,            partH+5,  "0");
195
196
197
198         return parade;
199     }
200 }
201
202 #undef CHOP255