]> git.sesse.net Git - kdenlive/blob - src/colorcorrection/histogramgenerator.cpp
c08cb0e12942bf3e859403ad0b0fe3f7ce729bfa
[kdenlive] / src / colorcorrection / histogramgenerator.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 <algorithm>
12 #include <math.h>
13 #include <QImage>
14 #include <QPainter>
15 #include "histogramgenerator.h"
16
17 HistogramGenerator::HistogramGenerator()
18 {
19 }
20
21 QImage HistogramGenerator::calculateHistogram(const QSize &paradeSize, const QImage &image, const int &components,
22                                               HistogramGenerator::Rec rec, bool unscaled, uint accelFactor) const
23 {
24     if (paradeSize.height() <= 0 || paradeSize.width() <= 0 || image.width() <= 0 || image.height() <= 0) {
25         return QImage();
26     }
27
28     bool drawY = (components & HistogramGenerator::ComponentY) != 0;
29     bool drawR = (components & HistogramGenerator::ComponentR) != 0;
30     bool drawG = (components & HistogramGenerator::ComponentG) != 0;
31     bool drawB = (components & HistogramGenerator::ComponentB) != 0;
32     bool drawSum = (components & HistogramGenerator::ComponentSum) != 0;
33
34     int r[256], g[256], b[256], y[256], s[766];
35     // Initialize the values to zero
36     std::fill(r, r+256, 0);
37     std::fill(g, g+256, 0);
38     std::fill(b, b+256, 0);
39     std::fill(y, y+256, 0);
40     std::fill(s, s+766, 0);
41
42     const uint iw = image.bytesPerLine();
43     const uint ih = image.height();
44     const uint ww = paradeSize.width();
45     const uint wh = paradeSize.height();
46     const uint byteCount = iw*ih;
47     const uint stepsize = image.depth() / 8 *accelFactor;
48
49     const uchar *bits = image.bits();
50     QRgb *col;
51
52
53     // Read the stats from the input image
54     for (uint i = 0; i < byteCount; i += stepsize) {
55         col = (QRgb *)bits;
56
57         r[qRed(*col)]++;
58         g[qGreen(*col)]++;
59         b[qBlue(*col)]++;
60         if (drawY) {
61             // Use if branch to avoid expensive multiplication if Y disabled
62             if (rec == HistogramGenerator::Rec_601) {
63                 y[(int)floor(.299*qRed(*col) + .587*qGreen(*col) + .114*qBlue(*col))]++;
64             } else {
65                 y[(int)floor(.2125*qRed(*col) + .7154*qGreen(*col) + .0721*qBlue(*col))]++;
66             }
67         }
68         if (drawSum) {
69             // Use an if branch here because the sum takes more operations than rgb
70             s[qRed(*col)]++;
71             s[qGreen(*col)]++;
72             s[qBlue(*col)]++;
73         }
74
75         bits += stepsize;
76     }
77
78
79     const int nParts = (drawY ? 1 : 0) + (drawR ? 1 : 0) + (drawG ? 1 : 0) + (drawB ? 1 : 0) + (drawSum ? 1 : 0);
80     if (nParts == 0) {
81         // Nothing to draw
82         return QImage();
83     }
84
85     const int d = 20; // Distance for text
86     const int partH = (wh-nParts*d)/nParts;
87     float scaling = 0;
88     int div = byteCount >> 7;
89     if ( div > 0 )
90         scaling = (float)partH/(byteCount >> 7);
91     const int dist = 40;
92
93     int wy = 0; // Drawing position
94
95     QImage histogram(paradeSize, QImage::Format_ARGB32);
96     QPainter davinci(&histogram);
97     davinci.setPen(QColor(220, 220, 220, 255));
98     histogram.fill(qRgba(0, 0, 0, 0));
99
100     if (drawY) {
101         drawComponentFull(&davinci, y, scaling, QRect(0, wy, ww, partH + dist), QColor(220, 220, 210, 255), dist, unscaled, 256);
102
103         wy += partH + d;
104     }
105
106     if (drawSum) {
107         drawComponentFull(&davinci, s, scaling/3, QRect(0, wy, ww, partH + dist), QColor(220, 220, 210, 255), dist, unscaled, 256);
108
109         wy += partH + d;
110     }
111
112     if (drawR) {
113         drawComponentFull(&davinci, r, scaling, QRect(0, wy, ww, partH + dist), QColor(255, 128, 0, 255), dist, unscaled, 256);
114
115         wy += partH + d;
116     }
117
118     if (drawG) {
119         drawComponentFull(&davinci, g, scaling, QRect(0, wy, ww, partH + dist), QColor(128, 255, 0, 255), dist, unscaled, 256);
120         wy += partH + d;
121     }
122
123     if (drawB) {
124         drawComponentFull(&davinci, b, scaling, QRect(0, wy, ww, partH + dist), QColor(0, 128, 255, 255), dist, unscaled, 256);
125
126         wy += partH + d;
127     }
128
129     return histogram;
130 }
131
132 QImage HistogramGenerator::drawComponent(const int *y, const QSize &size, const float &scaling, const QColor &color,
133                                          bool unscaled, uint max) const
134 {
135     QImage component(max, size.height(), QImage::Format_ARGB32);
136     component.fill(qRgba(0, 0, 0, 0));
137     Q_ASSERT(scaling != INFINITY);
138
139     const int partH = size.height();
140     int partY;
141
142     for (uint x = 0; x < max; x++) {
143         // Calculate the height of the curve at position x
144         partY = scaling*y[x];
145
146         // Invert the y axis
147         if (partY > partH-1) { partY = partH-1; }
148         partY = partH-1 - partY;
149
150         for (int k = partH-1; k >= partY; k--) {
151             component.setPixel(x, k, color.rgba());
152         }
153     }
154     if (unscaled && size.width() >= component.width()) {
155         return component;
156     } else {
157         return component.scaled(size, Qt::IgnoreAspectRatio, Qt::FastTransformation);
158     }
159 }
160
161 void HistogramGenerator::drawComponentFull(QPainter *davinci, const int *y, const float &scaling, const QRect &rect,
162                                         const QColor &color, int textSpace, bool unscaled, uint max) const
163 {
164     QImage component = drawComponent(y, rect.size() - QSize(0, textSpace), scaling, color, unscaled, max);
165     davinci->drawImage(rect.topLeft(), component);
166
167     int min = 0;
168     for (uint x = 0; x < max; x++) {
169         min = x;
170         if (y[x] > 0) {
171             break;
172         }
173     }
174     int maxVal = max-1;
175     for (int x = max-1; x >= 0; x--) {
176         maxVal = x;
177         if (y[x] > 0) {
178             break;
179         }
180     }
181
182     const int textY = rect.bottom()-textSpace+15;
183     const int dist = 40;
184     const int cw = component.width();
185
186     davinci->drawText(0,            textY, "min");
187     davinci->drawText(dist,         textY, QString::number(min, 'f', 0));
188
189     davinci->drawText(cw-dist-30,   textY, "max");
190     davinci->drawText(cw-30,        textY, QString::number(maxVal, 'f', 0));
191 }