rockbox/apps/plugins/fractals/mandelbrot_set.c
Thomas Martitz a1842c04f9 lcd-24bit: Introduce a 24-bit mid-level LCD driver
With LCD driver all calculation will be performed on RGB888 and the hardware/OS
can display from our 24bit framebuffer.

It is not yet as performance optimized as the existing drivers but should be
good enough.The vast number of small changes is due to the fact that
fb_data can be a struct type now, while most of the code expected a scalar type.

lcd-as-memframe ASM code does not work with 24bit currently so the with 24bit
it enforces the generic C code.

All plugins are ported over. Except for rockpaint. It uses so much memory that
it wouldnt fit into the 512k plugin buffer anymore (patches welcome).

Change-Id: Ibb1964545028ce0d8ff9833ccc3ab66be3ee0754
2014-06-21 00:15:53 +02:00

415 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2004 Matthias Wientapper
* Heavily extended 2005 Jens Arnold
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "fractal_sets.h"
#include "mandelbrot_set.h"
#define BUTTON_YIELD_TIMEOUT (HZ / 4)
#ifdef USEGSLIB
static unsigned char imgbuffer[LCD_HEIGHT];
#else
static fb_data imgbuffer[LCD_HEIGHT];
#endif
#ifdef USEGSLIB
#define LCOLOR(iter) ((iter ^ 7) << 5)
#else
/*
* Spread iter's colors over color range.
* 345 (=15*26-45) is max_iter maximal value
* This implementation ignores pixel format, thus it is not uniformly spread
*/
#define LCOLOR(iter) ((iter << LCD_DEPTH) / 345)
#endif
#ifdef HAVE_LCD_COLOR
#define COLOR(iter) FB_SCALARPACK(LCOLOR(iter))
#define CONVERGENCE_COLOR FB_RGBPACK(0, 0, 0)
#else /* greyscale */
#define COLOR(iter) (unsigned char)LCOLOR(iter)
#define CONVERGENCE_COLOR 0
#endif
#if CONFIG_LCD == LCD_SSD1815
/* Recorder, Ondio: pixel_height == 1.25 * pixel_width */
#define MB_HEIGHT (LCD_HEIGHT*5/4)
#else
/* square pixels */
#define MB_HEIGHT LCD_HEIGHT
#endif
#define MB_XOFS (-0x03000000L) /* -0.75 (s5.26) */
#if (3000 * MB_HEIGHT / LCD_WIDTH) >= 2400 /* width is limiting factor */
#define MB_XFAC (0x06000000LL) /* 1.5 (s5.26) */
#define MB_YFAC (MB_XFAC*MB_HEIGHT / LCD_WIDTH)
#else /* height is limiting factor */
#define MB_YFAC (0x04cccccdLL) /* 1.2 (s5.26) */
#define MB_XFAC (MB_YFAC*LCD_WIDTH / MB_HEIGHT)
#endif
#ifndef USEGSLIB
#define UPDATE_FREQ (HZ/50)
#endif
/* fixed point format s5.26: sign, 5 bits integer part, 26 bits fractional part */
struct fractal_ops *ops;
long x_min;
long x_max;
long x_step;
long x_delta;
long y_min;
long y_max;
long y_step;
long y_delta;
int step_log2;
unsigned max_iter;
static void mandelbrot_init(void);
static int mandelbrot_calc_low_prec(struct fractal_rect *rect,
int (*button_yield_cb)(void *), void *button_yield_ctx);
static int mandelbrot_calc_high_prec(struct fractal_rect *rect,
int (*button_yield_cb)(void *), void *button_yield_ctx);
static void mandelbrot_move(int dx, int dy);
static int mandelbrot_zoom(int factor);
static int mandelbrot_precision(int d);
struct fractal_ops mandelbrot_ops =
{
.init = mandelbrot_init,
.calc = NULL,
.move = mandelbrot_move,
.zoom = mandelbrot_zoom,
.precision = mandelbrot_precision,
};
#define LOG2_OUT_OF_BOUNDS -32767
static int ilog2_fp(long value) /* calculate integer log2(value_fp_6.26) */
{
int i = 0;
if (value <= 0)
{
return LOG2_OUT_OF_BOUNDS;
}
else if (value > (1L << 26))
{
while (value >= (2L << 26))
{
value >>= 1;
i++;
}
}
else
{
while (value < (1L << 26))
{
value <<= 1;
i--;
}
}
return i;
}
static int recalc_parameters(void)
{
x_step = (x_max - x_min) / LCD_WIDTH;
y_step = (y_max - y_min) / LCD_HEIGHT;
step_log2 = ilog2_fp(MIN(x_step, y_step));
if (step_log2 == LOG2_OUT_OF_BOUNDS)
return 1; /* out of bounds */
x_delta = X_DELTA(x_step);
y_delta = Y_DELTA(y_step);
max_iter = MAX(15, -15 * step_log2 - 45);
ops->calc = (step_log2 <= -10) ?
mandelbrot_calc_high_prec : mandelbrot_calc_low_prec;
return 0;
}
static void mandelbrot_init(void)
{
ops = &mandelbrot_ops;
x_min = MB_XOFS - MB_XFAC;
x_max = MB_XOFS + MB_XFAC;
y_min = -MB_YFAC;
y_max = MB_YFAC;
recalc_parameters();
}
static int mandelbrot_calc_low_prec(struct fractal_rect *rect,
int (*button_yield_cb)(void *), void *button_yield_ctx)
{
#ifndef USEGSLIB
long next_update = *rb->current_tick;
int last_px = rect->px_min;
#endif
unsigned n_iter;
long a32, b32;
short x, x2, y, y2, a, b;
int p_x, p_y;
unsigned long last_yield = *rb->current_tick;
unsigned long last_button_yield = *rb->current_tick;
a32 = x_min + x_step * rect->px_min;
for (p_x = rect->px_min; p_x < rect->px_max; p_x++)
{
a = a32 >> 16;
b32 = y_min + y_step * (LCD_HEIGHT - rect->py_max);
for (p_y = rect->py_max - 1; p_y >= rect->py_min; p_y--)
{
b = b32 >> 16;
x = a;
y = b;
n_iter = 0;
while (++n_iter <= max_iter)
{
x2 = MULS16_ASR10(x, x);
y2 = MULS16_ASR10(y, y);
if (x2 + y2 > (4<<10)) break;
y = 2 * MULS16_ASR10(x, y) + b;
x = x2 - y2 + a;
}
if (n_iter > max_iter)
imgbuffer[p_y] = CONVERGENCE_COLOR;
else
imgbuffer[p_y] = COLOR(n_iter);
/* be nice to other threads:
* if at least one tick has passed, yield */
if (TIME_AFTER(*rb->current_tick, last_yield))
{
rb->yield();
last_yield = *rb->current_tick;
}
if (TIME_AFTER(*rb->current_tick, last_button_yield))
{
if (button_yield_cb(button_yield_ctx))
{
#ifndef USEGSLIB
/* update screen part that was changed since last yield */
rb->lcd_update_rect(last_px, rect->py_min,
p_x - last_px + 1, rect->py_max - rect->py_min);
#endif
rect->px_min = (p_x == 0) ? 0 : p_x - 1;
return 1;
}
last_button_yield = *rb->current_tick + BUTTON_YIELD_TIMEOUT;
}
b32 += y_step;
}
#ifdef USEGSLIB
grey_ub_gray_bitmap_part(imgbuffer, 0, rect->py_min, 1,
p_x, rect->py_min, 1, rect->py_max - rect->py_min);
#else
rb->lcd_bitmap_part(imgbuffer, 0, rect->py_min, 1,
p_x, rect->py_min, 1, rect->py_max - rect->py_min);
if ((p_x == rect->px_max - 1) ||
TIME_AFTER(*rb->current_tick, next_update))
{
next_update = *rb->current_tick + UPDATE_FREQ;
/* update screen part that was changed since last yield */
rb->lcd_update_rect(last_px, rect->py_min,
p_x - last_px + 1, rect->py_max - rect->py_min);
last_px = p_x;
}
#endif
a32 += x_step;
}
rect->valid = 0;
return 0;
}
static int mandelbrot_calc_high_prec(struct fractal_rect *rect,
int (*button_yield_cb)(void *), void *button_yield_ctx)
{
#ifndef USEGSLIB
long next_update = *rb->current_tick;
int last_px = rect->px_min;
#endif
unsigned n_iter;
long x, x2, y, y2, a, b;
int p_x, p_y;
unsigned long last_yield = *rb->current_tick;
unsigned long last_button_yield = *rb->current_tick;
MULS32_INIT();
a = x_min + x_step * rect->px_min;
for (p_x = rect->px_min; p_x < rect->px_max; p_x++)
{
b = y_min + y_step * (LCD_HEIGHT - rect->py_max);
for (p_y = rect->py_max - 1; p_y >= rect->py_min; p_y--)
{
x = a;
y = b;
n_iter = 0;
while (++n_iter <= max_iter)
{
x2 = MULS32_ASR26(x, x);
y2 = MULS32_ASR26(y, y);
if (x2 + y2 > (4L<<26)) break;
y = 2 * MULS32_ASR26(x, y) + b;
x = x2 - y2 + a;
}
if (n_iter > max_iter)
imgbuffer[p_y] = CONVERGENCE_COLOR;
else
imgbuffer[p_y] = COLOR(n_iter);
/* be nice to other threads:
* if at least one tick has passed, yield */
if (TIME_AFTER(*rb->current_tick, last_yield))
{
rb->yield();
last_yield = *rb->current_tick;
}
if (TIME_AFTER(*rb->current_tick, last_button_yield))
{
if (button_yield_cb(button_yield_ctx))
{
#ifndef USEGSLIB
/* update screen part that was changed since last yield */
rb->lcd_update_rect(last_px, rect->py_min,
p_x - last_px + 1, rect->py_max - rect->py_min);
#endif
rect->px_min = (p_x == 0) ? 0 : p_x - 1;
return 1;
}
last_button_yield = *rb->current_tick + BUTTON_YIELD_TIMEOUT;
}
b += y_step;
}
#ifdef USEGSLIB
grey_ub_gray_bitmap_part(imgbuffer, 0, rect->py_min, 1,
p_x, rect->py_min, 1, rect->py_max - rect->py_min);
#else
rb->lcd_bitmap_part(imgbuffer, 0, rect->py_min, 1,
p_x, rect->py_min, 1, rect->py_max - rect->py_min);
if ((p_x == rect->px_max - 1) ||
TIME_AFTER(*rb->current_tick, next_update))
{
next_update = *rb->current_tick + UPDATE_FREQ;
/* update screen part that was changed since last yield */
rb->lcd_update_rect(last_px, rect->py_min,
p_x - last_px + 1, rect->py_max - rect->py_min);
last_px = p_x;
}
#endif
a += x_step;
}
rect->valid = 0;
return 0;
}
static void mandelbrot_move(int x_factor, int y_factor)
{
long dx = (long)x_factor * x_delta;
long dy = (long)y_factor * y_delta;
x_min += dx;
x_max += dx;
y_min += dy;
y_max += dy;
}
static int mandelbrot_zoom(int factor)
{
int res;
long factor_x = (long)factor * x_delta;
long factor_y = (long)factor * y_delta;
x_min += factor_x;
x_max -= factor_x;
y_min += factor_y;
y_max -= factor_y;
res = recalc_parameters();
if (res) /* zoom not possible, revert */
{
mandelbrot_zoom(-factor);
}
return res;
}
static int mandelbrot_precision(int d)
{
int changed = 0;
/* Increase precision */
for (; d > 0; d--)
{
max_iter += max_iter / 2;
changed = 1;
}
/* Decrease precision */
for (; d < 0 && max_iter >= 15; d++)
{
max_iter -= max_iter / 3;
changed = 1;
}
return changed;
}