fix(gif): fix transparency and frame disposal regression (#9858)
@@ -134,6 +134,9 @@ void lv_gif_restart(lv_obj_t * obj)
|
||||
|
||||
GIF_reset(&gifobj->gif);
|
||||
gifobj->loop_count = -1; /* match the behavior of the old library */
|
||||
|
||||
lv_draw_buf_clear(gifobj->draw_buf, NULL);
|
||||
|
||||
lv_timer_resume(gifobj->timer);
|
||||
lv_timer_reset(gifobj->timer);
|
||||
|
||||
@@ -309,38 +312,12 @@ static inline void gif_blend_to_rgb565(GIFDRAW * pDraw, lv_draw_buf_t * draw_buf
|
||||
uint16_t * dst = (uint16_t *)((uint8_t *)draw_buf->data + ((pDraw->iY + pDraw->y) * draw_buf->header.stride + pDraw->iX
|
||||
* 2));
|
||||
|
||||
if(pDraw->ucHasTransparency) {
|
||||
if(pDraw->ucDisposalMethod == 2) {
|
||||
/* Disposal 2: Replace transparent pixels with background color */
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel == pDraw->ucTransparent) {
|
||||
pixel = pDraw->ucBackground;
|
||||
}
|
||||
*dst++ = pal[pixel];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Disposal 0,1,3: Replace transparent pixels with background color to maintain position
|
||||
* The gif_disposal_last_frame function handles the actual background clearing
|
||||
*/
|
||||
uint16_t bg_color = pal[pDraw->ucBackground];
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel == pDraw->ucTransparent) {
|
||||
*dst++ = bg_color;
|
||||
}
|
||||
else {
|
||||
*dst++ = pal[pixel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
*dst++ = pal[pixel];
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(!pDraw->ucHasTransparency || pixel != pDraw->ucTransparent) {
|
||||
*dst = pal[pixel];
|
||||
}
|
||||
dst++;
|
||||
}
|
||||
|
||||
LV_PROFILER_DECODER_END;
|
||||
@@ -356,49 +333,16 @@ static inline void gif_blend_to_rgb888(GIFDRAW * pDraw, lv_draw_buf_t * draw_buf
|
||||
uint8_t * pal = pDraw->pPalette24;
|
||||
uint8_t * dst = (uint8_t *)draw_buf->data + ((pDraw->iY + pDraw->y) * draw_buf->header.stride + pDraw->iX * 3);
|
||||
|
||||
if(pDraw->ucHasTransparency) {
|
||||
if(pDraw->ucDisposalMethod == 2) {
|
||||
/* Disposal 2: Replace transparent pixels with background color */
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel == pDraw->ucTransparent) {
|
||||
pixel = pDraw->ucBackground;
|
||||
}
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
dst += 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Disposal 0,1,3: Replace transparent pixels with background color to maintain position */
|
||||
uint8_t bg_r = pal[(pDraw->ucBackground * 3) + 2];
|
||||
uint8_t bg_g = pal[(pDraw->ucBackground * 3) + 1];
|
||||
uint8_t bg_b = pal[(pDraw->ucBackground * 3) + 0];
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel == pDraw->ucTransparent) {
|
||||
dst[0] = bg_r;
|
||||
dst[1] = bg_g;
|
||||
dst[2] = bg_b;
|
||||
}
|
||||
else {
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
}
|
||||
dst += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(!pDraw->ucHasTransparency || pixel != pDraw->ucTransparent) {
|
||||
/* The memory layout of gif files store the channels as RGB, while lv_color_t stores BGR,
|
||||
* so the byte reversal is intentional. */
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
dst += 3;
|
||||
}
|
||||
dst += 3;
|
||||
}
|
||||
|
||||
LV_PROFILER_DECODER_END;
|
||||
@@ -414,49 +358,17 @@ static inline void gif_blend_to_argb8888(GIFDRAW * pDraw, lv_draw_buf_t * draw_b
|
||||
uint8_t * pal = pDraw->pPalette24;
|
||||
uint8_t * dst = (uint8_t *)draw_buf->data + ((pDraw->iY + pDraw->y) * draw_buf->header.stride + pDraw->iX * 4);
|
||||
|
||||
if(pDraw->ucHasTransparency) {
|
||||
if(pDraw->ucDisposalMethod == 2) {
|
||||
/* Disposal 2: Transparent pixels get alpha=0, opaque pixels get alpha=255 */
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel != pDraw->ucTransparent) {
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
dst[3] = 0xFF;
|
||||
}
|
||||
else {
|
||||
dst[3] = 0x00;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Disposal 0,1,3: Transparent pixels get alpha=0, opaque pixels get alpha=255 */
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(pixel != pDraw->ucTransparent) {
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
dst[3] = 0xFF;
|
||||
}
|
||||
else {
|
||||
dst[3] = 0x00;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
while(src < end) {
|
||||
pixel = *src++;
|
||||
if(!pDraw->ucHasTransparency || pixel != pDraw->ucTransparent) {
|
||||
/* The memory layout of gif files store the channels as RGB, while lv_color_t stores BGR,
|
||||
* so the byte reversal is intentional. */
|
||||
dst[0] = pal[(pixel * 3) + 2];
|
||||
dst[1] = pal[(pixel * 3) + 1];
|
||||
dst[2] = pal[(pixel * 3) + 0];
|
||||
dst[3] = 0xFF;
|
||||
dst += 4;
|
||||
}
|
||||
dst += 4;
|
||||
}
|
||||
|
||||
LV_PROFILER_DECODER_END;
|
||||
@@ -563,6 +475,8 @@ static void gif_initialize(lv_gif_t * gifobj)
|
||||
return;
|
||||
}
|
||||
|
||||
lv_draw_buf_clear(gifobj->draw_buf, NULL);
|
||||
|
||||
lv_image_set_src((lv_obj_t *) gifobj, gifobj->draw_buf);
|
||||
|
||||
gifobj->loop_count = GIF_getLoopCount(&gifobj->gif);
|
||||
@@ -618,27 +532,45 @@ static void gif_disposal_last_frame(GIFIMAGE * gif, lv_draw_buf_t * drawbuf)
|
||||
case GIF_PALETTE_RGB565_LE:
|
||||
case GIF_PALETTE_RGB565_BE: {
|
||||
unsigned short * palette16 = (unsigned short *)palette;
|
||||
uint16_t color = palette16[bg];
|
||||
|
||||
/* This check is equivalent to whether the gif has defined a transparent color or not. */
|
||||
if(gif->ucGIFBits & 1) color = 0;
|
||||
|
||||
for(i = y; i < y + h; i++) {
|
||||
uint8_t * dst = drawbuf->data + drawbuf->header.stride * i;
|
||||
for(j = x; j < x + w; j++) {
|
||||
*(uint16_t *)(dst + 2 * j) = palette16[bg];
|
||||
*(uint16_t *)(dst + 2 * j) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GIF_PALETTE_RGB888:
|
||||
for(i = y; i < y + h; i++) {
|
||||
uint8_t * dst = drawbuf->data + drawbuf->header.stride * i;
|
||||
for(j = x; j < x + w; j++) {
|
||||
dst[3 * j] = palette[(bg * 3) + 2];
|
||||
dst[3 * j + 1] = palette[(bg * 3) + 1];
|
||||
dst[3 * j + 2] = palette[(bg * 3) + 0];
|
||||
case GIF_PALETTE_RGB888: {
|
||||
uint8_t r = palette[(bg * 3) + 2];
|
||||
uint8_t g = palette[(bg * 3) + 1];
|
||||
uint8_t b = palette[(bg * 3) + 0];
|
||||
|
||||
/* This check is equivalent to whether the gif has defined a transparent color or not. */
|
||||
if(gif->ucGIFBits & 1) {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
}
|
||||
|
||||
for(i = y; i < y + h; i++) {
|
||||
uint8_t * dst = drawbuf->data + drawbuf->header.stride * i;
|
||||
for(j = x; j < x + w; j++) {
|
||||
dst[3 * j] = r;
|
||||
dst[3 * j + 1] = g;
|
||||
dst[3 * j + 2] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GIF_PALETTE_RGB8888: {
|
||||
lv_color32_t bg_color = lv_color32_make(palette[(bg * 3) + 2], palette[(bg * 3) + 1], palette[(bg * 3)], 0xff);
|
||||
/* has transparent */
|
||||
|
||||
/* This check is equivalent to whether the gif has defined a transparent color or not. */
|
||||
if(gif->ucGIFBits & 1) {
|
||||
bg_color = lv_color32_make(0, 0, 0, 0);
|
||||
}
|
||||
@@ -655,8 +587,11 @@ static void gif_disposal_last_frame(GIFIMAGE * gif, lv_draw_buf_t * drawbuf)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* disposal_method 0 and 1: do nothing, leave existing content */
|
||||
/* disposal_method 3: not supported, do nothing */
|
||||
else if(disposal_method == 3) {
|
||||
LV_LOG_WARN("GIF disposal method 3 not supported");
|
||||
}
|
||||
/* disposal_method 0 and 1: do nothing, leave existing content */
|
||||
|
||||
LV_PROFILER_DECODER_END;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -142,6 +142,19 @@ void test_gif_pause_and_resume(void)
|
||||
TEST_ASSERT_EQUAL(10, lv_gif_get_current_frame_index(gif));
|
||||
}
|
||||
|
||||
static void wait_for_frame(lv_obj_t * gif, int32_t target_frame)
|
||||
{
|
||||
uint32_t max_iterations = 100;
|
||||
uint32_t i = 0;
|
||||
|
||||
while(lv_gif_get_current_frame_index(gif) < target_frame) {
|
||||
lv_test_wait(10);
|
||||
TEST_ASSERT_MESSAGE(++i < max_iterations, "Timeout waiting for GIF frame");
|
||||
}
|
||||
|
||||
lv_gif_pause(gif);
|
||||
}
|
||||
|
||||
void test_gif_blend_to_xrgb8888(void)
|
||||
{
|
||||
lv_obj_t * gif = create_gif_image("A:src/test_assets/totoro_transparent.gif");
|
||||
@@ -149,6 +162,10 @@ void test_gif_blend_to_xrgb8888(void)
|
||||
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_XRGB8888);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_xrgb8888.png");
|
||||
|
||||
wait_for_frame(gif, 5);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_xrgb8888_frame_5.png");
|
||||
}
|
||||
|
||||
void test_gif_blend_to_argb8888(void)
|
||||
@@ -159,6 +176,10 @@ void test_gif_blend_to_argb8888(void)
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_argb8888.png");
|
||||
|
||||
wait_for_frame(gif, 5);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_argb8888_frame_5.png");
|
||||
|
||||
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
|
||||
|
||||
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_ARGB8888);
|
||||
@@ -180,6 +201,10 @@ void test_gif_blend_to_rgb565(void)
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb565.png");
|
||||
|
||||
wait_for_frame(gif, 5);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb565_frame_5.png");
|
||||
|
||||
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
|
||||
|
||||
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
|
||||
@@ -201,6 +226,10 @@ void test_gif_blend_to_rgb888(void)
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb888.png");
|
||||
|
||||
wait_for_frame(gif, 5);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("libs/gif_rgb888_frame_5.png");
|
||||
|
||||
gif = create_gif_image("A:src/test_assets/totoro_no_transparency.gif");
|
||||
|
||||
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB888);
|
||||
|
||||