]> git.sesse.net Git - kdenlive/blob - src/colorcorrection/waveformgenerator.cpp
21d4b9cc25a9d3282a2cbccfffd09c1ac2a685c0
[kdenlive] / src / colorcorrection / waveformgenerator.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 <cmath>
12
13 #include <QColor>
14 #include <QDebug>
15 #include <QImage>
16 #include <QPainter>
17 #include <QSize>
18 #include <QTime>
19
20 #include "waveformgenerator.h"
21
22 #define CHOP255(a) ((255) < (a) ? (255) : (a))
23
24 WaveformGenerator::WaveformGenerator()
25 {
26 }
27
28 WaveformGenerator::~WaveformGenerator()
29 {
30 }
31
32 QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QImage &image, WaveformGenerator::PaintMode paintMode,
33                                             const bool &drawAxis, WaveformGenerator::Rec rec, const uint &accelFactor)
34 {
35     Q_ASSERT(accelFactor >= 1);
36
37     QTime time;
38     time.start();
39
40     QImage wave(waveformSize, QImage::Format_ARGB32);
41
42     if (waveformSize.width() <= 0 || waveformSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
43         return QImage();
44
45     } else {
46
47         // Fill with transparent color
48         wave.fill(qRgba(0,0,0,0));
49
50         QRgb *col;
51
52         double dY, dx, dy;
53
54         const uint ww = waveformSize.width();
55         const uint wh = waveformSize.height();
56         const uint iw = image.bytesPerLine();
57         const uint ih = image.height();
58         const uint byteCount = iw*ih;
59
60         uint waveValues[waveformSize.width()][waveformSize.height()];
61         for (int i = 0; i < waveformSize.width(); i++) {
62             for (int j = 0; j < waveformSize.height(); j++) {
63                 waveValues[i][j] = 0;
64             }
65         }
66
67         // Number of input pixels that will fall on one scope pixel.
68         // Must be a float because the acceleration factor can be high, leading to <1 expected px per px.
69         const float pixelDepth = (float)((byteCount>>2) / accelFactor)/(ww*wh);
70         const float gain = 255/(8*pixelDepth);
71         qDebug() << "Pixel depth: expected " << pixelDepth << "; Gain: using " << gain << " (acceleration: " << accelFactor << "x)";
72
73
74         // Subtract 1 from sizes because we start counting from 0.
75         // Not doing it would result in attempts to paint outside of the image.
76         const float hPrediv = (float)(wh-1)/255;
77         const float wPrediv = (float)(ww-1)/(iw-1);
78
79         const uchar *bits = image.bits();
80         const uchar *bitsStart = bits;
81
82         for (uint i = 0, x = 0; i < byteCount; i += 4) {
83
84             Q_ASSERT(bits < bitsStart + byteCount);
85
86             col = (QRgb *)bits;
87
88             if (rec == WaveformGenerator::Rec_601) {
89                 // CIE 601 Luminance
90                 dY = .299*qRed(*col) + .587*qGreen(*col) + .114*qBlue(*col);
91             } else {
92                 // CIE 709 Luminance
93                 dY = .2125*qRed(*col) + .7154*qGreen(*col) + .0721*qBlue(*col);
94             }
95             // dY is on [0,255] now.
96
97             dy = dY*hPrediv;
98             dx = x*wPrediv;
99             waveValues[(int)dx][(int)dy]++;
100
101             bits += 4;
102             x += 4;
103             if (x > iw) {
104                 x -= iw;
105                 if (accelFactor > 1) {
106                     bits += 4*iw*(accelFactor-1);
107                     i += 4*iw*(accelFactor-1);
108                 }
109             }
110         }
111
112         switch (paintMode) {
113         case PaintMode_Green:
114             for (int i = 0; i < waveformSize.width(); i++) {
115                 for (int j = 0; j < waveformSize.height(); j++) {
116                     // Logarithmic scale. Needs fine tuning by hand, but looks great.
117                     wave.setPixel(i, waveformSize.height()-j-1, qRgba(CHOP255(52*log(0.1*gain*waveValues[i][j])),
118                                                                       CHOP255(52*log(gain*waveValues[i][j])),
119                                                                       CHOP255(52*log(.25*gain*waveValues[i][j])),
120                                                                       CHOP255(64*log(gain*waveValues[i][j]))));
121                 }
122             }
123             break;
124         case PaintMode_Yellow:
125             for (int i = 0; i < waveformSize.width(); i++) {
126                 for (int j = 0; j < waveformSize.height(); j++) {
127                     wave.setPixel(i, waveformSize.height()-j-1, qRgba(255,242,0,   CHOP255(gain*waveValues[i][j])));
128                 }
129             }
130             break;
131         default:
132             for (int i = 0; i < waveformSize.width(); i++) {
133                 for (int j = 0; j < waveformSize.height(); j++) {
134                     wave.setPixel(i, waveformSize.height()-j-1, qRgba(255,255,255, CHOP255(2*gain*waveValues[i][j])));
135                 }
136             }
137             break;
138         }
139
140         if (drawAxis) {
141             QPainter davinci(&wave);
142             QRgb opx;
143             davinci.setPen(qRgba(150,255,200,32));
144             davinci.setCompositionMode(QPainter::CompositionMode_Overlay);
145             for (uint i = 0; i <= 10; i++) {
146                 dy = (float)i/10 * (wh-1);
147                 for (uint x = 0; x < ww; x++) {
148                     opx = wave.pixel(x, dy);
149                     wave.setPixel(x,dy, qRgba(CHOP255(150+qRed(opx)), 255,
150                                               CHOP255(200+qBlue(opx)), CHOP255(32+qAlpha(opx))));
151                 }
152             }
153         }
154
155     }
156
157     uint diff = time.elapsed();
158     emit signalCalculationFinished(wave, diff);
159
160     return wave;
161 }
162 #undef CHOP255