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) {
112 m_image = QImage(w, h, QImage::Format_RGB32);
120 typedef std::complex<float_v>
Z;
122 static inline Z
P(Z
z, Z
c)
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)
177 const int x1 =
static_cast<int>(
std::floor(x));
178 const int x2 =
static_cast<int>(
std::ceil (x));
179 const int y1 =
static_cast<int>(
std::floor(y));
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
199 static inline uchar
reduceRange(
float x,
float m,
float h)
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);
415 const int iHeight =
m_image.height();
416 const int iWidth =
m_image.width();
420 const float nSteps[2] = {
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) {
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) {
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);
543 qDebug() << timer.
Cycles() <<
"cycles";
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)
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;
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()
#define foreach_bit(_it_, _mask_)
unsigned long long Cycles() const
static T clamp(T min, T value, T max)
ProgressWriter m_progress
static void usage(const char *argv0)
static unsigned int reduceRange(float x, float m, float h)
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 &)