FS-7500: libpng 1.6.0 has much simpler APIs, also add a new switch_img_patch_png to possible patch a transparent png to an img

While this is not optimal, we should cache the png:
1) cache the whole buffer, with comes with RGBARGBA pixel formats
2) Allow switch_image_t to be other formats e.g. VPX_IMG_FMT_ARGB, VPX_IMG_FMT_ARGB_LE, or VPX_IMG_FMT_444A
   those can have alpha channels so we can check the alpha channel before we patch

Note all PNG are created equel, or maybe a bug in libpng since for some PNG files with alpha the returned buffer
     not seems like RGBARGBA... while docs says it should default be RGBA
This commit is contained in:
Seven Du 2015-02-15 13:49:43 +08:00 committed by Michael Jerris
parent f4fad4e756
commit e02ff26569
2 changed files with 142 additions and 0 deletions

View File

@ -210,6 +210,7 @@ SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_render(switch_img_txt_hand
SWITCH_DECLARE(void) switch_img_patch_hole(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_image_rect_t *rect);
SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name);
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name);
SWITCH_DECLARE(switch_status_t) switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name);
SWITCH_DECLARE(void) switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y);

View File

@ -722,6 +722,109 @@ SWITCH_DECLARE(void) switch_img_patch_hole(switch_image_t *IMG, switch_image_t *
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* available from libpng 1.6.0 */
SWITCH_DECLARE(switch_status_t) switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name)
{
png_image png = { 0 };
png_bytep buffer = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_rgb_color_t *rgb_color;
switch_yuv_color_t yuv_color;
uint8_t alpha;
int i, j;
png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&png, file_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
switch_goto_status(SWITCH_STATUS_FALSE, end);
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png format: %d, size: %dx%d\n", png.format, png.width, png.height);
// if (png.format | PNG_FORMAT_FLAG_ALPHA) {
// printf("Alpha\n");
// }
png.format = PNG_FORMAT_RGBA;
buffer = malloc(PNG_IMAGE_SIZE(png));
switch_assert(buffer);
if (!png_image_finish_read(&png, NULL/*background*/, buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
switch_goto_status(SWITCH_STATUS_FALSE, end);
}
for (i = 0; i < png.height; i++) {
for (j = 0; j < png.width; j++) {
alpha = buffer[i * png.width * 4 + j * 4 + 3];
// printf("%d, %d alpha: %d\n", j, i, alpha);
if (alpha) { // todo, mux alpha with the underlying pixel
rgb_color = (switch_rgb_color_t *)(buffer + i * png.width * 4 + j * 4);
switch_color_rgb2yuv(rgb_color, &yuv_color);
switch_img_draw_pixel(img, x + j, i, &yuv_color);
}
}
}
end:
png_image_free(&png);
switch_safe_free(buffer);
return status;
}
SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name)
{
png_image png = { 0 };
png_bytep buffer = NULL;
switch_image_t *img = NULL;
png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&png, file_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error open png: %s\n", file_name);
goto err;
}
png.format = PNG_FORMAT_RGB;
buffer = malloc(PNG_IMAGE_SIZE(png));
switch_assert(buffer);
if (!png_image_finish_read(&png, NULL/*background*/, buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read png: %s\n", file_name);
goto err;
}
if (png.width > SWITCH_IMG_MAX_WIDTH || png.height > SWITCH_IMG_MAX_HEIGHT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", png.width, png.height);
goto err;
}
img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, png.width, png.height, 1);
switch_assert(img);
RAWToI420(buffer, png.width * 3,
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
png.width, png.height);
err:
png_image_free(&png);
switch_safe_free(buffer);
return img;
}
#else /* libpng < 1.6.0 */
SWITCH_DECLARE(switch_status_t) switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not implemented on libpng < 1.6.0\n");
return SWITCH_STATUS_FALSE;
}
// ref: most are out-dated, man libpng :)
// http://zarb.org/~gc/html/libpng.html
// http://www.libpng.org/pub/png/book/toc.html
@ -958,6 +1061,42 @@ end:
return img;
}
#endif
#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED /* available from libpng 1.6.0 */
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name)
{
png_image png = { 0 };
png_bytep buffer = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
buffer = malloc(img->d_w * img->d_h * 3);
switch_assert(buffer);
I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
buffer, img->d_w * 3,
img->d_w, img->d_h);
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGB;
png.width = img->d_w;
png.height = img->d_h;
if (!png_image_write_to_file(&png, file_name, 0, buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error write PNG %s\n", file_name);
status = SWITCH_STATUS_FALSE;
}
switch_safe_free(buffer);
return status;
}
#else
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name)
{
int width, height;
@ -1063,6 +1202,8 @@ end:
return status;
}
#endif
SWITCH_DECLARE(switch_status_t) switch_img_fit(switch_image_t **srcP, int width, int height)
{
switch_image_t *src, *tmp = NULL;