juanjqh / lvgl_eve_gpu_test-main

MIT License
1 stars 2 forks source link

flash animation feature #4

Open capricorn-one opened 6 months ago

capricorn-one commented 6 months ago

Hey again! Sorry for spamming your inbox, maybe I should just email directly, let me know.

Anyway wanted to share this with you, finally got it working last night with a very hacky workaround, but I'm able to use animations stored in external flash connected to the EVE display with this trick.

Basically I create an image with the necessary information about the animation frames in the data buffer for the fake image. Then I create an image the standard way so I don't have to mess with any of the lvgl core code. I simply add an extra function to your lv_draw_eve_image function to check if it's an animation image, then run the routine below.

The coolest part... now you can use all the proper layering and coloring and opacity tools built into lvgl and it all works without having to use a lvgl animation. In the video below I'm applying color changes to a gif that is drawing a new frame on every update cycle. With lvgl button overlayed on top! Honestly been waiting to use LVGL and EVE this way for a long time but it just wasn't possible. So cool!

IMG_4579

vs. original gif FYI: startup_animation

capricorn-one commented 6 months ago

Here's the current function FYI, still modifying it so I can use the proper lvgl coloring methods. Also need to find a way to track the current frame a little better, but hopefully you can consider this for the next version as well. And like I said, happy to help!

static void draw_eve_flash_animation(lv_draw_eve_unit_t * draw_unit, lv_image_dsc_t * dsc, const lv_area_t * coords) {

    static u_int16_t current_frame = 0;

    uint32_t aoptr = dsc->data[7] << 24 | dsc->data[6] << 16 | dsc->data[5] << 8 | dsc->data[4];
    uint16_t num_frames = dsc->data[9] << 8 | dsc->data[8];
    uint8_t loop = dsc->data[10];

    lv_color_t new_color = lv_color_make(0xFF, 0x44, current_frame*2);

    eve_scissor(draw_unit->base_unit.clip_area->x1, draw_unit->base_unit.clip_area->y1, draw_unit->base_unit.clip_area->x2, draw_unit->base_unit.clip_area->y2);

    eve_save_context();

    // if(draw_dsc->recolor_opa > LV_OPA_MIN) {
        eve_color_opa(75);
        eve_color(new_color);
        // eve_color(draw_dsc->recolor);
    // }

    // put it here
    EVE_cmd_dl_burst(VERTEX_FORMAT(4));

    EVE_cmd_animframe_burst(draw_unit->base_unit.clip_area->x1 + dsc->header.w / 2, draw_unit->base_unit.clip_area->y1 + dsc->header.h / 2, aoptr, current_frame++);

    eve_restore_context();
    EVE_end_cmd_burst();
    EVE_execute_cmd();

    EVE_start_cmd_burst();

    if(current_frame >= num_frames) {
        if(loop) {
            current_frame = 0;
        }
        else {
            current_frame = num_frames - 1;
        }
    }
}

And the first line of your lv_draw_eve_image function looks like this, to check if it's an animation:

  if(img_src[0] == 0xDE && img_src[1] == 0xAD && img_src[2] == 0xBE && img_src[3] == 0xEF) {
      draw_eve_flash_animation(draw_unit, img_dsc, coords);
      return;
  }

the image file then just looks like this:

#define ANIM_OBJ_ADDR     (4681088) // address of "startup_animation.anim.object" from *.map after generating Flash
#define NUM_FRAMES        (62)
#define LOOP             (1)

const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_STARTUP_ANIMATION uint8_t startup_animation_map[] = {
    0xDE, 0xAD, 0xBE, 0xEF,
    ANIM_OBJ_ADDR & 0xFF, (ANIM_OBJ_ADDR >> 8) & 0xFF, (ANIM_OBJ_ADDR >> 16) & 0xFF, (ANIM_OBJ_ADDR >> 24) & 0xFF,
    NUM_FRAMES & 0xFF, (NUM_FRAMES >> 8) & 0xFF,
    LOOP & 0xFF,
     0xFF
};

const lv_image_dsc_t startup_animation = {
  .header.cf = LV_COLOR_FORMAT_RGB565,
  .header.magic = LV_IMAGE_HEADER_MAGIC,
  .header.w = 282,
  .header.h = 282,
  .data_size = 12,
  .data = startup_animation_map,
};

I know this is hack AF, and theoretically you could have an image file that started with 0xDEADBEEF, but just wanted to get it working for now.

juanjqh commented 6 months ago

Wow, great job. I like it!! Another descriptive field for animations could be added to lv_image_dsc_t , I think.

capricorn-one commented 6 months ago

Yah, that's definitely where it's needed... I think maybe an alternative would be to use the lv_animimg... functionality somehow. That's probably the better approach to keep it a little more consistent, but either way some of the lvgl code will have to be modified to identify the source as being different than the current options. Probably an added enum somewhere followed by a case to handle it.

In the meantime, what I ended up doing to make things a little more lvgl typical (and solve an issue with the frames being updated in a weird way based on when the drawing engine was called), I used the pivot variable of the descriptor to store the frame number and num_frames variable. That way, I can create an animation from LVGL the proper way and also control the speed and loop position using the lv_anim methods.

Here's how it's looking now, really starting to be pretty powerful IMO. The kind of graphics I feel like you would expect to see from a MUCH more capable device. As I'm sure you know, with the previous method of writing to the RAMG buffer directly, the tearing would have looked horrible trying to accomplish something like this and there would be nowhere close to the response of the display. Even the load screen animation properly moves the gif one screen out of view while the other moves in. Super smooth.

IMG_4585