29 #include <QApplication> 30 #include <QTextStream> 32 #include <QtCore/QtDebug> 34 #include <QProgressBar> 56 static int lastPercent = -1;
57 static int lastHash = 0;
58 int p =
static_cast<int>(vf + 0.5f);
59 int h =
static_cast<int>(vf * 0.78f + 0.5f);
61 if (p != lastPercent) {
63 if (lastPercent == -1) {
64 m_out <<
"\033[80D\033[K" 66 m_out.setFieldWidth(3);
68 m_out.setFieldWidth(0);
72 m_out <<
"\033[s\033[80D\033[37C";
73 m_out.setFieldWidth(3);
75 m_out.setFieldWidth(0);
80 for (; lastHash <
h; ++lastHash) {
82 if (lastHash < 36 || lastHash > 39) {
109 m_width = w * m_height /
h;
110 m_x = m_width * -0.667f;
112 m_image = QImage(w, h, QImage::Format_RGB32);
120 typedef std::complex<float_v>
Z;
129 return z.real() * z.real() + z.imag() * z.imag();
132 template<
typename T>
static inline T square(
T a) {
return a *
a; }
133 template<
typename T>
static inline T minOf(
T a,
T b) {
return a < b ? a : b; }
134 template<
typename T>
static inline T maxOf(
T a,
T b) {
return a < b ? b :
a; }
140 return value < min ? min :
value;
150 static const Pixel NULL_PIXEL = { 0, 0, 0 };
155 Canvas(
int h,
int w);
156 void addDot(
float x,
float y,
int red,
int green,
int blue);
157 void toQImage(QImage *);
160 void addDot(
int x,
int y,
float red,
float green,
float blue) {
161 Pixel &p = m_pixels[x + y * m_width];
167 std::vector<Pixel> m_pixels;
170 Canvas::Canvas(
int h,
int w)
171 : m_width(w), m_pixels(
h * w, NULL_PIXEL)
175 void Canvas::addDot(
float x,
float y,
int red,
int green,
int blue)
180 const int y2 =
static_cast<int>(
std::ceil (
y));
184 const float g = green;
185 const float b = blue;
186 const float frac11 = (1.f - xfrac) * (1.
f - yfrac);
187 const float frac12 = (1.f - xfrac) * yfrac;
188 const float frac21 = xfrac * (1.f - yfrac);
189 const float frac22 = xfrac * yfrac;
190 addDot(x1, y1, r * frac11, g * frac11, b * frac11);
191 addDot(x2, y1, r * frac21, g * frac21, b * frac21);
192 addDot(x1, y2, r * frac12, g * frac12, b * frac12);
193 addDot(x2, y2, r * frac22, g * frac22, b * frac22);
196 #define BUDDHABROT_USE_FUNCTION1 198 #ifdef BUDDHABROT_USE_FUNCTION2 222 const float h2 = h *
h;
223 const float h3 = h2 *
h;
224 const float m2 = m *
m;
225 const float m3 = m2 *
m;
226 const float denom = h * m *
square(m - h);
229 510.
f * h3 + 127.
f * m3 - 765.
f * h2 * m
231 765.
f * h * m2 - 255.
f * h3 - 254.
f * m3
233 255.
f * h2 + 127.
f * m2 - 510.
f * h * m)
236 #elif defined(BUDDHABROT_USE_FUNCTION1) 241 + 4.f / 255.f * h * h / m * x
245 + 255.f - 4.f * h + 4.f / 255.f *
square(h)
246 + x / m * (16.f * h - 1020.f - 12.f / 255.f *
square(h))
247 +
square(x / m) * (1020.f - 12.f * h + 8.f / 255.f *
square(h));
252 void Canvas::toQImage(QImage *img)
254 uchar *
line = img->scanLine(0);
255 const Pixel *p = &m_pixels[0];
256 #ifdef BUDDHABROT_USE_FUNCTION2 257 float max [3] = { 0.f, 0.f, 0.f };
258 std::vector<float> sorted[3];
259 for (
int i = 0; i < 3; ++i) {
260 sorted[i].reserve(m_pixels.size());
262 for (
unsigned int i = 0; i < m_pixels.size(); ++i) {
263 max[0] =
maxOf(max[0], m_pixels[i].red);
264 max[1] =
maxOf(max[1], m_pixels[i].green);
265 max[2] =
maxOf(max[2], m_pixels[i].blue);
266 if (m_pixels[i].red > 1.
f) {
267 sorted[0].push_back(m_pixels[i].red);
269 if (m_pixels[i].green > 1.
f) {
270 sorted[1].push_back(m_pixels[i].green);
272 if (m_pixels[i].blue > 1.
f) {
273 sorted[2].push_back(m_pixels[i].blue);
276 for (
int i = 0; i < 3; ++i) {
277 std::sort(sorted[i].begin(), sorted[i].end());
279 const float median[3] = {
280 sorted[0][sorted[0].size() / 2],
281 sorted[1][sorted[1].size() / 2],
282 sorted[2][sorted[2].size() / 2]
301 for (
int yy = 0; yy < img->height(); ++yy) {
302 for (
int xx = 0; xx < img->width(); ++xx) {
303 line[0] =
reduceRange(p->blue , max[2], median[2]);
304 line[1] =
reduceRange(p->green, max[1], median[1]);
310 #elif defined(BUDDHABROT_USE_FUNCTION1) 311 float max[3] = { 0.f, 0.f, 0.f };
312 for (
unsigned int i = 0; i < m_pixels.size(); ++i) {
313 max[0] =
maxOf(max[0], m_pixels[i].red);
314 max[1] =
maxOf(max[1], m_pixels[i].green);
315 max[2] =
maxOf(max[2], m_pixels[i].blue);
317 float h[3] = { 220.f, 220.f, 220.f };
335 for (
int yy = 0; yy < img->height(); ++yy) {
336 for (
int xx = 0; xx < img->width(); ++xx) {
345 float max [3] = { 0.f, 0.f, 0.f };
346 float mean [3] = { 0.f, 0.f, 0.f };
347 float stddev[3] = { 0.f, 0.f, 0.f };
348 for (
unsigned int i = 0; i < m_pixels.size(); ++i) {
349 max[0] =
maxOf(max[0], m_pixels[i].red);
350 max[1] =
maxOf(max[1], m_pixels[i].green);
351 max[2] =
maxOf(max[2], m_pixels[i].blue);
352 mean[0] += m_pixels[i].red;
353 mean[1] += m_pixels[i].green;
354 mean[2] += m_pixels[i].blue;
355 stddev[0] +=
square(m_pixels[i].red);
356 stddev[1] +=
square(m_pixels[i].green);
357 stddev[2] +=
square(m_pixels[i].blue);
359 const float normalization = 1.f / m_pixels.size();
360 mean[0] *= normalization;
361 mean[1] *= normalization;
362 mean[2] *= normalization;
366 qDebug() <<
" max:" << max[0] << max[1] << max[2];
367 qDebug() <<
" mean:" << mean[0] << mean[1] << mean[2];
368 qDebug() <<
"stddev:" << stddev[0] << stddev[1] << stddev[2];
377 const float center[3] = {
383 const float sdFactor[3] = { 2.f, 2.f, 2.f };
384 const float redFactor = center[0] / (sdFactor[0] * stddev[0]);
385 const float greenFactor = center[1] / (sdFactor[1] * stddev[1]);
386 const float blueFactor = center[2] / (sdFactor[2] * stddev[2]);
388 for (
int yy = 0; yy < img->height(); ++yy) {
389 for (
int xx = 0; xx < img->width(); ++xx) {
390 line[0] =
clamp(0, static_cast<int>(center[2] + (p->blue - mean[2]) * blueFactor ), 255);
391 line[1] =
clamp(0, static_cast<int>(center[1] + (p->green - mean[1]) * greenFactor), 255);
392 line[2] =
clamp(0, static_cast<int>(center[0] + (p->red - mean[0]) * redFactor ), 255);
410 steps[0] = steps[1] = -1;
415 const int iHeight = m_image.height();
416 const int iWidth = m_image.width();
420 const float nSteps[2] = {
421 static_cast<float>(m_opt.steps[0] == -1 ?
std::sqrt(iWidth) * iWidth : m_opt.steps[0]),
422 static_cast<float>(m_opt.steps[1] == -1 ?
std::sqrt(iHeight) * iHeight : m_opt.steps[1])
424 const int upperBound[3] = { m_opt.red[1], m_opt.green[1], m_opt.blue[1] };
425 const int lowerBound[3] = { m_opt.red[0], m_opt.green[0], m_opt.blue[0] };
426 int overallLowerBound = m_opt.it[0];
427 int maxIterations = m_opt.it[1];
428 float realMin = -2.102613f;
429 float realMax = 1.200613f;
431 float imagMax = 1.23971f;
438 const int overallUpperBound =
maxOf(upperBound[0],
maxOf(upperBound[1], upperBound[2]));
439 const float maxX =
static_cast<float>(iWidth ) - 1.
f;
440 const float maxY =
static_cast<float>(iHeight) - 1.
f;
441 const float xFact = iWidth / m_width;
442 const float yFact = iHeight / m_height;
443 const float realStep = (realMax - realMin) / nSteps[0];
444 const float imagStep = (imagMax - imagMin) / nSteps[1];
446 Canvas canvas(iHeight, iWidth);
448 for (
float real = realMin; real <= realMax; real += realStep) {
449 m_progress.setValue(99.
f * (real - realMin) / (realMax - realMin));
450 for (
float imag = imagMin; imag <= imagMax; imag += imagStep) {
452 Z c2 =
Z(1.08
f * real + 0.15
f, imag);
458 for (n = 0; n <= maxIterations &&
fastNorm(z) <
S; ++
n) {
461 if (n <= maxIterations && n >= overallLowerBound) {
467 for (
int i = 0; i <= overallUpperBound; ++i) {
468 const float y2 = (std::imag(z) - m_y) * yFact;
469 const float yn2 = (std::imag(zn) - m_y) * yFact;
470 if (y2 >= 0.
f && y2 < maxY && yn2 >= 0.
f && yn2 < maxY) {
471 const float x2 = (std::real(z) - m_x) * xFact;
472 if (x2 >= 0.
f && x2 < maxX) {
473 const int red = (i >= lowerBound[0] && i <= upperBound[0]) ? 1 : 0;
474 const int green = (i >= lowerBound[1] && i <= upperBound[1]) ? 1 : 0;
475 const int blue = (i >= lowerBound[2] && i <= upperBound[2]) ? 1 : 0;
476 canvas.addDot(x2, y2 , red, green, blue);
477 canvas.addDot(x2, yn2, red, green, blue);
492 for (
float real = realMin; real <= realMax; real += realStep) {
493 m_progress.setValue(99.
f * (real - realMin) / (realMax - realMin));
494 for (
float_v imag = imagMin2; imag <= imagMax; imag += imagStep2) {
504 while (!(inside && n <= maxIterations).isEmpty()) {
509 inside |= n < overallLowerBound;
510 if (inside.isFull()) {
516 for (
int i = 0; i <= overallUpperBound; ++i) {
517 const float_v y2 = (std::imag(z) - m_y) * yFact;
518 const float_v yn2 = (std::imag(zn) - m_y) * yFact;
519 const float_v x2 = (std::real(z) - m_x) * xFact;
522 const float_m drawMask = !inside && y2 >= 0.f && x2 >= 0.f && y2 < maxY && x2 < maxX && yn2 >= 0.f && yn2 < maxY;
524 const int red = (i >= lowerBound[0] && i <= upperBound[0]) ? 1 : 0;
525 const int green = (i >= lowerBound[1] && i <= upperBound[1]) ? 1 : 0;
526 const int blue = (i >= lowerBound[2] && i <= upperBound[2]) ? 1 : 0;
529 canvas.addDot(x2[j], y2 [j], red, green, blue);
530 canvas.addDot(x2[j], yn2[j], red, green, blue);
539 canvas.toQImage(&m_image);
543 qDebug() << timer.
Cycles() <<
"cycles";
545 if (m_filename.isEmpty()) {
546 m_filename = QString(
"r%1-%2_g%3-%4_b%5-%6_s%7-%8_i%9-%10_%11x%12.png")
547 .arg(lowerBound[0]).arg(upperBound[0])
548 .arg(lowerBound[1]).arg(upperBound[1])
549 .arg(lowerBound[2]).arg(upperBound[2])
550 .arg(nSteps[0]).arg(nSteps[1])
551 .arg(overallLowerBound).arg(maxIterations)
552 .arg(m_image.width()).arg(m_image.height());
555 m_image.save(m_filename);
558 static void usage(
const char *argv0)
562 QTextStream out(stdout);
563 out <<
"Usage: " << argv0 <<
" [options] [<filename>]\n\n" 565 <<
" -h|--help This message.\n" 566 <<
" -s|--size <w> <h> Specify the width and height of the resulting image file. [1024 768]\n" 567 <<
" -r|--red <int> <int> Specify lower and upper iteration bounds for a red trace. [" 568 << o.
red[0] <<
' ' << o.
red[1] <<
"]\n" 569 <<
" -g|--green <int> <int> Specify lower and upper iteration bounds for a green trace. [" 571 <<
" -b|--blue <int> <int> Specify lower and upper iteration bounds for a blue trace. [" 572 << o.
blue[0] <<
' ' << o.
blue[1] <<
"]\n" 573 <<
" --steps <int> <int> Specify the steps in real and imaginary direction. [width^1.5 height^1.5]\n" 574 <<
" --minIt <int> Overall lower iteration bound. [" << o.
it[0] <<
"]\n" 575 <<
" --maxIt <int> Overall upper iteration bound. [" << o.
it[1] <<
"]\n" 579 int main(
int argc,
char **argv)
581 QCoreApplication app(argc, argv);
582 const QStringList &args = QCoreApplication::arguments();
583 if (args.contains(
"--help") || args.contains(
"-h")) {
595 for (
int i = 1; i < args.size(); ++i) {
596 const QString &arg = args[i];
598 if (arg == QLatin1String(
"--red") || arg == QLatin1String(
"-r")) {
599 opt.
red[0] = args[++i].toInt(&ok);
601 opt.
red[1] = args[++i].toInt(&ok);
603 }
else if (arg == QLatin1String(
"--green") || arg == QLatin1String(
"-g")) {
604 opt.
green[0] = args[++i].toInt(&ok);
606 opt.
green[1] = args[++i].toInt(&ok);
608 }
else if (arg == QLatin1String(
"--blue") || arg == QLatin1String(
"-b")) {
609 opt.
blue[0] = args[++i].toInt(&ok);
611 opt.
blue[1] = args[++i].toInt(&ok);
613 }
else if (arg == QLatin1String(
"--steps")) {
614 opt.
steps[0] = args[++i].toInt(&ok);
616 opt.
steps[1] = args[++i].toInt(&ok);
618 }
else if (arg == QLatin1String(
"--minIt")) {
619 opt.
it[0] = args[++i].toInt(&ok);
620 }
else if (arg == QLatin1String(
"--maxIt")) {
621 opt.
it[1] = args[++i].toInt(&ok);
622 }
else if (arg == QLatin1String(
"--size") || arg == QLatin1String(
"-s")) {
623 width = args[++i].toInt(&ok);
625 height = args[++i].toInt(&ok);
628 static bool filenameSet =
false;
unsigned long long Cycles() const
static Vc_ALWAYS_INLINE int_v min(const int_v &x, const int_v &y)
void setSize(int w, int h)
static const char * filename()
static const double x2[5]
#define foreach_bit(_it_, _mask_)
static T clamp(T min, T value, T max)
static void usage(const char *argv0)
static unsigned int reduceRange(float x, float m, float h)
static const double x1[5]
int main(int argc, char **argv)
Main program.
static Z::value_type fastNorm(const Z &z)
static Vc_ALWAYS_INLINE int_v max(const int_v &x, const int_v &y)
std::complex< float_v > Z
void setOptions(Options o)
void setFilename(const QString &)