diff --git a/apps/recorder/bmp.c b/apps/recorder/bmp.c index d6f61d1c87..819d2e6976 100644 --- a/apps/recorder/bmp.c +++ b/apps/recorder/bmp.c @@ -173,6 +173,7 @@ static inline void set_rgb_union(struct uint8_rgb *dst, union rgb_union src) dst->red = src.red; dst->green = src.green; dst->blue = src.blue; + dst->alpha = 0xff; } struct bmp_args { @@ -189,6 +190,9 @@ struct bmp_args { int cur_col; struct img_part part; #endif + /* as read_part_line() goes through the rows it'll set this to true + * if it finds transparency. Initialize to false before calling */ + bool alpha_detected; }; static unsigned int read_part_line(struct bmp_args *ba) @@ -233,6 +237,7 @@ static unsigned int read_part_line(struct bmp_args *ba) #endif return 0; } + while (ibuf < ba->buf + (BM_MAX_WIDTH << 2)) { switch (depth) @@ -274,6 +279,7 @@ static unsigned int read_part_line(struct bmp_args *ba) component = data & 0xf8; component |= component >> 5; buf->red = component; + buf->alpha = 0xff; buf++; ibuf += 2; break; @@ -282,13 +288,12 @@ static unsigned int read_part_line(struct bmp_args *ba) buf->blue = *ibuf++; buf->green = *ibuf++; buf->red = *ibuf++; - if (depth == 32) - ibuf++; + buf->alpha = (depth == 32) ? *ibuf++ : 0xff; + if (buf->alpha != 0xff) ba->alpha_detected = true; buf++; break; } } - #if !defined(HAVE_LCD_COLOR) && \ ((LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) || \ defined(PLUGIN)) @@ -422,41 +427,40 @@ void output_row_8_native(uint32_t row, void * row_in, } #endif /* LCD_PIXELFORMAT */ #elif LCD_DEPTH == 16 -#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE - /* M:Robe 500 */ - (void) fb_width; - fb_data *dest = (fb_data *)ctx->bm->data + row; + /* iriver h300, colour iPods, X5 */ + (void)fb_width; + fb_data *dest = STRIDE_MAIN((fb_data *)ctx->bm->data + fb_width * row, + (fb_data *)ctx->bm->data + row); int delta = 127; - unsigned r, g, b; + unsigned r, g, b; + /* setup alpha channel buffer */ + unsigned char *bm_alpha = NULL; + if (ctx->bm->alpha_offset > 0) + bm_alpha = ctx->bm->data + ctx->bm->alpha_offset; + if (bm_alpha) + bm_alpha += ctx->bm->width*row/2; + for (col = 0; col < ctx->bm->width; col++) { if (ctx->dither) delta = DITHERXDY(col,dy); r = qp->red; g = qp->green; - b = (qp++)->blue; + b = qp->blue; r = (31 * r + (r >> 3) + delta) >> 8; g = (63 * g + (g >> 2) + delta) >> 8; b = (31 * b + (b >> 3) + delta) >> 8; *dest = LCD_RGBPACK_LCD(r, g, b); - dest += ctx->bm->height; + dest += STRIDE_MAIN(1, ctx->bm->height); + if (bm_alpha) { + /* pack alpha channel for 2 pixels into 1 byte */ + unsigned alpha = 255-qp->alpha; + if (col%2) + *bm_alpha++ |= alpha&0xf0; + else + *bm_alpha = alpha>>4; + } + qp++; } -#else - /* iriver h300, colour iPods, X5 */ - fb_data *dest = (fb_data *)ctx->bm->data + fb_width * row; - int delta = 127; - unsigned r, g, b; - for (col = 0; col < ctx->bm->width; col++) { - if (ctx->dither) - delta = DITHERXDY(col,dy); - r = qp->red; - g = qp->green; - b = (qp++)->blue; - r = (31 * r + (r >> 3) + delta) >> 8; - g = (63 * g + (g >> 2) + delta) >> 8; - b = (31 * b + (b >> 3) + delta) >> 8; - *dest++ = LCD_RGBPACK_LCD(r, g, b); - } -#endif #endif /* LCD_DEPTH */ } #endif @@ -480,6 +484,7 @@ int read_bmp_fd(int fd, int depth, numcolors, compression, totalsize; int ret; bool return_size = format & FORMAT_RETURN_SIZE; + bool read_alpha = format & FORMAT_TRANSPARENT; unsigned char *bitmap = bm->data; struct uint8_rgb palette[256]; @@ -608,8 +613,14 @@ int read_bmp_fd(int fd, if (cformat) totalsize = cformat->get_size(bm); - else + else { totalsize = BM_SIZE(bm->width,bm->height,format,remote); +#ifdef HAVE_REMOTE_LCD + if (!remote) +#endif + if (depth == 32 && read_alpha) /* account for possible 4bit alpha per pixel */ + totalsize += bm->width * bm->height / 2; + } if(return_size) { @@ -704,13 +715,21 @@ int read_bmp_fd(int fd, memset(bitmap, 0, totalsize); +#ifdef HAVE_LCD_COLOR + if (read_alpha && depth == 32) + bm->alpha_offset = totalsize - (bm->width * bm->height / 2); + else + bm->alpha_offset = 0; +#endif + struct bmp_args ba = { .fd = fd, .padded_width = padded_width, .read_width = read_width, .width = src_dim.width, .depth = depth, .palette = palette, #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ defined(HAVE_BMP_SCALING) || defined(PLUGIN) - .cur_row = 0, .cur_col = 0, .part = {0,0} + .cur_row = 0, .cur_col = 0, .part = {0,0}, #endif + .alpha_detected = false, }; #if (LCD_DEPTH > 1 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)) && \ @@ -759,7 +778,6 @@ int read_bmp_fd(int fd, return -6; } #endif - int row; /* loop to read rows and put them to buffer */ for (row = rset.rowstart; row != rset.rowstop; row += rset.rowstep) { @@ -858,5 +876,14 @@ int read_bmp_fd(int fd, } #endif } +#ifdef HAVE_LCD_COLOR + if (!ba.alpha_detected) + { /* if this has an alpha channel, totalsize accounts for it as well + * subtract if no actual alpha information was found */ + if (bm->alpha_offset > 0) + totalsize -= bm->width*bm->height/2; + bm->alpha_offset = 0; + } +#endif return totalsize; /* return the used buffer size. */ } diff --git a/apps/recorder/bmp.h b/apps/recorder/bmp.h index 31bd0730bd..3ddc3b66df 100644 --- a/apps/recorder/bmp.h +++ b/apps/recorder/bmp.h @@ -39,6 +39,7 @@ struct uint8_rgb { uint8_t blue; uint8_t green; uint8_t red; + uint8_t alpha; }; struct dim { diff --git a/firmware/drivers/lcd-16bit-common.c b/firmware/drivers/lcd-16bit-common.c index bbac2b295a..7b37bf7c30 100644 --- a/firmware/drivers/lcd-16bit-common.c +++ b/firmware/drivers/lcd-16bit-common.c @@ -229,7 +229,6 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height); } -/* draw alpha bitmap for anti-alias font */ #define ALPHA_COLOR_FONT_DEPTH 2 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH) #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1) @@ -285,11 +284,17 @@ static inline unsigned blend_color(unsigned c, unsigned a) return blend_two_colors(c, current_vp->fg_pattern, a); } -void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, - int src_y, int stride, int x, int y, - int width, int height) +/* Blend an image with an alpha channel + * if image is NULL, drawing will happen according to the drawmode + * src is the alpha channel (4bit per pixel) */ +static void ICODE_ATTR lcd_alpha_bitmap_part_mix(const fb_data* image, + const unsigned char *src, int src_x, + int src_y, int x, int y, + int width, int height, + int stride_image, int stride_src) { fb_data *dst, *dst_row; + const fb_data *image_row; unsigned dmask = 0x00000000; int drmode = current_vp->drawmode; /* nothing to draw? */ @@ -356,13 +361,22 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, { dmask = ~dmask; } + /* sourcing from an image ignore drawmode. + * Set to DRMODE_BG as we use its code path in the switch below */ + if (image != NULL) + { + dmask = 0; + drmode = DRMODE_BG; + } dst_row = LCDADDR(x, y); int col, row = height; unsigned data, pixels; - unsigned skip_end = (stride - width); - unsigned skip_start = src_y * stride + src_x; + unsigned skip_end = (stride_src - width); + unsigned skip_start = src_y * stride_src + src_x; + unsigned skip_start_image = STRIDE_MAIN(src_y * stride_image + src_x, + src_x * stride_image + src_y); #ifdef ALPHA_BITMAP_READ_WORDS uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3); @@ -379,6 +393,9 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, #ifdef ALPHA_BITMAP_READ_WORDS pixels = 8 - pixels; #endif + if (image) + image += skip_start_image; + image_row = image; /* go through the rows and update each pixel */ do @@ -386,6 +403,12 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, col = width; dst = dst_row; dst_row += ROW_INC; + if (image_row) { + image = image_row; + image_row += STRIDE_MAIN(stride_image,1); + } + else + image = dst; #ifdef ALPHA_BITMAP_READ_WORDS #define UPDATE_SRC_ALPHA do { \ if (--pixels) \ @@ -431,10 +454,11 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, uintptr_t bo = lcd_backdrop_offset; do { - *dst = blend_two_colors(*dst, *(fb_data *)((uintptr_t)dst + bo), - data & ALPHA_COLOR_LOOKUP_SIZE ); + *dst = blend_two_colors(*(fb_data *)((uintptr_t)dst + bo), + *image, data & ALPHA_COLOR_LOOKUP_SIZE ); dst += COL_INC; + image += STRIDE_MAIN(1, stride_image); UPDATE_SRC_ALPHA; } while (--col); @@ -443,9 +467,10 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, { do { - *dst = blend_two_colors(*dst, current_vp->bg_pattern, - data & ALPHA_COLOR_LOOKUP_SIZE ); + *dst = blend_two_colors(current_vp->bg_pattern, + *image, data & ALPHA_COLOR_LOOKUP_SIZE ); dst += COL_INC; + image += STRIDE_MAIN(1, stride_image); UPDATE_SRC_ALPHA; } while (--col); @@ -516,6 +541,15 @@ void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, } while (--row); } + +/* draw alpha bitmap for anti-alias font */ +void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, + int src_y, int stride, int x, int y, + int width, int height) +{ + lcd_alpha_bitmap_part_mix(NULL, src, src_x, src_y, x, y, width, height, 0, stride); +} + /* Draw a partial bitmap (mono or native) including alpha channel */ void ICODE_ATTR lcd_bmp_part(const struct bitmap* bm, int src_x, int src_y, int x, int y, int width, int height) @@ -523,6 +557,10 @@ void ICODE_ATTR lcd_bmp_part(const struct bitmap* bm, int src_x, int src_y, int bitmap_stride = STRIDE_MAIN(bm->width, bm->height); if (bm->format == FORMAT_MONO) lcd_mono_bitmap_part(bm->data, src_x, src_y, bitmap_stride, x, y, width, height); + else if (bm->alpha_offset > 0) + lcd_alpha_bitmap_part_mix((fb_data*)bm->data, bm->data+bm->alpha_offset, + src_x, src_y, x, y, width, height, + bitmap_stride, bm->width); else lcd_bitmap_transparent_part((fb_data*)bm->data, src_x, src_y, bitmap_stride, x, y, width, height); diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index 47ea94bca9..ad579820ad 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h @@ -467,6 +467,9 @@ struct bitmap { #if (LCD_DEPTH > 1) || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1) int format; unsigned char *maskdata; +#endif +#ifdef HAVE_LCD_COLOR + int alpha_offset; /* byte-offset of alpha channel in data */ #endif unsigned char *data; };