1 /*
2     Copyright (C) 2010-2015 Matthias Kretz <kretz@kde.org>
3 
4     Permission to use, copy, modify, and distribute this software
5     and its documentation for any purpose and without fee is hereby
6     granted, provided that the above copyright notice appear in all
7     copies and that both that the copyright notice and this
8     permission notice and warranty disclaimer appear in supporting
9     documentation, and that the name of the author not be used in
10     advertising or publicity pertaining to distribution of the
11     software without specific, written prior permission.
12 
13     The author disclaim all warranties with regard to this
14     software, including all implied warranties of merchantability
15     and fitness.  In no event shall the author be liable for any
16     special, indirect or consequential damages or any damages
17     whatsoever resulting from loss of use, data or profits, whether
18     in an action of contract, negligence or other tortious action,
19     arising out of or in connection with the use or performance of
20     this software.
21 
22 */
23 
24 #include "main.h"
25 #include "../tsc.h"
26 #include <complex>
27 #include <cmath>
28 
29 #include <QApplication>
30 #include <QTextStream>
31 #include <QTimer>
32 #include <QtCore/QtDebug>
33 #include <QPainter>
34 #include <QProgressBar>
35 
36 #ifdef Scalar
37 typedef float float_v;
38 typedef int int_v;
39 typedef bool int_m;
40 #ifdef _MSC_VER
41 #define Vc_CDECL __cdecl
42 #else
43 #define Vc_CDECL
44 #endif
45 #else
46 #include <Vc/Vc>
47 
48 using Vc::float_v;
49 using Vc::float_m;
50 using int_v = Vc::SimdArray<int, float_v::size()>;
51 using int_m = int_v::mask_type;
52 #endif
53 
ProgressWriter()54 ProgressWriter::ProgressWriter()
55     : m_out(stdout)
56 {
57 }
58 
setValue(float vf)59 void ProgressWriter::setValue(float vf)
60 {
61     static int lastPercent = -1;
62     static int lastHash = 0;
63     int p = static_cast<int>(vf + 0.5f);
64     int h = static_cast<int>(vf * 0.78f + 0.5f);
65     bool flush = false;
66     if (p != lastPercent) {
67         flush = true;
68         if (lastPercent == -1) {
69             m_out << "\033[80D\033[K"
70                   << "[                                    ";
71             m_out.setFieldWidth(3);
72             m_out << p;
73             m_out.setFieldWidth(0);
74             m_out << "%                                      ]"
75                   << "\033[79D";
76         } else {
77             m_out << "\033[s\033[80D\033[37C";
78             m_out.setFieldWidth(3);
79             m_out << p;
80             m_out.setFieldWidth(0);
81             m_out << "\033[u";
82         }
83         lastPercent = p;
84     }
85     for (; lastHash < h; ++lastHash) {
86         flush = true;
87         if (lastHash < 36 || lastHash > 39) {
88             m_out << '#';
89         } else {
90             m_out << "\033[1C";
91         }
92     }
93     if (flush) {
94         m_out.flush();
95     }
96 }
97 
done()98 void ProgressWriter::done()
99 {
100     setValue(100.f);
101     m_out << "\033[2C";
102     m_out.flush();
103 }
104 
Baker()105 Baker::Baker()
106 {
107 }
108 
setSize(int w,int h)109 void Baker::setSize(int w, int h)
110 {
111     m_y = -1.f;
112     m_height = 2.f;
113 
114     m_width = w * m_height / h;
115     m_x = m_width * -0.667f;
116 
117     m_image = QImage(w, h, QImage::Format_RGB32);
118 }
119 
setFilename(const QString & filename)120 void Baker::setFilename(const QString &filename)
121 {
122     m_filename = filename;
123 }
124 
125 struct Z
126 {
127     using value_type = float_v;
128     value_type real, imag;
129 };
130 
operator *(Z a,Z b)131 inline Z operator*(Z a, Z b)
132 {
133     return {a.real * b.real - a.imag * b.imag, a.real * b.imag + a.imag * b.real};
134 }
operator +(Z a,Z b)135 inline Z operator+(Z a, Z b)
136 {
137     return {a.real + b.real, a.imag + b.imag};
138 }
139 
P(Z z,Z c)140 static inline Z P(Z z, Z c)
141 {
142     return z * z + c;
143 }
144 
fastNorm(const Z & z)145 static inline Z::value_type fastNorm(const Z &z)
146 {
147     return z.real * z.real + z.imag * z.imag;
148 }
149 
square(T a)150 template<typename T> static inline T square(T a) { return a * a; }
minOf(T a,T b)151 template<typename T> static inline T minOf(T a, T b) { return a < b ? a : b; }
maxOf(T a,T b)152 template<typename T> static inline T maxOf(T a, T b) { return a < b ? b : a; }
clamp(T min,T value,T max)153 template<typename T> static inline T clamp(T min, T value, T max)
154 {
155     if (value > max) {
156         return max;
157     }
158     return value < min ? min : value;
159 }
160 
161 struct Pixel
162 {
163     float blue;
164     float green;
165     float red;
166 };
167 
168 static const Pixel NULL_PIXEL = { 0, 0, 0 };
169 
170 class Canvas
171 {
172     public:
173         Canvas(int h, int w);
174         void addDot(float x, float y, int red, int green, int blue);
175         void toQImage(QImage *);
176 
177     private:
addDot(int x,int y,float red,float green,float blue)178         void addDot(int x, int y, float red, float green, float blue) {
179             Pixel &p = m_pixels[x + y * m_width];
180             p.blue  += blue;
181             p.green += green;
182             p.red   += red;
183         }
184         const int m_width;
185         std::vector<Pixel> m_pixels;
186 };
187 
Canvas(int h,int w)188 Canvas::Canvas(int h, int w)
189     : m_width(w), m_pixels(h * w, NULL_PIXEL)
190 {
191 }
192 
addDot(float x,float y,int red,int green,int blue)193 void Canvas::addDot(float x, float y, int red, int green, int blue)
194 {
195     const int x1 = static_cast<int>(std::floor(x));
196     const int x2 = static_cast<int>(std::ceil (x));
197     const int y1 = static_cast<int>(std::floor(y));
198     const int y2 = static_cast<int>(std::ceil (y));
199     const float xfrac = x - std::floor(x);
200     const float yfrac = y - std::floor(y);
201     const float r = red;
202     const float g = green;
203     const float b = blue;
204     const float frac11 = (1.f - xfrac) * (1.f - yfrac);
205     const float frac12 = (1.f - xfrac) * yfrac;
206     const float frac21 = xfrac * (1.f - yfrac);
207     const float frac22 = xfrac * yfrac;
208     addDot(x1, y1, r * frac11, g * frac11, b * frac11);
209     addDot(x2, y1, r * frac21, g * frac21, b * frac21);
210     addDot(x1, y2, r * frac12, g * frac12, b * frac12);
211     addDot(x2, y2, r * frac22, g * frac22, b * frac22);
212 }
213 
214 #define BUDDHABROT_USE_FUNCTION1
215 
216 #ifdef BUDDHABROT_USE_FUNCTION2
reduceRange(float x,float m,float h)217 static inline uchar reduceRange(float x, float m, float h)
218 {
219     /* m: max, h: median
220      * +-                              -+
221      * |        3        3        2     |
222      * |   510 h  + 127 m  - 765 h  m   |
223      * |   --------------------------   |
224      * |         3    3        2  2     |
225      * |      h m  + h  m - 2 h  m      |
226      * |                                |
227      * |         3        3          2  |
228      * |  - 255 h  - 254 m  + 765 h m   |
229      * |  ----------------------------  |
230      * |        4      2  3    3  2     |
231      * |     h m  - 2 h  m  + h  m      |
232      * |                                |
233      * |                    2        2  |
234      * |   - 510 h m + 255 h  + 127 m   |
235      * |   ---------------------------  |
236      * |         4      2  3    3  2    |
237      * |      h m  - 2 h  m  + h  m     |
238      * +-                              -+
239      */
240     const float h2 = h * h;
241     const float h3 = h2 * h;
242     const float m2 = m * m;
243     const float m3 = m2 * m;
244     const float denom = h * m * square(m - h);
245     return minOf(255.f, 0.5f //rounding
246             + x / denom * (
247                 510.f * h3 + 127.f * m3 - 765.f * h2 * m
248                 + x / m * (
249                     765.f * h * m2 - 255.f * h3 - 254.f * m3
250                     + x * (
251                         255.f * h2 + 127.f * m2 - 510.f * h * m)
252                     )));
253 }
254 #elif defined(BUDDHABROT_USE_FUNCTION1)
reduceRange(float x,float m,float h)255 static inline unsigned int reduceRange(float x, float m, float h)
256 {
257     if (x <= m) {
258         return 0.5f // rounding
259             + 4.f / 255.f * h * h / m * x
260             + square(x) * (h / square(m)) * (4.f - 8.f / 255.f * h);
261     } else {
262         return 0.5f // rounding
263             + 255.f - 4.f * h + 4.f / 255.f * square(h)
264             + x / m * (16.f * h - 1020.f - 12.f / 255.f * square(h))
265             + square(x / m) * (1020.f - 12.f * h + 8.f / 255.f * square(h));
266     }
267 }
268 #endif
269 
toQImage(QImage * img)270 void Canvas::toQImage(QImage *img)
271 {
272     uchar *line = img->scanLine(0);
273     const Pixel *p = &m_pixels[0];
274 #ifdef BUDDHABROT_USE_FUNCTION2
275     float max   [3] = { 0.f, 0.f, 0.f };
276     std::vector<float> sorted[3];
277     for (int i = 0; i < 3; ++i) {
278         sorted[i].reserve(m_pixels.size());
279     }
280     for (unsigned int i = 0; i < m_pixels.size(); ++i) {
281         max[0] = maxOf(max[0], m_pixels[i].red);
282         max[1] = maxOf(max[1], m_pixels[i].green);
283         max[2] = maxOf(max[2], m_pixels[i].blue);
284         if (m_pixels[i].red > 1.f) {
285             sorted[0].push_back(m_pixels[i].red);
286         }
287         if (m_pixels[i].green > 1.f) {
288             sorted[1].push_back(m_pixels[i].green);
289         }
290         if (m_pixels[i].blue > 1.f) {
291             sorted[2].push_back(m_pixels[i].blue);
292         }
293     }
294     for (int i = 0; i < 3; ++i) {
295         std::sort(sorted[i].begin(), sorted[i].end());
296     }
297     const float median[3] = {
298         sorted[0][sorted[0].size() / 2],
299         sorted[1][sorted[1].size() / 2],
300         sorted[2][sorted[2].size() / 2]
301     };
302 
303     /*
304     int hist[3][2];
305     for (int i = 0; i < 3; ++i) {
306         hist[i][0] = hist[i][1] = 0;
307     }
308     for (unsigned int i = 0; i < m_pixels.size(); ++i) {
309         ++hist[0][reduceRange(m_pixels[i].red  , max[0], median[0]) / 128];
310         ++hist[1][reduceRange(m_pixels[i].green, max[1], median[1]) / 128];
311         ++hist[2][reduceRange(m_pixels[i].blue , max[2], median[2]) / 128];
312     }
313     qDebug() << "Histogram:\n  red:"
314         << median[0] << hist[0][0] << hist[0][1] << "\ngreen:"
315         << median[1] << hist[1][0] << hist[1][1] << "\n blue:"
316         << median[2] << hist[2][0] << hist[2][1];
317     */
318 
319     for (int yy = 0; yy < img->height(); ++yy) {
320         for (int xx = 0; xx < img->width(); ++xx) {
321             line[0] = reduceRange(p->blue , max[2], median[2]);
322             line[1] = reduceRange(p->green, max[1], median[1]);
323             line[2] = reduceRange(p->red  , max[0], median[0]);
324             line += 4;
325             ++p;
326         }
327     }
328 #elif defined(BUDDHABROT_USE_FUNCTION1)
329     float max[3] = { 0.f, 0.f, 0.f };
330     for (unsigned int i = 0; i < m_pixels.size(); ++i) {
331         max[0] = maxOf(max[0], m_pixels[i].red);
332         max[1] = maxOf(max[1], m_pixels[i].green);
333         max[2] = maxOf(max[2], m_pixels[i].blue);
334     }
335     float h[3] = { 220.f, 220.f, 220.f };
336 
337     /*
338     int hist[3][2];
339     for (int i = 0; i < 3; ++i) {
340         hist[i][0] = hist[i][1] = 0;
341     }
342     for (unsigned int i = 0; i < m_pixels.size(); ++i) {
343         ++hist[0][reduceRange(m_pixels[i].red  , max[0], h[0]) / 128];
344         ++hist[1][reduceRange(m_pixels[i].green, max[1], h[1]) / 128];
345         ++hist[2][reduceRange(m_pixels[i].blue , max[2], h[2]) / 128];
346     }
347     qDebug() << "Histogram:\n  red:"
348         << hist[0][0] << hist[0][1] << "\ngreen:"
349         << hist[1][0] << hist[1][1] << "\n blue:"
350         << hist[2][0] << hist[2][1];
351     */
352 
353     for (int yy = 0; yy < img->height(); ++yy) {
354         for (int xx = 0; xx < img->width(); ++xx) {
355             line[0] = reduceRange(p->blue , max[2], h[2]);
356             line[1] = reduceRange(p->green, max[1], h[1]);
357             line[2] = reduceRange(p->red  , max[0], h[0]);
358             line += 4;
359             ++p;
360         }
361     }
362 #else
363     float max   [3] = { 0.f, 0.f, 0.f };
364     float mean  [3] = { 0.f, 0.f, 0.f };
365     float stddev[3] = { 0.f, 0.f, 0.f };
366     for (unsigned int i = 0; i < m_pixels.size(); ++i) {
367         max[0] = maxOf(max[0], m_pixels[i].red);
368         max[1] = maxOf(max[1], m_pixels[i].green);
369         max[2] = maxOf(max[2], m_pixels[i].blue);
370         mean[0] += m_pixels[i].red;
371         mean[1] += m_pixels[i].green;
372         mean[2] += m_pixels[i].blue;
373         stddev[0] += square(m_pixels[i].red);
374         stddev[1] += square(m_pixels[i].green);
375         stddev[2] += square(m_pixels[i].blue);
376     }
377     const float normalization = 1.f / m_pixels.size();
378     mean[0] *= normalization;
379     mean[1] *= normalization;
380     mean[2] *= normalization;
381     stddev[0] = std::sqrt(stddev[0] * normalization - square(mean[0]));
382     stddev[1] = std::sqrt(stddev[1] * normalization - square(mean[1]));
383     stddev[2] = std::sqrt(stddev[2] * normalization - square(mean[2]));
384     qDebug() << "   max:" << max[0] << max[1] << max[2];
385     qDebug() << "  mean:" << mean[0] << mean[1] << mean[2];
386     qDebug() << "stddev:" << stddev[0] << stddev[1] << stddev[2];
387 
388     // colors have the range 0..max at this point
389     // they should be transformed such that for the resulting mean and stddev:
390     //    mean - stddev = 0
391     //    mean + stddev = min(min(2 * mean, max), 255)
392     //
393     // newColor = (c - mean) * min(min(2 * mean, max), 255) * 0.5 / stddev + 127.5
394 
395     const float center[3] = {
396         minOf(minOf(2.f * mean[0], max[0]), 255.f) * 0.5f,
397         minOf(minOf(2.f * mean[1], max[1]), 255.f) * 0.5f,
398         minOf(minOf(2.f * mean[2], max[2]), 255.f) * 0.5f
399     };
400 
401     const float sdFactor[3] = { 2.f, 2.f, 2.f };
402     const float redFactor   = center[0] / (sdFactor[0] * stddev[0]);
403     const float greenFactor = center[1] / (sdFactor[1] * stddev[1]);
404     const float blueFactor  = center[2] / (sdFactor[2] * stddev[2]);
405 
406     for (int yy = 0; yy < img->height(); ++yy) {
407         for (int xx = 0; xx < img->width(); ++xx) {
408             line[0] = clamp(0, static_cast<int>(center[2] + (p->blue  - mean[2]) * blueFactor ), 255);
409             line[1] = clamp(0, static_cast<int>(center[1] + (p->green - mean[1]) * greenFactor), 255);
410             line[2] = clamp(0, static_cast<int>(center[0] + (p->red   - mean[0]) * redFactor  ), 255);
411             line += 4;
412             ++p;
413         }
414     }
415 #endif
416 }
417 
Options()418 Baker::Options::Options()
419 {
420     red[0] = 2;
421     red[1] = 10;
422     green[0] = 0;
423     green[1] = 1;
424     blue[0] = 11;
425     blue[1] = 20;
426     it[0] = 10000;
427     it[1] = 50000;
428     steps[0] = steps[1] = -1;
429 }
430 
createImage()431 void Baker::createImage()
432 {
433     const int iHeight = m_image.height();
434     const int iWidth  = m_image.width();
435 
436     // Parameters Begin
437     const float S = 4.f;
438     const float nSteps[2]   = {
439         static_cast<float>(m_opt.steps[0] == -1 ? std::sqrt(iWidth) * iWidth : m_opt.steps[0]),
440         static_cast<float>(m_opt.steps[1] == -1 ? std::sqrt(iHeight) * iHeight : m_opt.steps[1])
441     };
442     const int upperBound[3] = { m_opt.red[1], m_opt.green[1], m_opt.blue[1] };
443     const int lowerBound[3] = { m_opt.red[0], m_opt.green[0], m_opt.blue[0] };
444     int overallLowerBound = m_opt.it[0];
445     int maxIterations = m_opt.it[1];// maxOf(maxOf(overallLowerBound, upperBound[0]), maxOf(upperBound[1], upperBound[2]));
446     float realMin = -2.102613f;
447     float realMax =  1.200613f;
448     float imagMin = 0.f;
449     float imagMax = 1.23971f;
450     // Parameters End
451 
452     TimeStampCounter timer;
453     timer.start();
454 
455     // helper constants
456     const int overallUpperBound = maxOf(upperBound[0], maxOf(upperBound[1], upperBound[2]));
457     const float maxX = static_cast<float>(iWidth ) - 1.f;
458     const float maxY = static_cast<float>(iHeight) - 1.f;
459     const float xFact = iWidth / m_width;
460     const float yFact = iHeight / m_height;
461     const float realStep = (realMax - realMin) / nSteps[0];
462     const float imagStep = (imagMax - imagMin) / nSteps[1];
463 
464     Canvas canvas(iHeight, iWidth);
465 #ifdef Scalar
466     for (float real = realMin; real <= realMax; real += realStep) {
467         m_progress.setValue(99.f * (real - realMin) / (realMax - realMin));
468         for (float imag = imagMin; imag <= imagMax; imag += imagStep) {
469             Z c{real, imag};
470             Z c2{1.08f * real + 0.15f, imag};
471             if (fastNorm(Z{real + 1.f, imag}) < 0.06f ||
472                 (c2.real < 0.42f && fastNorm(c2) < 0.417f)) {
473                 continue;
474             }
475             Z z = c;
476             int n;
477             for (n = 0; n <= maxIterations && fastNorm(z) < S; ++n) {
478                 z = P(z, c);
479             }
480             if (n <= maxIterations && n >= overallLowerBound) {
481                 // point is outside of the Mandelbrot set and required enough (overallLowerBound)
482                 // iterations to reach the cut-off value S
483                 Z cn{real, -imag};
484                 Z zn = cn;
485                 z = c;
486                 for (int i = 0; i <= overallUpperBound; ++i) {
487                     const float y2 = (z.imag - m_y) * yFact;
488                     const float yn2 = (zn.imag - m_y) * yFact;
489                     if (y2 >= 0.f && y2 < maxY && yn2 >= 0.f && yn2 < maxY) {
490                         const float x2 = (z.real - m_x) * xFact;
491                         if (x2 >= 0.f && x2 < maxX) {
492                             const int red   = (i >= lowerBound[0] && i <= upperBound[0]) ? 1 : 0;
493                             const int green = (i >= lowerBound[1] && i <= upperBound[1]) ? 1 : 0;
494                             const int blue  = (i >= lowerBound[2] && i <= upperBound[2]) ? 1 : 0;
495                             canvas.addDot(x2, y2 , red, green, blue);
496                             canvas.addDot(x2, yn2, red, green, blue);
497                         }
498                     }
499                     z = P(z, c);
500                     zn = P(zn, cn);
501                     if (fastNorm(z) >= S) { // optimization: skip some useless looping
502                         break;
503                     }
504                 }
505             }
506         }
507     }
508 #else
509     const float imagStep2 = imagStep * float_v::Size;
510     const float_v imagMin2 = imagMin + imagStep * float_v::IndexesFromZero();
511     for (float real = realMin; real <= realMax; real += realStep) {
512         m_progress.setValue(99.f * (real - realMin) / (realMax - realMin));
513         for (float_v imag = imagMin2; all_of(imag <= imagMax); imag += imagStep2) {
514             // FIXME: extra "tracks" if nSteps[1] is not a multiple of float_v::Size
515             Z c{real, imag};
516             Z c2 = Z{1.08f * real + 0.15f, imag};
517             if (all_of(fastNorm(Z{real + 1.f, imag}) < 0.06f ||
518                        (c2.real < 0.42f && fastNorm(c2) < 0.417f))) {
519                 continue;
520             }
521             Z z = c;
522             int_v n(Vc::Zero);
523             int_m inside = fastNorm(z) < S;
524             while (!(inside && n <= maxIterations).isEmpty()) {
525                 z = P(z, c);
526                 ++n(inside);
527                 inside &= fastNorm(z) < S;
528             }
529             inside |= n < overallLowerBound;
530             if (inside.isFull()) {
531                 continue;
532             }
533             Z cn{real, -imag};
534             Z zn = cn;
535             z = c;
536             for (int i = 0; i <= overallUpperBound; ++i) {
537                 const float_v y2 = (z.imag - m_y) * yFact;
538                 const float_v yn2 = (zn.imag - m_y) * yFact;
539                 const float_v x2 = (z.real - m_x) * xFact;
540                 z = P(z, c);
541                 zn = P(zn, cn);
542                 const float_m drawMask = !inside && y2 >= 0.f && x2 >= 0.f && y2 < maxY && x2 < maxX && yn2 >= 0.f && yn2 < maxY;
543 
544                 const int red   = (i >= lowerBound[0] && i <= upperBound[0]) ? 1 : 0;
545                 const int green = (i >= lowerBound[1] && i <= upperBound[1]) ? 1 : 0;
546                 const int blue  = (i >= lowerBound[2] && i <= upperBound[2]) ? 1 : 0;
547 
548                 for(int j : where(drawMask)) {
549                     canvas.addDot(x2[j], y2 [j], red, green, blue);
550                     canvas.addDot(x2[j], yn2[j], red, green, blue);
551                 }
552                 if (all_of(fastNorm(z) >= S)) {  // optimization: skip some useless looping
553                     break;
554                 }
555             }
556         }
557     }
558 #endif
559     canvas.toQImage(&m_image);
560 
561     timer.stop();
562     m_progress.done();
563     qDebug() << timer.cycles() << "cycles";
564 
565     if (m_filename.isEmpty()) {
566         m_filename = QString("r%1-%2_g%3-%4_b%5-%6_s%7-%8_i%9-%10_%11x%12.png")
567             .arg(lowerBound[0]).arg(upperBound[0])
568             .arg(lowerBound[1]).arg(upperBound[1])
569             .arg(lowerBound[2]).arg(upperBound[2])
570             .arg(nSteps[0]).arg(nSteps[1])
571             .arg(overallLowerBound).arg(maxIterations)
572             .arg(m_image.width()).arg(m_image.height());
573     }
574 
575     m_image.save(m_filename);
576 }
577 
usage(const char * argv0)578 static void usage(const char *argv0)
579 {
580     Baker::Options o;
581 
582     QTextStream out(stdout);
583     out << "Usage: " << argv0 << " [options] [<filename>]\n\n"
584         << "Options:\n"
585         << "  -h|--help               This message.\n"
586         << "  -s|--size <w> <h>       Specify the width and height of the resulting image file. [1024 768]\n"
587         << "  -r|--red   <int> <int>  Specify lower and upper iteration bounds for a red trace. ["
588         << o.red[0] << ' ' << o.red[1] << "]\n"
589         << "  -g|--green <int> <int>  Specify lower and upper iteration bounds for a green trace. ["
590         << o.green[0] << ' ' << o.green[1] << "]\n"
591         << "  -b|--blue  <int> <int>  Specify lower and upper iteration bounds for a blue trace. ["
592         << o.blue[0] << ' ' << o.blue[1] << "]\n"
593         << "  --steps <int> <int>     Specify the steps in real and imaginary direction. [width^1.5 height^1.5]\n"
594         << "  --minIt <int>           Overall lower iteration bound. [" << o.it[0] << "]\n"
595         << "  --maxIt <int>           Overall upper iteration bound. [" << o.it[1] << "]\n"
596         ;
597 }
598 
main(int argc,char ** argv)599 int Vc_CDECL main(int argc, char **argv)
600 {
601     QCoreApplication app(argc, argv);
602     const QStringList &args = QCoreApplication::arguments();
603     if (args.contains("--help") || args.contains("-h")) {
604         usage(argv[0]);
605         return 0;
606     }
607 
608     Baker b;
609 
610     Baker::Options opt;
611     int width = 1024;
612     int height = 768;
613 
614     // parse args
615     for (int i = 1; i < args.size(); ++i) {
616         const QString &arg = args[i];
617         bool ok = true;
618         if (arg == QLatin1String("--red") || arg == QLatin1String("-r")) {
619             opt.red[0] = args[++i].toInt(&ok);
620             if (ok) {
621                 opt.red[1] = args[++i].toInt(&ok);
622             }
623         } else if (arg == QLatin1String("--green") || arg == QLatin1String("-g")) {
624             opt.green[0] = args[++i].toInt(&ok);
625             if (ok) {
626                 opt.green[1] = args[++i].toInt(&ok);
627             }
628         } else if (arg == QLatin1String("--blue") || arg == QLatin1String("-b")) {
629             opt.blue[0] = args[++i].toInt(&ok);
630             if (ok) {
631                 opt.blue[1] = args[++i].toInt(&ok);
632             }
633         } else if (arg == QLatin1String("--steps")) {
634             opt.steps[0] = args[++i].toInt(&ok);
635             if (ok) {
636                 opt.steps[1] = args[++i].toInt(&ok);
637             }
638         } else if (arg == QLatin1String("--minIt")) {
639             opt.it[0] = args[++i].toInt(&ok);
640         } else if (arg == QLatin1String("--maxIt")) {
641             opt.it[1] = args[++i].toInt(&ok);
642         } else if (arg == QLatin1String("--size") || arg == QLatin1String("-s")) {
643             width = args[++i].toInt(&ok);
644             if (ok) {
645                 height = args[++i].toInt(&ok);
646             }
647         } else {
648             static bool filenameSet = false;
649             ok = !filenameSet;
650             filenameSet = true;
651             b.setFilename(arg);
652         }
653         if (!ok) {
654             usage(argv[0]);
655             return 1;
656         }
657     }
658 
659     b.setOptions(opt);
660     b.setSize(width, height);
661     b.createImage();
662     return 0;
663 }
664