]> git.sesse.net Git - kdenlive/blob - src/colorcorrection/rgbparadegenerator.cpp
Use QLatin1String
[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 <KLocalizedString>
14
15 #include <QColor>
16 #include <QPainter>
17
18 #define CHOP255(a) ((255) < (a) ? (255) : (a))
19 #define CHOP1255(a) ((a) < (1) ? (1) : ((a) > (255) ? (255) : (a)))
20
21 const QColor RGBParadeGenerator::colHighlight(255, 245, 235, 255);
22 const QColor RGBParadeGenerator::colLight(200, 200, 200, 255);
23 const QColor RGBParadeGenerator::colSoft(150, 150, 150, 255);
24
25
26 const uchar RGBParadeGenerator::distRight(40);
27 const uchar RGBParadeGenerator::distBottom(40);
28
29 struct StructRGB {
30     uint r;
31     uint g;
32     uint b;
33 };
34
35 RGBParadeGenerator::RGBParadeGenerator()
36 {
37 }
38
39 QImage RGBParadeGenerator::calculateRGBParade(const QSize &paradeSize, const QImage &image,
40                                               const RGBParadeGenerator::PaintMode paintMode, bool drawAxis,
41                                               bool drawGradientRef, uint accelFactor)
42 {
43     Q_ASSERT(accelFactor >= 1);
44
45     if (paradeSize.width() <= 0 || paradeSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
46         return QImage();
47
48     } else {
49         QImage parade(paradeSize, QImage::Format_ARGB32);
50         parade.fill(Qt::transparent);
51
52         QRgb *col;
53         QPainter davinci(&parade);
54
55         double dx, dy;
56
57         const uint ww = paradeSize.width();
58         const uint wh = paradeSize.height();
59         const uint iw = image.bytesPerLine();
60         const uint ih = image.height();
61         const uint byteCount = iw*ih;   // Note that 1 px = 4 B
62
63         const uchar offset = 10;
64         const uint partW = (ww - 2*offset - distRight) / 3;
65         const uint partH = wh - distBottom;
66
67         // Statistics
68         uchar minR = 255, minG = 255, minB = 255, maxR = 0, maxG = 0, maxB = 0, r, g, b;
69
70
71         // Number of input pixels that will fall on one scope pixel.
72         // Must be a float because the acceleration factor can be high, leading to <1 expected px per px.
73         const float pixelDepth = (float)((byteCount>>2) / accelFactor)/(partW*255);
74         const float gain = 255/(8*pixelDepth);
75 //        qDebug() << "Pixel depth: expected " << pixelDepth << "; Gain: using " << gain << " (acceleration: " << accelFactor << "x)";
76
77         QImage unscaled(ww-distRight, 256, QImage::Format_ARGB32);
78         unscaled.fill(qRgba(0, 0, 0, 0));
79
80         const float wPrediv = (float)(partW-1)/(iw-1);
81
82         StructRGB paradeVals[partW][256];
83         for (uint i = 0; i < partW; ++i) {
84             for (uint j = 0; j < 256; j++) {
85                 paradeVals[i][j].r = 0;
86                 paradeVals[i][j].g = 0;
87                 paradeVals[i][j].b = 0;
88             }
89         }
90
91         const uchar *bits = image.bits();
92         const uint stepsize = image.depth() / 8 *accelFactor;
93
94         for (uint i = 0, x = 0; i < byteCount; i += stepsize) {
95             col = (QRgb *)bits;
96             r = qRed(*col);
97             g = qGreen(*col);
98             b = qBlue(*col);
99
100             dx = x*wPrediv;
101
102             paradeVals[(int)dx][r].r++;
103             paradeVals[(int)dx][g].g++;
104             paradeVals[(int)dx][b].b++;
105
106
107             if (r < minR) { minR = r; }
108             if (g < minG) { minG = g; }
109             if (b < minB) { minB = b; }
110             if (r > maxR) { maxR = r; }
111             if (g > maxG) { maxG = g; }
112             if (b > maxB) { maxB = b; }
113
114             bits += stepsize;
115             x += stepsize;
116             x %= iw; // Modulo image width, to represent the current x position in the image
117         }
118
119
120         const uint offset1 = partW + offset;
121         const uint offset2 = 2*partW + 2*offset;
122         switch(paintMode) {
123         case PaintMode_RGB:
124             for (uint i = 0; i < partW; ++i) {
125                 for (uint j = 0; j < 256; j++) {
126                     unscaled.setPixel(i,         j, qRgba(255,10,10, CHOP255(gain*paradeVals[i][j].r)));
127                     unscaled.setPixel(i+offset1, j, qRgba(10,255,10, CHOP255(gain*paradeVals[i][j].g)));
128                     unscaled.setPixel(i+offset2, j, qRgba(10,10,255, CHOP255(gain*paradeVals[i][j].b)));
129                 }
130             }
131             break;
132         default:
133             for (uint i = 0; i < partW; ++i) {
134                 for (uint j = 0; j < 256; j++) {
135                     unscaled.setPixel(i,         j, qRgba(255,255,255, CHOP255(gain*paradeVals[i][j].r)));
136                     unscaled.setPixel(i+offset1, j, qRgba(255,255,255, CHOP255(gain*paradeVals[i][j].g)));
137                     unscaled.setPixel(i+offset2, j, qRgba(255,255,255, CHOP255(gain*paradeVals[i][j].b)));
138                 }
139             }
140             break;
141         }
142
143         // Scale the image to the target height. Scaling is not accomplished before because
144         // there are only 255 different values which would lead to gaps if the height is not exactly 255.
145         // Don't use bilinear transformation because the fast transformation meets the goal better.
146         davinci.drawImage(0, 0, unscaled.mirrored(false, true).scaled(unscaled.width(), partH, Qt::IgnoreAspectRatio, Qt::FastTransformation));
147
148         if (drawAxis) {
149             QRgb opx;
150             for (uint i = 0; i <= 10; ++i) {
151                 dy = (float)i/10 * (partH-1);
152                 for (uint x = 0; x < ww-distRight; x++) {
153                     opx = parade.pixel(x, dy);
154                     parade.setPixel(x,dy, qRgba(CHOP255(150+qRed(opx)), 255,
155                                               CHOP255(200+qBlue(opx)), CHOP255(32+qAlpha(opx))));
156                 }
157             }
158         }
159         
160         if (drawGradientRef) {
161             davinci.setPen(colLight);
162             davinci.drawLine(0                 ,partH,   partW,           0);
163             davinci.drawLine(  partW +   offset,partH, 2*partW +   offset,0);
164             davinci.drawLine(2*partW + 2*offset,partH, 3*partW + 2*offset,0);
165         }
166
167
168         const int d = 50;
169
170         // Show numerical minimum
171         if (minR == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
172         davinci.drawText(0,                     wh, i18n("min: "));
173         if (minG == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
174         davinci.drawText(partW + offset,        wh, i18n("min: "));
175         if (minB == 0) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
176         davinci.drawText(2*partW + 2*offset,    wh, i18n("min: "));
177
178         // Show numerical maximum
179         if (maxR == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
180         davinci.drawText(0,                     wh-20, i18n("max: "));
181         if (maxG == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
182         davinci.drawText(partW + offset,        wh-20, i18n("max: "));
183         if (maxB == 255) { davinci.setPen(colHighlight); } else { davinci.setPen(colSoft); }
184         davinci.drawText(2*partW + 2*offset,    wh-20, i18n("max: "));
185
186         davinci.setPen(colLight);
187         davinci.drawText(d,                        wh, QString::number(minR, 'f', 0));
188         davinci.drawText(partW + offset + d,       wh, QString::number(minG, 'f', 0));
189         davinci.drawText(2*partW + 2*offset + d,   wh, QString::number(minB, 'f', 0));
190
191         davinci.drawText(d,                        wh-20, QString::number(maxR, 'f', 0));
192         davinci.drawText(partW + offset + d,       wh-20, QString::number(maxG, 'f', 0));
193         davinci.drawText(2*partW + 2*offset + d,   wh-20, QString::number(maxB, 'f', 0));
194
195
196         return parade;
197     }
198 }
199
200 #undef CHOP255