diff options
| author | Dien-Nhung Nguyen <kein@kienlab.com> | 2024-08-27 20:08:24 +0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-27 15:08:24 +0200 |
| commit | c95faf32a6a98975828717d596ff51dedeecdf56 (patch) | |
| tree | 8fe52862e0c34bfe82509adf0c98a8e57056d4c7 /src/animation.c | |
| parent | f5874d607315ff88b27414299089b8528b5bb07c (diff) | |
Add animations (#15)
* animation: add xbm animations
* refactor: correct framebuffer terminology
* animation: add animations and effect
* animation: timing with TMOS scheduler
Diffstat (limited to 'src/animation.c')
| -rw-r--r-- | src/animation.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/animation.c b/src/animation.c new file mode 100644 index 0000000..53eff58 --- /dev/null +++ b/src/animation.c @@ -0,0 +1,464 @@ +#include <stdlib.h> + +#include "xbm.h" +#include "leddrv.h" +#include "bmlist.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; +} + +void ani_scroll_x(bm_t *bm, uint16_t *fb, int dir) +{ + int x = mod(bm->anim_step, bm->width + LED_COLS) - 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; + } +} + +void ani_scroll_left(bm_t *bm, uint16_t *fb) +{ + ani_scroll_x(bm, fb, 0); +} + +void ani_scroll_right(bm_t *bm, uint16_t *fb) +{ + 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 frame = mod(bm->anim_step, frame_steps*frames)/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); + } +} + +void ani_scroll_up(bm_t *bm, uint16_t *fb) +{ + ani_scroll_y(bm, fb); + bm->anim_step++; +} + +void ani_scroll_down(bm_t *bm, uint16_t *fb) +{ + ani_scroll_y(bm, fb); + bm->anim_step--; +} + +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; + } +} + +void 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 frame = mod(bm->anim_step, frame_steps*frames)/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); +} + +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; + } +} + +void 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 frame = mod(bm->anim_step, frame_steps*frames)/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); + } +} + +void ani_animation(bm_t *bm, uint16_t *fb) +{ + int frame_steps = ANI_ANIMATION_STEPS; + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + bm->anim_step++; + + still(bm, fb, frame); +} + +void ani_fixed(bm_t *bm, uint16_t *fb) +{ + int frame_steps = ANI_FIXED_STEPS; + int frames = ALIGN(bm->width, LED_COLS) / LED_COLS; + int frame = mod(bm->anim_step, frame_steps*frames)/frame_steps; + + bm->anim_step++; + still(bm, fb, frame); +} + +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 > LED_COLS/2) { + 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 >= LED_COLS) + return; + fb[hc + i - 1] = -1; + fb[hc - i] = -1; + + for (i++; i< LED_COLS; i++) { + fb[hc + i - 1] = 0; + fb[hc - i] = 0; + } +} + +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 >= LED_COLS) + return; + fb[hc + i - 1] = -1; + fb[hc - i] = -1; +} + +void 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 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; + } + picture(bm, fb, bm->anim_step, frame); +} + +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); +}
\ No newline at end of file |