new plugin: FS#10559 - lrcplayer: a plugin to view .lrc file.
[kugel-rb.git] / apps / plugins / fractals / mandelbrot_set.c
blob6e47527a201a99ca7a2c33174e8f0be356406e03
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Matthias Wientapper
11 * Heavily extended 2005 Jens Arnold
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "fractal_sets.h"
24 #include "mandelbrot_set.h"
26 #define BUTTON_YIELD_TIMEOUT (HZ / 4)
28 #ifdef USEGSLIB
29 static unsigned char imgbuffer[LCD_HEIGHT];
30 #else
31 static fb_data imgbuffer[LCD_HEIGHT];
32 #endif
34 #ifdef USEGSLIB
35 #define LCOLOR(iter) ((iter ^ 7) << 5)
36 #else
38 * Spread iter's colors over color range.
39 * 345 (=15*26-45) is max_iter maximal value
40 * This implementation ignores pixel format, thus it is not uniformly spread
42 #define LCOLOR(iter) ((iter << LCD_DEPTH) / 345)
43 #endif
45 #ifdef HAVE_LCD_COLOR
46 #define COLOR(iter) (fb_data)LCOLOR(iter)
47 #define CONVERGENCE_COLOR LCD_RGBPACK(0, 0, 0)
48 #else /* greyscale */
49 #define COLOR(iter) (unsigned char)LCOLOR(iter)
50 #define CONVERGENCE_COLOR 0
51 #endif
53 #if CONFIG_LCD == LCD_SSD1815
54 /* Recorder, Ondio: pixel_height == 1.25 * pixel_width */
55 #define MB_HEIGHT (LCD_HEIGHT*5/4)
56 #else
57 /* square pixels */
58 #define MB_HEIGHT LCD_HEIGHT
59 #endif
61 #define MB_XOFS (-0x03000000L) /* -0.75 (s5.26) */
62 #if (3000 * MB_HEIGHT / LCD_WIDTH) >= 2400 /* width is limiting factor */
63 #define MB_XFAC (0x06000000LL) /* 1.5 (s5.26) */
64 #define MB_YFAC (MB_XFAC*MB_HEIGHT / LCD_WIDTH)
65 #else /* height is limiting factor */
66 #define MB_YFAC (0x04cccccdLL) /* 1.2 (s5.26) */
67 #define MB_XFAC (MB_YFAC*LCD_WIDTH / MB_HEIGHT)
68 #endif
70 #ifndef USEGSLIB
71 #define UPDATE_FREQ (HZ/50)
72 #endif
74 /* fixed point format s5.26: sign, 5 bits integer part, 26 bits fractional part */
75 struct fractal_ops *ops;
76 long x_min;
77 long x_max;
78 long x_step;
79 long x_delta;
80 long y_min;
81 long y_max;
82 long y_step;
83 long y_delta;
84 int step_log2;
85 unsigned max_iter;
87 static void mandelbrot_init(void);
89 static int mandelbrot_calc_low_prec(struct fractal_rect *rect,
90 int (*button_yield_cb)(void *), void *button_yield_ctx);
92 static int mandelbrot_calc_high_prec(struct fractal_rect *rect,
93 int (*button_yield_cb)(void *), void *button_yield_ctx);
95 static void mandelbrot_move(int dx, int dy);
97 static int mandelbrot_zoom(int factor);
99 static int mandelbrot_precision(int d);
101 struct fractal_ops mandelbrot_ops =
103 .init = mandelbrot_init,
104 .calc = NULL,
105 .move = mandelbrot_move,
106 .zoom = mandelbrot_zoom,
107 .precision = mandelbrot_precision,
110 #define LOG2_OUT_OF_BOUNDS -32767
112 static int ilog2_fp(long value) /* calculate integer log2(value_fp_6.26) */
114 int i = 0;
116 if (value <= 0)
118 return LOG2_OUT_OF_BOUNDS;
120 else if (value > (1L << 26))
122 while (value >= (2L << 26))
124 value >>= 1;
125 i++;
128 else
130 while (value < (1L << 26))
132 value <<= 1;
133 i--;
136 return i;
139 static int recalc_parameters(void)
141 x_step = (x_max - x_min) / LCD_WIDTH;
142 y_step = (y_max - y_min) / LCD_HEIGHT;
143 step_log2 = ilog2_fp(MIN(x_step, y_step));
145 if (step_log2 == LOG2_OUT_OF_BOUNDS)
146 return 1; /* out of bounds */
148 x_delta = X_DELTA(x_step);
149 y_delta = Y_DELTA(y_step);
150 max_iter = MAX(15, -15 * step_log2 - 45);
152 ops->calc = (step_log2 <= -10) ?
153 mandelbrot_calc_high_prec : mandelbrot_calc_low_prec;
155 return 0;
158 static void mandelbrot_init(void)
160 ops = &mandelbrot_ops;
162 x_min = MB_XOFS - MB_XFAC;
163 x_max = MB_XOFS + MB_XFAC;
164 y_min = -MB_YFAC;
165 y_max = MB_YFAC;
167 recalc_parameters();
170 static int mandelbrot_calc_low_prec(struct fractal_rect *rect,
171 int (*button_yield_cb)(void *), void *button_yield_ctx)
173 #ifndef USEGSLIB
174 long next_update = *rb->current_tick;
175 int last_px = rect->px_min;
176 #endif
177 unsigned n_iter;
178 long a32, b32;
179 short x, x2, y, y2, a, b;
180 int p_x, p_y;
181 unsigned long last_yield = *rb->current_tick;
182 unsigned long last_button_yield = *rb->current_tick;
184 a32 = x_min + x_step * rect->px_min;
186 for (p_x = rect->px_min; p_x < rect->px_max; p_x++)
188 a = a32 >> 16;
190 b32 = y_min + y_step * (LCD_HEIGHT - rect->py_max);
192 for (p_y = rect->py_max - 1; p_y >= rect->py_min; p_y--)
194 b = b32 >> 16;
195 x = a;
196 y = b;
197 n_iter = 0;
199 while (++n_iter <= max_iter)
201 x2 = MULS16_ASR10(x, x);
202 y2 = MULS16_ASR10(y, y);
204 if (x2 + y2 > (4<<10)) break;
206 y = 2 * MULS16_ASR10(x, y) + b;
207 x = x2 - y2 + a;
210 if (n_iter > max_iter)
211 imgbuffer[p_y] = CONVERGENCE_COLOR;
212 else
213 imgbuffer[p_y] = COLOR(n_iter);
215 /* be nice to other threads:
216 * if at least one tick has passed, yield */
217 if (TIME_AFTER(*rb->current_tick, last_yield))
219 rb->yield();
220 last_yield = *rb->current_tick;
223 if (TIME_AFTER(*rb->current_tick, last_button_yield))
225 if (button_yield_cb(button_yield_ctx))
227 #ifndef USEGSLIB
228 /* update screen part that was changed since last yield */
229 rb->lcd_update_rect(last_px, rect->py_min,
230 p_x - last_px + 1, rect->py_max - rect->py_min);
231 #endif
232 rect->px_min = (p_x == 0) ? 0 : p_x - 1;
234 return 1;
237 last_button_yield = *rb->current_tick + BUTTON_YIELD_TIMEOUT;
240 b32 += y_step;
242 #ifdef USEGSLIB
243 grey_ub_gray_bitmap_part(imgbuffer, 0, rect->py_min, 1,
244 p_x, rect->py_min, 1, rect->py_max - rect->py_min);
245 #else
246 rb->lcd_bitmap_part(imgbuffer, 0, rect->py_min, 1,
247 p_x, rect->py_min, 1, rect->py_max - rect->py_min);
249 if ((p_x == rect->px_max - 1) ||
250 TIME_AFTER(*rb->current_tick, next_update))
252 next_update = *rb->current_tick + UPDATE_FREQ;
254 /* update screen part that was changed since last yield */
255 rb->lcd_update_rect(last_px, rect->py_min,
256 p_x - last_px + 1, rect->py_max - rect->py_min);
257 last_px = p_x;
259 #endif
261 a32 += x_step;
264 rect->valid = 0;
266 return 0;
269 static int mandelbrot_calc_high_prec(struct fractal_rect *rect,
270 int (*button_yield_cb)(void *), void *button_yield_ctx)
272 #ifndef USEGSLIB
273 long next_update = *rb->current_tick;
274 int last_px = rect->px_min;
275 #endif
276 unsigned n_iter;
277 long x, x2, y, y2, a, b;
278 int p_x, p_y;
279 unsigned long last_yield = *rb->current_tick;
280 unsigned long last_button_yield = *rb->current_tick;
282 MULS32_INIT();
284 a = x_min + x_step * rect->px_min;
286 for (p_x = rect->px_min; p_x < rect->px_max; p_x++)
288 b = y_min + y_step * (LCD_HEIGHT - rect->py_max);
290 for (p_y = rect->py_max - 1; p_y >= rect->py_min; p_y--)
292 x = a;
293 y = b;
294 n_iter = 0;
296 while (++n_iter <= max_iter)
298 x2 = MULS32_ASR26(x, x);
299 y2 = MULS32_ASR26(y, y);
301 if (x2 + y2 > (4L<<26)) break;
303 y = 2 * MULS32_ASR26(x, y) + b;
304 x = x2 - y2 + a;
307 if (n_iter > max_iter)
308 imgbuffer[p_y] = CONVERGENCE_COLOR;
309 else
310 imgbuffer[p_y] = COLOR(n_iter);
312 /* be nice to other threads:
313 * if at least one tick has passed, yield */
314 if (TIME_AFTER(*rb->current_tick, last_yield))
316 rb->yield();
317 last_yield = *rb->current_tick;
320 if (TIME_AFTER(*rb->current_tick, last_button_yield))
322 if (button_yield_cb(button_yield_ctx))
324 #ifndef USEGSLIB
325 /* update screen part that was changed since last yield */
326 rb->lcd_update_rect(last_px, rect->py_min,
327 p_x - last_px + 1, rect->py_max - rect->py_min);
328 #endif
329 rect->px_min = (p_x == 0) ? 0 : p_x - 1;
331 return 1;
334 last_button_yield = *rb->current_tick + BUTTON_YIELD_TIMEOUT;
337 b += y_step;
339 #ifdef USEGSLIB
340 grey_ub_gray_bitmap_part(imgbuffer, 0, rect->py_min, 1,
341 p_x, rect->py_min, 1, rect->py_max - rect->py_min);
342 #else
343 rb->lcd_bitmap_part(imgbuffer, 0, rect->py_min, 1,
344 p_x, rect->py_min, 1, rect->py_max - rect->py_min);
346 if ((p_x == rect->px_max - 1) ||
347 TIME_AFTER(*rb->current_tick, next_update))
349 next_update = *rb->current_tick + UPDATE_FREQ;
351 /* update screen part that was changed since last yield */
352 rb->lcd_update_rect(last_px, rect->py_min,
353 p_x - last_px + 1, rect->py_max - rect->py_min);
354 last_px = p_x;
356 #endif
357 a += x_step;
360 rect->valid = 0;
362 return 0;
365 static void mandelbrot_move(int x_factor, int y_factor)
367 long dx = (long)x_factor * x_delta;
368 long dy = (long)y_factor * y_delta;
370 x_min += dx;
371 x_max += dx;
372 y_min += dy;
373 y_max += dy;
376 static int mandelbrot_zoom(int factor)
378 int res;
379 long factor_x = (long)factor * x_delta;
380 long factor_y = (long)factor * y_delta;
382 x_min += factor_x;
383 x_max -= factor_x;
384 y_min += factor_y;
385 y_max -= factor_y;
387 res = recalc_parameters();
388 if (res) /* zoom not possible, revert */
390 mandelbrot_zoom(-factor);
393 return res;
396 static int mandelbrot_precision(int d)
398 int changed = 0;
400 /* Increase precision */
401 for (; d > 0; d--)
403 max_iter += max_iter / 2;
404 changed = 1;
407 /* Decrease precision */
408 for (; d < 0 && max_iter >= 15; d++)
410 max_iter -= max_iter / 3;
411 changed = 1;
414 return changed;