1 /***************************************************************************
2 * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
3 * This file is part of kdenlive. See www.kdenlive.org. *
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 ***************************************************************************/
15 The basis matrix for converting RGB to YUV is:
19 0.29900 0.58700 0.11400 1.00000
20 -0.14741 -0.28939 0.43680 x 0.00000
21 0.61478 -0.51480 -0.09998 0.00000
23 The resulting YUV value is then drawn on the circle
24 using U and V as coordinate values.
26 The maximum length of such an UV vector is reached
27 for the colors Red and Cyan: 0.632.
28 To make optimal use of space in the circle, this value
29 can be used for scaling.
31 As we are dealing with RGB values in a range of {0,...,255}
32 and the conversion values are made for [0,1], we already
33 divide the conversion values by 255 previously, e.g. in
37 http://de.wikipedia.org/wiki/Vektorskop
38 http://www.elektroniktutor.de/techno/vektskop.html
45 #include "vectorscopegenerator.h"
47 // The maximum distance from the center for any RGB color is 0.63, so
48 // no need to make the circle bigger than required.
49 const float SCALING = 1/.7;
51 const float VectorscopeGenerator::scaling = 1/.7;
54 Input point is on [-1,1]², 0 being at the center,
55 and positive directions are →top/→right.
57 Maps to the coordinates used in QImages with the 0 point
58 at the top left corner.
77 1. Scale from [-1,1] to [0,1] with y01 := (y+1)/2
78 2. Invert (Orientation of the y axis changes) with y10 := 1-y01
79 3. Scale from [1,0] to [height-1,0] with yy := (height-1) * y10
80 x does not need to be inverted.
83 QPoint VectorscopeGenerator::mapToCircle(const QSize &targetSize, const QPointF &point) const
85 return QPoint( (targetSize.width() -1) * (point.x()+1)/2,
86 (targetSize.height()-1) * (1 - (point.y()+1)/2) );
89 QImage VectorscopeGenerator::calculateVectorscope(const QSize &vectorscopeSize, const QImage &image, const float &gain,
90 const VectorscopeGenerator::PaintMode &paintMode, const bool&,
91 const uint &accelFactor) const
93 if (vectorscopeSize.width() <= 0 || vectorscopeSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
98 // Prepare the vectorscope data
99 const int cw = (vectorscopeSize.width() < vectorscopeSize.height()) ? vectorscopeSize.width() : vectorscopeSize.height();
100 QImage scope = QImage(cw, cw, QImage::Format_ARGB32);
101 scope.fill(qRgba(0,0,0,0));
103 const uchar *bits = image.bits();
106 double dy, dr, dg, db, dmax;
111 const int stepsize = 4*accelFactor;
113 // Just an average for the number of image pixels per scope pixel.
114 // NOTE: byteCount() has to be replaced by (img.bytesPerLine()*img.height()) for Qt 4.5 to compile, see: http://doc.trolltech.org/4.6/qimage.html#bytesPerLine
115 double avgPxPerPx = (double) 4*(image.bytesPerLine()*image.height())/scope.size().width()/scope.size().height()/accelFactor;
116 qDebug() << "Expecting " << avgPxPerPx << " pixels per pixel.";
118 for (int i = 0; i < (image.bytesPerLine()*image.height()); i+= stepsize) {
119 QRgb *col = (QRgb *) bits;
125 y = (double) 0.001173 * r +0.002302 * g +0.0004471* b;
126 u = (double) -0.0005781* r -0.001135 * g +0.001713 * b;
127 v = (double) 0.002411 * r -0.002019 * g -0.0003921* b;
129 pt = mapToCircle(vectorscopeSize, QPointF(SCALING*gain*u, SCALING*gain*v));
131 if (pt.x() >= scope.width() || pt.x() < 0
132 || pt.y() >= scope.height() || pt.y() < 0) {
133 // Point lies outside (because of scaling), don't plot it
137 // Draw the pixel using the chosen draw mode.
141 dy = 128; // Default Y value. Lower = darker.
143 // Calculate the RGB values from YUV
145 dg = dy - 100.6*u - 148*v;
151 if (dr > 255) dr = 255;
152 if (dg > 255) dg = 255;
153 if (db > 255) db = 255;
155 scope.setPixel(pt, qRgba(dr, dg, db, 255));
158 case PaintMode_Chroma:
159 dy = 200; // Default Y value. Lower = darker.
161 // Calculate the RGB values from YUV
163 dg = dy - 100.6*u - 148*v;
166 // Scale the RGB values back to max 255
168 if (dg > dmax) dmax = dg;
169 if (db > dmax) dmax = db;
176 scope.setPixel(pt, qRgba(dr, dg, db, 255));
178 case PaintMode_Original:
179 scope.setPixel(pt, *col);
181 case PaintMode_Green:
182 px = scope.pixel(pt);
183 scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/(3*avgPxPerPx), qGreen(px)+20*(255-qGreen(px))/(avgPxPerPx),
184 qBlue(px)+(255-qBlue(px))/(avgPxPerPx), qAlpha(px)+(255-qAlpha(px))/(avgPxPerPx)));
186 case PaintMode_Green2:
187 px = scope.pixel(pt);
188 scope.setPixel(pt, qRgba(qRed(px)+ceil((255-(float)qRed(px))/(4*avgPxPerPx)), 255,
189 qBlue(px)+ceil((255-(float)qBlue(px))/(avgPxPerPx)), qAlpha(px)+ceil((255-(float)qAlpha(px))/(avgPxPerPx))));
191 case PaintMode_Black:
192 px = scope.pixel(pt);
193 scope.setPixel(pt, qRgba(0,0,0, qAlpha(px)+(255-qAlpha(px))/20));