blob: 02a48528e3c94e0ebc1c7472890df8f1dda5aa41 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2022
* All rights reserved
*
* This file is part of GPAC / Media Tools sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/internal/media_dev.h>
#include <gpac/internal/isomedia_dev.h>
#include <gpac/constants.h>
#ifdef GPAC_HAS_PNG
#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__)
#pragma comment(lib, "libpng")
#endif
/*include png.h before setjmp.h, otherwise we get compilation errors*/
#include <png.h>
#endif /*GPAC_HAS_PNG*/
#ifdef GPAC_HAS_JPEG
#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__)
#pragma comment(lib, "libjpeg")
#endif
#include <jpeglib.h>
#include <setjmp.h>
#endif /*GPAC_HAS_JPEG*/
GF_EXPORT
void gf_img_parse(GF_BitStream *bs, u32 *codecid, u32 *width, u32 *height, u8 **dsi, u32 *dsi_len)
{
u8 b1, b2, b3;
u32 size, type;
u64 pos;
pos = gf_bs_get_position(bs);
gf_bs_seek(bs, 0);
*width = *height = 0;
*codecid = 0;
if (dsi) {
*dsi = NULL;
*dsi_len = 0;
}
b1 = gf_bs_read_u8(bs);
b2 = gf_bs_read_u8(bs);
b3 = gf_bs_read_u8(bs);
/*JPEG*/
if ((b1==0xFF) && (b2==0xD8) && (b3==0xFF)) {
u32 offset = 0;
u32 Xdens, Ydens, nb_comp;
gf_bs_read_u8(bs);
gf_bs_skip_bytes(bs, 10); /*2 size, 5 JFIF\0, 2 version, 1 units*/
Xdens = gf_bs_read_int(bs, 16);
Ydens = gf_bs_read_int(bs, 16);
nb_comp = 0;
/*get frame header FFC0*/
while (gf_bs_available(bs)) {
u32 w, h,length;
if (gf_bs_read_u8(bs) != 0xFF) continue;
if (!offset) offset = (u32)gf_bs_get_position(bs) - 1;
type = gf_bs_read_u8(bs);
/*process all Start of Image markers*/
switch (type) {
case 0xC0:
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC5:
case 0xC6:
case 0xC7:
case 0xC9:
case 0xCA:
case 0xCB:
case 0xCD:
case 0xCE:
case 0xCF:
length = gf_bs_read_u16(bs);
/*prec = */gf_bs_read_u8(bs);
h = gf_bs_read_int(bs, 16);
w = gf_bs_read_int(bs, 16);
nb_comp = gf_bs_read_int(bs, 8);
if (length != 8 + 3*nb_comp) continue; //This is not the right marker
if ((w > *width) || (h > *height)) {
*width = w;
*height = h;
}
break;
}
}
*codecid = GF_CODECID_JPEG;
if (dsi) {
GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_u16(bs_dsi, offset);
gf_bs_write_u16(bs_dsi, Xdens);
gf_bs_write_u16(bs_dsi, Ydens);
gf_bs_write_u8(bs_dsi, nb_comp);
gf_bs_get_content(bs_dsi, dsi, dsi_len);
gf_bs_del(bs_dsi);
}
}
/*PNG*/
else if ((b1==0x89) && (b2==0x50) && (b3==0x4E)) {
/*check for PNG sig*/
if ( (gf_bs_read_u8(bs) != 0x47) || (gf_bs_read_u8(bs) != 0x0D) || (gf_bs_read_u8(bs) != 0x0A)
|| (gf_bs_read_u8(bs) != 0x1A) || (gf_bs_read_u8(bs) != 0x0A) ) goto exit;
gf_bs_read_u32(bs);
/*check for PNG IHDR*/
if ( (gf_bs_read_u8(bs) != 'I') || (gf_bs_read_u8(bs) != 'H')
|| (gf_bs_read_u8(bs) != 'D') || (gf_bs_read_u8(bs) != 'R')) goto exit;
*width = gf_bs_read_u32(bs);
*height = gf_bs_read_u32(bs);
*codecid = GF_CODECID_PNG;
}
/* try jpeg200 */
else {
u32 jp2h_size=0, jp2h_start=0;
size = gf_bs_read_u8(bs);
type = gf_bs_read_u32(bs);
/* try jp2 isomedia */
if ( ((size==12) && (type==GF_ISOM_BOX_TYPE_JP ))
|| (type==GF_ISOM_BOX_TYPE_JP2H ) ) {
if (type==GF_ISOM_BOX_TYPE_JP2H) {
*codecid = GF_CODECID_J2K;
goto j2k_restart;
}
type = gf_bs_read_u32(bs);
if (type!=0x0D0A870A) goto exit;
*codecid = GF_CODECID_J2K;
while (gf_bs_available(bs)) {
j2k_restart:
size = gf_bs_read_u32(bs);
type = gf_bs_read_u32(bs);
switch (type) {
case GF_ISOM_BOX_TYPE_JP2H:
jp2h_size=size-8;
jp2h_start = (u32) gf_bs_get_position(bs);
goto j2k_restart;
case GF_ISOM_BOX_TYPE_IHDR:
{
*height = gf_bs_read_u32(bs);
*width = gf_bs_read_u32(bs);
/*nb_comp = gf_bs_read_u16(bs);
BPC = gf_bs_read_u8(bs);
C = gf_bs_read_u8(bs);
UnkC = gf_bs_read_u8(bs);
IPR = gf_bs_read_u8(bs);
*/
if (dsi && jp2h_size) {
*dsi = gf_malloc(sizeof(char)*jp2h_size);
gf_bs_seek(bs, jp2h_start);
gf_bs_read_data(bs, *dsi, jp2h_size);
*dsi_len = jp2h_size;
}
goto exit;
}
break;
default:
gf_bs_skip_bytes(bs, size-8);
break;
}
}
}
/* try j2k codestream */
else {
gf_bs_seek(bs, pos);
type = gf_bs_read_u32(bs);
/* Start-of-codestream (SOC) + Image size (SIZ) magic number */
if (type == 0xFF4FFF51 ) {
/* u32 Lsiz = */gf_bs_read_u16(bs);
/* u32 Rsiz = */gf_bs_read_u16(bs);
*width = gf_bs_read_u32(bs);
*height = gf_bs_read_u32(bs);
*codecid = GF_CODECID_J2K;
}
}
}
exit:
gf_bs_seek(bs, pos);
}
#ifdef GPAC_HAS_JPEG
void gf_jpeg_nonfatal_error2(j_common_ptr cinfo, int lev)
{
}
/*JPG context while decoding*/
typedef struct
{
struct jpeg_error_mgr pub;
jmp_buf jmpbuf;
} JPGErr;
typedef struct
{
/*io manager*/
struct jpeg_source_mgr src;
s32 skip;
struct jpeg_decompress_struct cinfo;
} JPGCtx;
static void gf_jpeg_output_message (j_common_ptr cinfo)
{
if (cinfo) {
char buffer[JMSG_LENGTH_MAX];
/* Create the message */
(*cinfo->err->format_message) (cinfo, buffer);
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[JPEG OUTPUT MESSAGE]: %s\n", buffer));
}
}
static void gf_jpeg_fatal_error(j_common_ptr cinfo)
{
if (cinfo) {
JPGErr *err = (JPGErr *) cinfo->err;
gf_jpeg_output_message(cinfo);
longjmp(err->jmpbuf, 1);
}
}
void gf_jpeg_stub(j_decompress_ptr cinfo) {}
/*a JPEG is always carried in a complete, single MPEG4 AU so no refill*/
static boolean gf_jpeg_fill_input_buffer(j_decompress_ptr cinfo)
{
return 0;
}
static void gf_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
if (cinfo) {
JPGCtx *jpx = (JPGCtx *) cinfo->src;
if (num_bytes > (long) jpx->src.bytes_in_buffer) {
jpx->skip = (s32) (num_bytes - jpx->src.bytes_in_buffer);
jpx->src.next_input_byte += jpx->src.bytes_in_buffer;
jpx->src.bytes_in_buffer = 0;
} else {
jpx->src.bytes_in_buffer -= num_bytes;
jpx->src.next_input_byte += num_bytes;
jpx->skip = 0;
}
}
}
#define JPEG_MAX_SCAN_BLOCK_HEIGHT 16
GF_EXPORT
GF_Err gf_img_jpeg_dec(u8 *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, u8 *dst, u32 *dst_size, u32 dst_nb_comp)
{
s32 i, j, scans, k;
u32 stride;
char *scan_line, *ptr, *tmp;
char *lines[JPEG_MAX_SCAN_BLOCK_HEIGHT];
JPGErr jper;
JPGCtx jpx;
#ifdef GPAC_ENABLE_COVERAGE
if (gf_sys_is_cov_mode()) {
gf_jpeg_fatal_error(NULL);
gf_jpeg_output_message(NULL);
gf_jpeg_nonfatal_error2(NULL, 0);
gf_jpeg_fill_input_buffer(NULL);
gf_jpeg_skip_input_data(NULL, 0);
}
#endif
if (!dst) *dst_size = 0;
jpx.cinfo.err = jpeg_std_error(&(jper.pub));
jper.pub.error_exit = gf_jpeg_fatal_error;
jper.pub.output_message = gf_jpeg_output_message;
jper.pub.emit_message = gf_jpeg_nonfatal_error2;
if (setjmp(jper.jmpbuf)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[JPEGDecode] : Failed to decode\n"));
jpeg_destroy_decompress(&jpx.cinfo);
return GF_IO_ERR;
}
/*create decompress struct*/
jpeg_create_decompress(&jpx.cinfo);
/*prepare IO*/
jpx.src.init_source = gf_jpeg_stub;
jpx.src.fill_input_buffer = gf_jpeg_fill_input_buffer;
jpx.src.skip_input_data = gf_jpeg_skip_input_data;
jpx.src.resync_to_restart = jpeg_resync_to_restart;
jpx.src.term_source = gf_jpeg_stub;
jpx.skip = 0;
jpx.src.next_input_byte = (JOCTET *) jpg;
jpx.src.bytes_in_buffer = jpg_size;
jpx.cinfo.src = (void *) &jpx.src;
/*read header*/
do {
i = jpeg_read_header(&jpx.cinfo, TRUE);
} while (i == JPEG_HEADER_TABLES_ONLY);
/*we're supposed to have the full image in the buffer, wrong stream*/
if (i == JPEG_SUSPENDED) {
jpeg_destroy_decompress(&jpx.cinfo);
return GF_NON_COMPLIANT_BITSTREAM;
}
*width = jpx.cinfo.image_width;
*height = jpx.cinfo.image_height;
stride = *width * jpx.cinfo.num_components;
switch (jpx.cinfo.num_components) {
case 1:
*pixel_format = GF_PIXEL_GREYSCALE;
break;
case 3:
*pixel_format = GF_PIXEL_RGB;
break;
default:
jpeg_destroy_decompress(&jpx.cinfo);
return GF_NON_COMPLIANT_BITSTREAM;
}
if (!dst || (*dst_size < *height * *width * jpx.cinfo.num_components)) {
*dst_size = *height * *width * jpx.cinfo.num_components;
jpeg_destroy_decompress(&jpx.cinfo);
return GF_BUFFER_TOO_SMALL;
}
if (!dst_nb_comp) dst_nb_comp = jpx.cinfo.num_components;
scan_line = NULL;
/*decode*/
jpx.cinfo.do_fancy_upsampling = FALSE;
jpx.cinfo.do_block_smoothing = FALSE;
if (!jpeg_start_decompress(&jpx.cinfo)) {
jpeg_destroy_decompress(&jpx.cinfo);
return GF_NON_COMPLIANT_BITSTREAM;
}
if (jpx.cinfo.rec_outbuf_height>JPEG_MAX_SCAN_BLOCK_HEIGHT) {
jpeg_destroy_decompress(&jpx.cinfo);
GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[gf_img_jpeg_dec] : jpx.cinfo.rec_outbuf_height>JPEG_MAX_SCAN_BLOCK_HEIGHT\n"));
return GF_IO_ERR;
}
/*read scanlines (the scan is not one line by one line so alloc a placeholder for block scaning) */
scan_line = gf_malloc(sizeof(char) * stride * jpx.cinfo.rec_outbuf_height);
for (i = 0; i<jpx.cinfo.rec_outbuf_height; i++) {
lines[i] = scan_line + i * stride;
}
tmp = dst;
for (j=0; j< (s32) *height; j += jpx.cinfo.rec_outbuf_height) {
jpeg_read_scanlines(&jpx.cinfo, (unsigned char **) lines, jpx.cinfo.rec_outbuf_height);
scans = jpx.cinfo.rec_outbuf_height;
if (( (s32) *height - j) < scans) scans = *height - j;
ptr = scan_line;
/*for each line in the scan*/
for (k = 0; k < scans; k++) {
if (dst_nb_comp==(u32)jpx.cinfo.num_components) {
memcpy(tmp, ptr, sizeof(char) * stride);
ptr += stride;
tmp += stride;
} else {
u32 z, c;
for (z=0; z<*width; z++) {
for (c=0; c<(u32)jpx.cinfo.num_components; c++) {
if (c >= dst_nb_comp) break;
tmp[c] = ptr[c];
}
ptr += jpx.cinfo.num_components;
tmp += dst_nb_comp;
}
}
}
}
/*done*/
jpeg_finish_decompress(&jpx.cinfo);
jpeg_destroy_decompress(&jpx.cinfo);
gf_free(scan_line);
return GF_OK;
}
#else
GF_EXPORT
GF_Err gf_img_jpeg_dec(u8 *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, u8 *dst, u32 *dst_size, u32 dst_nb_comp)
{
return GF_NOT_SUPPORTED;
}
#endif /*GPAC_HAS_JPEG*/
#ifdef GPAC_HAS_PNG
typedef struct
{
char *buffer;
u32 pos;
u32 size;
png_byte **rows;
} GFpng;
static void gf_png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
GFpng *ctx = (GFpng*)png_get_io_ptr(png_ptr);
if (ctx->pos + length > ctx->size) {
png_error(png_ptr, "Read Error");
} else {
memcpy(data, (char*) ctx->buffer + ctx->pos, length);
ctx->pos += (u32) length;
}
}
static void gf_png_user_error_fn(png_structp png_ptr,png_const_charp error_msg)
{
if (png_ptr) {
longjmp(png_jmpbuf(png_ptr), 1);
}
}
GF_EXPORT
GF_Err gf_img_png_dec(u8 *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, u8 *dst, u32 *dst_size)
{
GFpng udta;
png_struct *png_ptr;
png_info *info_ptr;
u32 i, stride, out_size;
png_bytep trans_alpha;
int num_trans;
png_color_16p trans_color;
if ((png_size<8) || png_sig_cmp((png_bytep)png, 0, 8) ) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[PNG]: Wrong signature\n"));
return GF_NON_COMPLIANT_BITSTREAM;
}
udta.buffer = png;
udta.size = png_size;
udta.pos = 0;
udta.rows=NULL;
#ifdef GPAC_ENABLE_COVERAGE
if (gf_sys_is_cov_mode()) {
gf_png_user_error_fn(NULL, NULL);
}
#endif
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) &udta, NULL, NULL);
if (!png_ptr) return GF_IO_ERR;
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
return GF_IO_ERR;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr);
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
if (udta.rows) gf_free(udta.rows);
return GF_IO_ERR;
}
png_set_read_fn(png_ptr, &udta, (png_rw_ptr) gf_png_user_read_data);
png_set_error_fn(png_ptr, &udta, (png_error_ptr) gf_png_user_error_fn, NULL);
png_read_info(png_ptr, info_ptr);
/*unpaletize*/
if (png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_PALETTE) {
png_set_expand(png_ptr);
png_read_update_info(png_ptr, info_ptr);
}
num_trans = 0;
png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color);
if (num_trans) {
png_set_tRNS_to_alpha(png_ptr);
png_read_update_info(png_ptr, info_ptr);
}
*width = (u32) png_get_image_width(png_ptr, info_ptr);
*height = (u32) png_get_image_height(png_ptr, info_ptr);
switch (png_get_color_type(png_ptr, info_ptr)) {
case PNG_COLOR_TYPE_GRAY:
*pixel_format = GF_PIXEL_GREYSCALE;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
*pixel_format = GF_PIXEL_GREYALPHA;
break;
case PNG_COLOR_TYPE_RGB:
*pixel_format = GF_PIXEL_RGB;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
*pixel_format = GF_PIXEL_RGBA;
break;
default:
png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr);
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
return GF_NOT_SUPPORTED;
}
out_size = (u32) (png_get_rowbytes(png_ptr, info_ptr) * png_get_image_height(png_ptr, info_ptr));
/*new cfg, reset*/
if (*dst_size != out_size) {
*dst_size = out_size;
png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr);
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
return GF_BUFFER_TOO_SMALL;
}
*dst_size = out_size;
if (!dst) return GF_BAD_PARAM;
/*read*/
stride = (u32) png_get_rowbytes(png_ptr, info_ptr);
udta.rows = (png_bytepp) gf_malloc(sizeof(png_bytep) * png_get_image_height(png_ptr, info_ptr));
for (i=0; i<png_get_image_height(png_ptr, info_ptr); i++) {
udta.rows[i] = (png_bytep)dst + i*stride;
}
png_read_image(png_ptr, udta.rows);
png_read_end(png_ptr, NULL);
gf_free(udta.rows);
png_destroy_info_struct(png_ptr,(png_infopp) & info_ptr);
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
return GF_OK;
}
void gf_png_write(png_structp png, png_bytep data, png_size_t size)
{
GFpng *p = (GFpng *)png_get_io_ptr(png);
memcpy(p->buffer+p->pos, data, sizeof(char)*size);
p->pos += (u32) size;
}
void gf_png_flush(png_structp png)
{
}
/* write a png file */
GF_EXPORT
GF_Err gf_img_png_enc(u8 *data, u32 width, u32 height, s32 stride, u32 pixel_format, u8 *dst, u32 *dst_size)
{
GFpng udta;
png_color_8 sig_bit;
png_int_32 k;
png_bytep *row_pointers;
png_structp png_ptr;
png_infop info_ptr;
u32 type, nb_comp;
switch (pixel_format) {
case GF_PIXEL_GREYSCALE:
nb_comp = 1;
type = PNG_COLOR_TYPE_GRAY;
break;
case GF_PIXEL_GREYALPHA:
nb_comp = 1;
type = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
case GF_PIXEL_RGB:
case GF_PIXEL_BGR:
case GF_PIXEL_RGBX:
case GF_PIXEL_BGRX:
nb_comp = 3;
type = PNG_COLOR_TYPE_RGB;
break;
case GF_PIXEL_RGBA:
case GF_PIXEL_ARGB:
nb_comp = 4;
type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
default:
return GF_NOT_SUPPORTED;
}
if (*dst_size < width*height*nb_comp) return GF_BUFFER_TOO_SMALL;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
// png_voidp user_error_ptr, user_error_fn, user_warning_fn);
if (png_ptr == NULL) return GF_IO_ERR;
#ifdef GPAC_ENABLE_COVERAGE
if (gf_sys_is_cov_mode()) {
gf_png_flush(NULL);
}
#endif
/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_write_struct(&png_ptr, NULL);
return GF_IO_ERR;
}
/* Set error handling. REQUIRED if you aren't supplying your own
* error handling functions in the png_create_write_struct() call.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return GF_NON_COMPLIANT_BITSTREAM;
}
udta.buffer = dst;
udta.pos = 0;
png_set_write_fn(png_ptr, &udta, gf_png_write, gf_png_flush);
png_set_IHDR(png_ptr, info_ptr, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
/* otherwise, if we are dealing with a color image then */
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
sig_bit.gray = 8;
sig_bit.alpha = 8;
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
#if 0
/* Optionally write comments into the image */
text_ptr[0].key = "Title";
text_ptr[0].text = "Mona Lisa";
text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[1].key = "Author";
text_ptr[1].text = "Leonardo DaVinci";
text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[2].key = "Description";
text_ptr[2].text = "<long text>";
text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
#ifdef PNG_iTXt_SUPPORTED
text_ptr[0].lang = NULL;
text_ptr[1].lang = NULL;
text_ptr[2].lang = NULL;
#endif
png_set_text(png_ptr, info_ptr, text_ptr, 3);
#endif
png_write_info(png_ptr, info_ptr);
/* Shift the pixels up to a legal bit depth and fill in
* as appropriate to correctly scale the image.
*/
png_set_shift(png_ptr, &sig_bit);
/* pack pixels into bytes */
png_set_packing(png_ptr);
if (pixel_format==GF_PIXEL_ARGB) {
// png_set_swap_alpha(png_ptr);
png_set_bgr(png_ptr);
}
switch (pixel_format) {
case GF_PIXEL_RGBX:
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
png_set_bgr(png_ptr);
break;
case GF_PIXEL_BGRX:
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
break;
case GF_PIXEL_BGR:
png_set_bgr(png_ptr);
break;
}
row_pointers = gf_malloc(sizeof(png_bytep)*height);
for (k = 0; k < (s32)height; k++)
row_pointers[k] = (png_bytep) data + k*stride;
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
gf_free(row_pointers);
/* clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, &info_ptr);
*dst_size = udta.pos;
return GF_OK;
}
#else
GF_EXPORT
GF_Err gf_img_png_dec(u8 *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, u8 *dst, u32 *dst_size)
{
return GF_NOT_SUPPORTED;
}
GF_EXPORT
GF_Err gf_img_png_enc(u8 *data, u32 width, u32 height, s32 stride, u32 pixel_format, u8 *dst, u32 *dst_size)
{
return GF_NOT_SUPPORTED;
}
#endif /*GPAC_HAS_PNG*/