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 ***************************************************************************/
12 #include "colortools.h"
20 ColorTools::ColorTools()
26 QImage ColorTools::yuvColorWheel(const QSize &size, const unsigned char &Y, const float &scaling, const bool &modifiedVersion, const bool &circleOnly)
28 QImage wheel(size, QImage::Format_ARGB32);
29 if (size.width() == 0 || size.height() == 0) {
30 qCritical("ERROR: Size of the color wheel must not be 0!");
34 wheel.fill(qRgba(0,0,0,0));
37 double dr, dg, db, du, dv, dmax;
39 const int w = size.width();
40 const int h = size.height();
41 const float w2 = (float)w/2;
42 const float h2 = (float)h/2;
44 for (int u = 0; u < w; u++) {
45 // Transform u from {0,...,w} to [-1,1]
46 du = (double) 2*u/(w-1) - 1;
49 for (int v = 0; v < h; v++) {
50 dv = (double) 2*v/(h-1) - 1;
54 // Ellipsis equation: x²/a² + y²/b² = 1
55 // Here: x=ru, y=rv, a=w/2, b=h/2, 1=rr
56 // For rr > 1, the point lies outside. Don't draw it.
59 rr = ru*ru/(w2*w2) + rv*rv/(h2*h2);
65 // Calculate the RGB values from YUV
67 dg = Y - 100.6*du - 148*dv;
70 if (modifiedVersion) {
71 // Scale the RGB values down, or up, to max 255
73 if (fabs(dg) > dmax) dmax = fabs(dg);
74 if (fabs(db) > dmax) dmax = fabs(db);
82 // Avoid overflows (which would generate intersting patterns).
83 // Note that not all possible (y,u,v) values with u,v \in [-1,1]
84 // have a correct RGB representation, therefore some RGB values
85 // may exceed {0,...,255}.
89 if (dr > 255) dr = 255;
90 if (dg > 255) dg = 255;
91 if (db > 255) db = 255;
93 wheel.setPixel(u, (h-v-1), qRgba(dr, dg, db, 255));
97 emit signalYuvWheelCalculationFinished();
101 QImage ColorTools::yuvVerticalPlane(const QSize &size, const float &angle, const float &scaling)
103 QImage plane(size, QImage::Format_ARGB32);
104 if (size.width() == 0 || size.height() == 0) {
105 qCritical("ERROR: Size of the color plane must not be 0!");
109 double dr, dg, db, du, dv, Y;
110 const int w = size.width();
111 const int h = size.height();
112 const double uscaling = scaling*cos(M_PI*angle/180);
113 const double vscaling = scaling*sin(M_PI*angle/180);
115 for (int uv = 0; uv < w; uv++) {
116 du = uscaling*((double)2*uv/w - 1);//(double)?
117 dv = vscaling*((double)2*uv/w - 1);
119 for (int y = 0; y < h; y++) {
122 // See yuv2rgb, yuvColorWheel
124 dg = Y - 100.6*du - 148*dv;
129 if (dr > 255) dr = 255;
130 if (dg > 255) dg = 255;
131 if (db > 255) db = 255;
133 plane.setPixel(uv, (h-y-1), qRgba(dr, dg, db, 255));
143 QImage ColorTools::rgbCurvePlane(const QSize &size, const ColorTools::ColorsRGB &color, float scaling, const QRgb &background)
145 Q_ASSERT(scaling > 0 && scaling <= 1);
147 QImage plane(size, QImage::Format_ARGB32);
148 if (size.width() == 0 || size.height() == 0) {
149 qCritical("ERROR: Size of the color plane must not be 0!");
153 const int w = size.width();
154 const int h = size.height();
159 for (int x = 0; x < w; x++) {
160 dval = (double)255*x/(w-1);
162 for (int y = 0; y < h; y++) {
163 dy = (double)y/(h-1);
164 dx = (double)x/(w-1);
166 if (1-scaling < 0.0001) {
167 dcol = (double)255*dy;
169 dcol = (double)255 * (dy - (dy-dx)*(1-scaling));
172 if (color == ColorTools::COL_R) {
173 plane.setPixel(x, (h-y-1), qRgb(dcol, dval, dval));
174 } else if (color == ColorTools::COL_G) {
175 plane.setPixel(x, (h-y-1), qRgb(dval, dcol, dval));
176 } else if (color == ColorTools::COL_B){
177 plane.setPixel(x, (h-y-1), qRgb(dval, dval, dcol));
178 } else if (color == ColorTools::COL_A) {
179 plane.setPixel(x, (h-y-1), qRgb(dcol / 255. * qRed(background), dcol / 255. * qGreen(background), dcol / 255. * qBlue(background)));
181 plane.setPixel(x, (h-y-1), qRgb(dcol, dcol, dcol));
189 QImage ColorTools::yPbPrColorWheel(const QSize &size, const unsigned char &Y, const float &scaling, const bool &circleOnly)
192 QImage wheel(size, QImage::Format_ARGB32);
193 if (size.width() == 0 || size.height() == 0) {
194 qCritical("ERROR: Size of the color wheel must not be 0!");
198 wheel.fill(qRgba(0,0,0,0));
201 double dr, dg, db, dpB, dpR;
203 const int w = size.width();
204 const int h = size.height();
205 const float w2 = (float)w/2;
206 const float h2 = (float)h/2;
208 for (int b = 0; b < w; b++) {
209 // Transform pB from {0,...,w} to [-0.5,0.5]
210 dpB = (double) b/(w-1) - .5;
213 for (int r = 0; r < h; r++) {
214 dpR = (double) r/(h-1) - .5;
221 rr = rB*rB/(w2*w2) + rR*rR/(h2*h2);
227 // Calculate the RGB values from YPbPr
229 dg = Y - 87.75*dpB - 182.1*dpR;
232 // Avoid overflows (which would generate intersting patterns).
233 // Note that not all possible (y,u,v) values with u,v \in [-1,1]
234 // have a correct RGB representation, therefore some RGB values
235 // may exceed {0,...,255}.
239 if (dr > 255) dr = 255;
240 if (dg > 255) dg = 255;
241 if (db > 255) db = 255;
243 wheel.setPixel(b, (h-r-1), qRgba(dr, dg, db, 255));
250 QImage ColorTools::hsvHueShiftPlane(const QSize &size, const uint &S, const uint &V, const int &MIN, const int &MAX)
252 Q_ASSERT(size.width() > 0);
253 Q_ASSERT(size.height() > 0);
256 QImage plane(size, QImage::Format_ARGB32);
259 qDebug() << "Requested: Saturation " << S << ", Value " << V;
260 QColor colTest(-1, 256, 257);
261 qDebug() << "-1 mapped to " << colTest.red() << ", 256 to " << colTest.green() << ", 257 to " << colTest.blue();
266 const int hueValues = MAX-MIN;
270 for (int x = 0; x < size.width(); x++) {
271 hue = x/(size.width() - 1.0) * 359;
272 for (int y = 0; y < size.height(); y++) {
273 huediff = (1.0f - y/(size.height() - 1.0)) * hueValues + MIN;
274 // qDebug() << "hue: " << hue << ", huediff: " << huediff;
276 newhue = hue + huediff + 360; // Avoid negative numbers. Rest (>360) will be mapped correctly.
278 col.setHsv(newhue, S, V);
279 plane.setPixel(x, y, col.rgba());
288 QImage ColorTools::hsvCurvePlane(const QSize &size, const QColor &baseColor,
289 const ComponentsHSV &xVariant, const ComponentsHSV &yVariant, const bool &shear, const float offsetY)
291 Q_ASSERT(size.width() > 0);
292 Q_ASSERT(size.height() > 0);
317 QImage plane(size, QImage::Format_ARGB32);
322 hue = baseColor.hueF();
323 sat = baseColor.saturationF();
324 val = baseColor.valueF();
326 for (int x = 0; x < size.width(); x++) {
329 hue = x / (size.width()-1.0);
332 sat = x / (size.width()-1.0);
335 val = x / (size.width()-1.0);
338 for (int y = 0; y < size.height(); y++) {
341 hue = 1.0 - y / (size.height()-1.0);
344 sat = 1.0 - y / (size.height()-1.0);
347 val = 1.0 - y / (size.height()-1.0);
351 col.setHsvF(hue, sat, val);
354 plane.setPixel(x, y, col.rgba());
356 plane.setPixel(x, int(2*size.height() + y - x*size.width()/size.height() - offsetY * size.height()) % size.height(), col.rgba());