]> git.sesse.net Git - kdenlive/blob - src/colortools.cpp
Fix label
[kdenlive] / src / colortools.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 <math.h>
12 #include "colortools.h"
13 #include <QColor>
14
15 //#define DEBUG_CT
16 #ifdef DEBUG_CT
17 #include <QDebug>
18 #endif
19
20 ColorTools::ColorTools()
21 {
22 }
23
24
25
26 QImage ColorTools::yuvColorWheel(const QSize &size, const unsigned char &Y, const float &scaling, const bool &modifiedVersion, const bool &circleOnly)
27 {
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!");
31         return wheel;
32     }
33     if (circleOnly) {
34         wheel.fill(qRgba(0,0,0,0));
35     }
36
37     double dr, dg, db, du, dv, dmax;
38     double ru, rv, rr;
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;
43
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;
47         du = scaling*du;
48
49         for (int v = 0; v < h; v++) {
50             dv = (double) 2*v/(h-1) - 1;
51             dv = scaling*dv;
52
53             if (circleOnly) {
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.
57                 ru = u - w2;
58                 rv = v - h2;
59                 rr = ru*ru/(w2*w2) + rv*rv/(h2*h2);
60                 if (rr > 1) {
61                     continue;
62                 }
63             }
64
65             // Calculate the RGB values from YUV
66             dr = Y + 290.8*dv;
67             dg = Y - 100.6*du - 148*dv;
68             db = Y + 517.2*du;
69
70             if (modifiedVersion) {
71                 // Scale the RGB values down, or up, to max 255
72                 dmax = fabs(dr);
73                 if (fabs(dg) > dmax) dmax = fabs(dg);
74                 if (fabs(db) > dmax) dmax = fabs(db);
75                 dmax = 255/dmax;
76
77                 dr *= dmax;
78                 dg *= dmax;
79                 db *= dmax;
80             }
81
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}.
86             if (dr < 0) dr = 0;
87             if (dg < 0) dg = 0;
88             if (db < 0) db = 0;
89             if (dr > 255) dr = 255;
90             if (dg > 255) dg = 255;
91             if (db > 255) db = 255;
92
93             wheel.setPixel(u, (h-v-1), qRgba(dr, dg, db, 255));
94         }
95     }
96
97     emit signalYuvWheelCalculationFinished();
98     return wheel;
99 }
100
101 QImage ColorTools::yuvVerticalPlane(const QSize &size, const float &angle, const float &scaling)
102 {
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!");
106         return plane;
107     }
108
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);
114
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);
118
119         for (int y = 0; y < h; y++) {
120             Y = (double)255*y/h;
121
122             // See yuv2rgb, yuvColorWheel
123             dr = Y + 290.8*dv;
124             dg = Y - 100.6*du - 148*dv;
125             db = Y + 517.2*du;
126             if (dr < 0) dr = 0;
127             if (dg < 0) dg = 0;
128             if (db < 0) db = 0;
129             if (dr > 255) dr = 255;
130             if (dg > 255) dg = 255;
131             if (db > 255) db = 255;
132
133             plane.setPixel(uv, (h-y-1), qRgba(dr, dg, db, 255));
134
135         }
136     }
137
138
139     return plane;
140
141 }
142
143 QImage ColorTools::rgbCurvePlane(const QSize &size, const ColorTools::ColorsRGB &color, float scaling, const QRgb &background)
144 {
145     Q_ASSERT(scaling > 0 && scaling <= 1);
146
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!");
150         return plane;
151     }
152
153     const int w = size.width();
154     const int h = size.height();
155
156     double dcol, dval;
157     double dx, dy;
158
159     for (int x = 0; x < w; x++) {
160         dval = (double)255*x/(w-1);
161
162         for (int y = 0; y < h; y++) {
163             dy = (double)y/(h-1);
164             dx = (double)x/(w-1);
165
166             if (1-scaling < 0.0001) {
167                 dcol = (double)255*dy;
168             } else {
169                 dcol = (double)255 * (dy - (dy-dx)*(1-scaling));
170             }
171
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)));
180             } else {
181                 plane.setPixel(x, (h-y-1), qRgb(dcol, dcol, dcol));
182             }
183
184         }
185     }
186     return plane;
187 }
188
189 QImage ColorTools::yPbPrColorWheel(const QSize &size, const unsigned char &Y, const float &scaling, const bool &circleOnly)
190 {
191
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!");
195         return wheel;
196     }
197     if (circleOnly) {
198         wheel.fill(qRgba(0,0,0,0));
199     }
200
201     double dr, dg, db, dpB, dpR;
202     double rB, rR, rr;
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;
207
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;
211         dpB = scaling*dpB;
212
213         for (int r = 0; r < h; r++) {
214             dpR = (double) r/(h-1) - .5;
215             dpR = scaling*dpR;
216
217             if (circleOnly) {
218                 // see yuvColorWheel
219                 rB = b - w2;
220                 rR = r - h2;
221                 rr = rB*rB/(w2*w2) + rR*rR/(h2*h2);
222                 if (rr > 1) {
223                     continue;
224                 }
225             }
226
227             // Calculate the RGB values from YPbPr
228             dr = Y + 357.5*dpR;
229             dg = Y - 87.75*dpB - 182.1*dpR;
230             db = Y + 451.86*dpB;
231
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}.
236             if (dr < 0) dr = 0;
237             if (dg < 0) dg = 0;
238             if (db < 0) db = 0;
239             if (dr > 255) dr = 255;
240             if (dg > 255) dg = 255;
241             if (db > 255) db = 255;
242
243             wheel.setPixel(b, (h-r-1), qRgba(dr, dg, db, 255));
244         }
245     }
246
247     return wheel;
248 }
249
250 QImage ColorTools::hsvHueShiftPlane(const QSize &size, const uint &S, const uint &V, const int &MIN, const int &MAX)
251 {
252     Q_ASSERT(size.width() > 0);
253     Q_ASSERT(size.height() > 0);
254     Q_ASSERT(MAX > MIN);
255
256     QImage plane(size, QImage::Format_ARGB32);
257
258 #ifdef DEBUG_CT
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();
262 #endif
263
264     QColor col(0, 0, 0);
265
266     const int hueValues = MAX-MIN;
267
268     float hue, huediff;
269     int newhue;
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;
275
276             newhue = hue + huediff + 360; // Avoid negative numbers. Rest (>360) will be mapped correctly.
277
278             col.setHsv(newhue, S, V);
279             plane.setPixel(x, y, col.rgba());
280
281         }
282     }
283
284     return plane;
285
286 }
287
288 QImage ColorTools::hsvCurvePlane(const QSize &size, const QColor &baseColor,
289                                  const ComponentsHSV &xVariant, const ComponentsHSV &yVariant, const bool &shear, const float offsetY)
290 {
291     Q_ASSERT(size.width() > 0);
292     Q_ASSERT(size.height() > 0);
293
294     /*int xMax, yMax;
295
296     switch(xVariant) {
297     case COM_H:
298         xMax = 360;
299         break;
300     case COM_S:
301     case COM_V:
302         xMax = 256;
303         break;
304     }
305
306     switch (yVariant) {
307     case COM_H:
308         yMax = 360;
309         break;
310     case COM_S:
311     case COM_V:
312         yMax = 256;
313         break;
314     }*/
315
316
317     QImage plane(size, QImage::Format_ARGB32);
318
319     QColor col(0, 0, 0);
320
321     float hue, sat, val;
322     hue = baseColor.hueF();
323     sat = baseColor.saturationF();
324     val = baseColor.valueF();
325
326     for (int x = 0; x < size.width(); x++) {
327         switch (xVariant) {
328         case COM_H:
329             hue = x / (size.width()-1.0);
330             break;
331         case COM_S:
332             sat = x / (size.width()-1.0);
333             break;
334         case COM_V:
335             val = x / (size.width()-1.0);
336             break;
337         }
338         for (int y = 0; y < size.height(); y++) {
339             switch (yVariant) {
340             case COM_H:
341                 hue = 1.0 - y / (size.height()-1.0);
342                 break;
343             case COM_S:
344                 sat = 1.0 - y / (size.height()-1.0);
345                 break;
346             case COM_V:
347                 val = 1.0 - y / (size.height()-1.0);
348                 break;
349             }
350
351             col.setHsvF(hue, sat, val);
352
353             if (!shear) {
354                 plane.setPixel(x, y, col.rgba());
355             } else {
356                 plane.setPixel(x, int(2*size.height() + y - x*size.width()/size.height() - offsetY * size.height()) % size.height(), col.rgba());
357             }
358         }
359     }
360
361     return plane;
362
363 }
364
365
366
367
368
369
370
371
372
373 #include "colortools.moc"