* fix: crash in picture animation * ble: add next gen profile * force read header from flash anyway * fix: bmlist: mem leak * feat: add reset option after legacy transfers
492 lines
10 KiB
C
492 lines
10 KiB
C
#include <stdlib.h>
|
|
|
|
#include "xbm.h"
|
|
#include "leddrv.h"
|
|
#include "bmlist.h"
|
|
#include "debug.h"
|
|
|
|
#define ANI_ANIMATION_STEPS (5) // steps
|
|
#define ANI_FIXED_STEPS (LED_COLS) // steps
|
|
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define ALIGN(x, range) (((x % (range))>0)*(range) + (x / (range)) * (range))
|
|
|
|
// Shift left on positive and right on negative n
|
|
#define SIGNED_SHIFT(reg, n) ((n) >= 0) ? (reg) >> (n) : (reg) << abs(n)
|
|
|
|
int mod(int a, int b)
|
|
{
|
|
int r = a % b;
|
|
return r < 0 ? r + b : r;
|
|
}
|
|
|
|
void fb_fill(uint16_t *fb, uint16_t v)
|
|
{
|
|
for (int i = 0; i < LED_COLS; i++) {
|
|
fb[i] = v;
|
|
}
|
|
}
|
|
|
|
int ani_xbm_next_frame(xbm_t *xbm, uint16_t *fb, int col, int row)
|
|
{
|
|
static int i;
|
|
|
|
xbm_t frame;
|
|
if (extract_frame(xbm, &frame, i) == NULL) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
xbm2fb(&frame, fb, col, row);
|
|
|
|
return ++i;
|
|
}
|
|
|
|
int ani_xbm_scroll_up(xbm_t *xbm, int vh,
|
|
uint16_t *fb, int col, int row)
|
|
{
|
|
static int i;
|
|
|
|
xbm_t frame;
|
|
if (xbm_croph(xbm, &frame, i, i + vh) == NULL) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
xbm2fb(&frame, fb, col, row);
|
|
|
|
return ++i;
|
|
}
|
|
|
|
static int scroll_pt_next(xbm_t *xbm, int vh, int pt,
|
|
uint16_t *fb, int col, int row)
|
|
{
|
|
static int i;
|
|
xbm_t frame;
|
|
|
|
if (i >= pt) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
if (xbm_croph(xbm, &frame, 0, i + (vh -pt)) == NULL) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
xbm2fb(&frame, fb, col, row +pt -i);
|
|
|
|
return ++i;
|
|
}
|
|
|
|
static int scroll_pb_next( xbm_t *xbm, int vh, int pb,
|
|
uint16_t *fb, int col, int row)
|
|
{
|
|
static int i;
|
|
xbm_t frame;
|
|
|
|
if (i >= pb) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
if (xbm_croph(xbm, &frame, xbm->h - (vh -1 -i), xbm->h) == NULL) {
|
|
i = 0;
|
|
return i;
|
|
}
|
|
xbm2fb(&frame, fb, col, row);
|
|
|
|
return ++i;
|
|
}
|
|
|
|
int ani_xbm_scrollup_pad( xbm_t *xbm, int vh, int pt, int pb,
|
|
uint16_t *fb, int col, int row)
|
|
{
|
|
static int i;
|
|
|
|
if (i == 0)
|
|
i = 1;
|
|
if (i == 1 && scroll_pt_next(xbm, vh, pt, fb, col, row) == 0) {
|
|
i = 2;
|
|
}
|
|
if (i == 2 && ani_xbm_scroll_up(xbm, vh, fb, col, row) == 0) {
|
|
i = 3;
|
|
}
|
|
if (i == 3 && scroll_pb_next(xbm, vh, pb, fb, col, row) == 0) {
|
|
i = 0;
|
|
}
|
|
return i;
|
|
}
|
|
/**
|
|
* Infinite scroll up
|
|
*/
|
|
int ani_xbm_scrollup_inf(xbm_t *xbm, uint16_t *fb,
|
|
int vh, int col, int row)
|
|
{
|
|
static int i, r;
|
|
|
|
xbm_t frame, frame_circle;
|
|
if (xbm_croph(xbm, &frame, i, i +vh) == NULL) {
|
|
|
|
xbm_croph(xbm, &frame, i +r, i -1 +vh);
|
|
xbm_croph(xbm, &frame_circle, 0, r);
|
|
|
|
xbm2fb(&frame, fb, col, row);
|
|
xbm2fb_dirty(&frame_circle, fb, col, row +vh -r);
|
|
|
|
r++;
|
|
if (r >= vh) {
|
|
i = 0;
|
|
r = 0;
|
|
}
|
|
return i;
|
|
}
|
|
xbm2fb(&frame, fb, col, row);
|
|
|
|
return ++i;
|
|
}
|
|
|
|
int ani_scroll_x(bm_t *bm, uint16_t *fb, int dir)
|
|
{
|
|
int total_steps = bm->width + LED_COLS;
|
|
int step = mod(bm->anim_step, total_steps);
|
|
int x = step - LED_COLS;
|
|
bm->anim_step += (dir) ? -1 : 1;
|
|
|
|
for (int i = 0; i < LED_COLS; i++) {
|
|
if (i + x >= bm->width) {
|
|
fb[i] = 0;
|
|
continue;
|
|
}
|
|
fb[i] = (i + x) >= 0 ? bm->buf[i + x] : 0;
|
|
}
|
|
return mod(bm->anim_step, total_steps); // How many steps left until restart again
|
|
}
|
|
|
|
int ani_scroll_left(bm_t *bm, uint16_t *fb)
|
|
{
|
|
return ani_scroll_x(bm, fb, 0);
|
|
}
|
|
|
|
int ani_scroll_right(bm_t *bm, uint16_t *fb)
|
|
{
|
|
return ani_scroll_x(bm, fb, 1);
|
|
}
|
|
|
|
void ani_shift_y(bm_t *bm, uint16_t *fb, int y, int frame)
|
|
{
|
|
frame *= LED_COLS;
|
|
|
|
int size = MIN(LED_COLS, bm->width);
|
|
int i = 0;
|
|
for (; i < size; i++) {
|
|
if ((frame + i) >= bm->width)
|
|
break;
|
|
fb[i] = SIGNED_SHIFT(bm->buf[frame + i], y);
|
|
}
|
|
for (; i < LED_COLS; i++)
|
|
fb[i] = 0;
|
|
}
|
|
|
|
void ani_scroll_y(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int frame_steps = LED_ROWS * 3; // in-still-out
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
int frame = mod(bm->anim_step, total_steps)/frame_steps;
|
|
|
|
int y = mod(bm->anim_step, frame_steps);
|
|
|
|
if (y < LED_ROWS) { // Scrolling up (in)
|
|
ani_shift_y(bm, fb, y - LED_ROWS, frame);
|
|
} else if (y < LED_ROWS * 2) { // Stay still
|
|
ani_shift_y(bm, fb, 0, frame);
|
|
} else { // Scrolling up (out)
|
|
ani_shift_y(bm, fb, y - LED_ROWS * 2, frame);
|
|
}
|
|
}
|
|
|
|
int ani_scroll_up(bm_t *bm, uint16_t *fb)
|
|
{
|
|
ani_scroll_y(bm, fb);
|
|
|
|
int frame_steps = LED_ROWS * 3; // in-still-out
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
bm->anim_step++;
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
int ani_scroll_down(bm_t *bm, uint16_t *fb)
|
|
{
|
|
ani_scroll_y(bm, fb);
|
|
|
|
int frame_steps = LED_ROWS * 3; // in-still-out
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
bm->anim_step--;
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
static void laser_in(bm_t *bm, uint16_t *fb, int step, int frame)
|
|
{
|
|
int c = mod(step, LED_COLS);
|
|
frame *= LED_COLS;
|
|
|
|
int i = 0;
|
|
for (; i < c; i++) {
|
|
fb[i] = (frame + i < bm->width) ? bm->buf[frame + i] : 0;
|
|
}
|
|
for (; i < MIN(LED_COLS, bm->width - frame); i++) {
|
|
fb[i] = bm->buf[frame + c];
|
|
}
|
|
for (; i < LED_COLS; i++) {
|
|
fb[i] = (frame + c < bm->width) ? bm->buf[frame + c] : 0;
|
|
}
|
|
}
|
|
|
|
static void laser_out(bm_t *bm, uint16_t *fb, int step, int frame)
|
|
{
|
|
int c = mod(step, LED_COLS);
|
|
frame *= LED_COLS;
|
|
|
|
int i = 0;
|
|
for (; i < c; i++) {
|
|
fb[i] = (frame + c < bm->width) ? bm->buf[frame + c] : 0;
|
|
}
|
|
for (; i < MIN(LED_COLS, bm->width - frame); i++) {
|
|
fb[i] = bm->buf[frame + i];
|
|
}
|
|
for (; i < LED_COLS; i++) {
|
|
fb[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void still(bm_t *bm, uint16_t *fb, int frame)
|
|
{
|
|
int i = frame * LED_COLS;
|
|
int j = 0;
|
|
for (; j < LED_COLS; j++) {
|
|
if (i >= bm->width)
|
|
break;
|
|
fb[j] = bm->buf[i];
|
|
i++;
|
|
}
|
|
for (; j< LED_COLS; j++) {
|
|
fb[j] = 0;
|
|
}
|
|
}
|
|
|
|
int ani_laser(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int frame_steps = LED_COLS * 3; // in-still-out
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
int frame = mod(bm->anim_step, total_steps)/frame_steps;
|
|
|
|
int c = mod(bm->anim_step, frame_steps);
|
|
bm->anim_step++;
|
|
|
|
if (c < LED_COLS)
|
|
laser_in(bm, fb, c - LED_COLS, frame);
|
|
else if (c < LED_COLS * 2)
|
|
still(bm, fb, frame);
|
|
else
|
|
laser_out(bm, fb, c - LED_COLS * 2, frame);
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
static uint32_t b16dialate(uint16_t w, int from, int to)
|
|
{
|
|
uint32_t ret = 0;
|
|
int i = 0, j = from;
|
|
for (; i < from; i++) {
|
|
ret |= (w & (1 << i));
|
|
}
|
|
for (; i < to; i++, j += 2) {
|
|
ret |= ((w & (1 << i)) >> i) << j;
|
|
}
|
|
for (; i < 16; i++, j++) {
|
|
ret |= ((w & (1 << i)) >> i) << j;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void snowflake_in(bm_t *bm, uint16_t *fb, int step, int frame)
|
|
{
|
|
int y = mod(step, LED_ROWS*2) - LED_ROWS;
|
|
frame *= LED_COLS;
|
|
|
|
int size = MIN(LED_COLS, bm->width - frame);
|
|
int i = 0;
|
|
for (; i < size; i++) {
|
|
if (y < 0) {
|
|
fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS),
|
|
LED_ROWS - y);
|
|
} else
|
|
fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS - y),
|
|
(LED_ROWS - y));
|
|
}
|
|
for (; i < LED_COLS; i++) {
|
|
fb[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void snowflake_out(bm_t *bm, uint16_t *fb, int step, int frame)
|
|
{
|
|
int y = mod(step, LED_ROWS*2) - LED_ROWS;
|
|
frame *= LED_COLS;
|
|
|
|
int size = MIN(LED_COLS, bm->width - frame);
|
|
int i = 0;
|
|
for (; i < size; i++) {
|
|
if (y <= 0) {
|
|
fb[i] = SIGNED_SHIFT(b16dialate(bm->buf[frame + i], 0, LED_ROWS),
|
|
y);
|
|
} else {
|
|
fb[i] = b16dialate(bm->buf[frame + i], y, LED_ROWS);
|
|
}
|
|
}
|
|
for (; i < LED_COLS; i++) {
|
|
fb[i] = 0;
|
|
}
|
|
}
|
|
|
|
int ani_snowflake(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int frame_steps = LED_ROWS * 6; // in-still-out, each costs 2xLED_ROWS step
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
int frame = mod(bm->anim_step, total_steps)/frame_steps;
|
|
|
|
int c = mod(bm->anim_step, frame_steps);
|
|
bm->anim_step++;
|
|
|
|
if (c < LED_ROWS * 2) {
|
|
snowflake_in(bm, fb, c - LED_ROWS * 2, frame);
|
|
} else if (c <= LED_ROWS * 4) {
|
|
still(bm, fb, frame);
|
|
} else {
|
|
snowflake_out(bm, fb, -(c - LED_ROWS * 4), frame);
|
|
}
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
int ani_animation(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int frame_steps = ANI_ANIMATION_STEPS;
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
int frame = mod(bm->anim_step, total_steps)/frame_steps;
|
|
|
|
bm->anim_step++;
|
|
|
|
still(bm, fb, frame);
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
int ani_fixed(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int frame_steps = ANI_FIXED_STEPS;
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS;
|
|
int total_steps = frame_steps * frames;
|
|
int frame = mod(bm->anim_step, total_steps)/frame_steps;
|
|
|
|
bm->anim_step++;
|
|
still(bm, fb, frame);
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
static void picture(bm_t *bm, uint16_t *fb, int step, int frame)
|
|
{
|
|
int hc = LED_COLS / 2;
|
|
int range = mod(step - 1, LED_COLS);
|
|
|
|
if (range > hc) {
|
|
still(bm, fb, frame);
|
|
return;
|
|
}
|
|
|
|
frame *= LED_COLS;
|
|
int i = 0;
|
|
for (; i <= range; i++) {
|
|
fb[hc + i - 1] = (hc + i - 1 + frame >= bm->width) ?
|
|
0 : bm->buf[hc + i - 1 + frame];
|
|
fb[hc - i] = (hc - i + frame >= bm->width) ?
|
|
0 : bm->buf[hc - i + frame];
|
|
}
|
|
|
|
if (i >= hc)
|
|
return;
|
|
fb[hc + i - 1] = -1;
|
|
fb[hc - i] = -1;
|
|
}
|
|
|
|
static void picture_out(bm_t *bm, uint16_t *fb, int step)
|
|
{
|
|
int hc = LED_COLS / 2;
|
|
|
|
if (step > hc)
|
|
return;
|
|
|
|
int range = step;
|
|
int i = 0;
|
|
for (; i <= range; i++) {
|
|
fb[hc + i - 1] = 0;
|
|
fb[hc - i] = 0;
|
|
}
|
|
if (i >= hc)
|
|
return;
|
|
fb[hc + i - 1] = -1;
|
|
fb[hc - i] = -1;
|
|
}
|
|
|
|
int ani_picture(bm_t *bm, uint16_t *fb)
|
|
{
|
|
int last_steps = LED_COLS / 2;
|
|
int frame_steps = LED_COLS;
|
|
int frames = ALIGN(bm->width, LED_COLS) / LED_COLS + 1;
|
|
int total_steps = frame_steps * frames - last_steps;
|
|
int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps;
|
|
bm->anim_step++;
|
|
|
|
if (frame == frames - 1) {
|
|
picture_out(bm, fb, mod(bm->anim_step, LED_COLS) );
|
|
|
|
/* picture_out() costs only half LED_COLS */
|
|
if (mod(bm->anim_step, LED_COLS) >= last_steps) {
|
|
bm->anim_step = 0;
|
|
return 0;
|
|
}
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
picture(bm, fb, bm->anim_step, frame);
|
|
|
|
return mod(bm->anim_step, total_steps);
|
|
}
|
|
|
|
void ani_marque(bm_t *bm, uint16_t *fb, int step)
|
|
{
|
|
int tpl = 0b000100010001;
|
|
|
|
int i;
|
|
for (i = 0 ; i < LED_COLS - 1; i++) {
|
|
fb[i] = fb[i] & ~((1 << (LED_ROWS - 1)) | 1);
|
|
|
|
fb[i] |= !!(tpl & (1 << ((step + i) % 4)) );
|
|
fb[i] |= !!(tpl & (1 << mod(-step + i - 2, 4))) << (LED_ROWS - 1);
|
|
}
|
|
|
|
fb[0] = tpl << (step % 4);
|
|
fb[LED_COLS - 1] = tpl >> ((step + 3) % 4);
|
|
|
|
}
|
|
|
|
void ani_flash(bm_t *bm, uint16_t *fb, int step)
|
|
{
|
|
if (!(step % 2))
|
|
fb_fill(fb, 0);
|
|
} |