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