root/src/media_tools/img.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gf_img_parse
  2. gf_jpeg_nonfatal_error2
  3. gf_jpeg_output_message
  4. gf_jpeg_fatal_error
  5. gf_jpeg_stub
  6. gf_jpeg_fill_input_buffer
  7. gf_jpeg_skip_input_data
  8. gf_img_jpeg_dec
  9. gf_img_jpeg_dec
  10. gf_png_user_read_data
  11. gf_png_user_error_fn
  12. gf_img_png_dec
  13. gf_png_write
  14. gf_png_flush
  15. gf_img_png_enc
  16. gf_img_png_enc_file
  17. gf_img_png_dec
  18. gf_img_png_enc
  19. gf_img_png_enc_file
  20. gf_img_file_dec

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      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/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, u8 *OTI, u32 *mtype, u32 *width, u32 *height, char **dsi, u32 *dsi_len)
{
        u8 b1, b2, b3;
        u32 size, type;
        u64 pos;
        pos = gf_bs_get_position(bs);
        gf_bs_seek(bs, 0);

        *mtype = *width = *height = 0;
        *OTI = 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 type, w, h;
                        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:
                                gf_bs_skip_bytes(bs, 3);
                                h = gf_bs_read_int(bs, 16);
                                w = gf_bs_read_int(bs, 16);
                                if ((w > *width) || (h > *height)) {
                                        *width = w;
                                        *height = h;
                                }
                                nb_comp = gf_bs_read_int(bs, 8);
                                break;
                        }
                }
                *OTI = GPAC_OTI_IMAGE_JPEG;
                *mtype = GF_4CC('j','p','e','g');
                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);
                *OTI = GPAC_OTI_IMAGE_PNG;
                *mtype = GF_4CC('p','n','g',' ');
        }
        /*try j2k*/
        else {
                size = gf_bs_read_u8(bs);
                type = gf_bs_read_u32(bs);
                if ( ((size==12) && (type==GF_4CC('j','P',' ',' ') ))
                || (type==GF_4CC('j','p','2','h') ) ) {

                        if (type==GF_4CC('j','p','2','h')) {
                                *OTI = GPAC_OTI_IMAGE_JPEG_2000;
                                *mtype = GF_4CC('j','p','2',' ');
                                goto j2k_restart;
                        }

                        type = gf_bs_read_u32(bs);
                        if (type!=0x0D0A870A) goto exit;

                        *OTI = GPAC_OTI_IMAGE_JPEG_2000;
                        *mtype = GF_4CC('j','p','2',' ');

                        while (gf_bs_available(bs)) {
j2k_restart:
                                size = gf_bs_read_u32(bs);
                                type = gf_bs_read_u32(bs);
                                switch (type) {
                                case GF_4CC('j','p','2','h'):
                                        goto j2k_restart;
                                case GF_4CC('i','h','d','r'):
                                {
                                        u16 nb_comp;
                                        u8 BPC, C, UnkC, IPR;
                                        *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) {
                                                GF_BitStream *bs_dsi = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
                                                gf_bs_write_u32(bs_dsi, *height);
                                                gf_bs_write_u32(bs_dsi, *width);
                                                gf_bs_write_u16(bs_dsi, nb_comp);
                                                gf_bs_write_u8(bs_dsi, BPC);
                                                gf_bs_write_u8(bs_dsi, C);
                                                gf_bs_write_u8(bs_dsi, UnkC);
                                                gf_bs_write_u8(bs_dsi, IPR);
                                                gf_bs_get_content(bs_dsi, dsi, dsi_len);
                                                gf_bs_del(bs_dsi);
                                        }
                                        goto exit;
                                }
                                break;
                                default:
                                        gf_bs_skip_bytes(bs, size-8);
                                        break;
                                }
                        }
                }
        }

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) {
        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)
{
        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)
{
        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(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *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;

        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_24;
                break;
        default:
                jpeg_destroy_decompress(&jpx.cinfo);
                return GF_NON_COMPLIANT_BITSTREAM;
        }
        if (*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)) {
                if (scan_line) gf_free(scan_line);
                jpeg_destroy_decompress(&jpx.cinfo);
                return GF_NON_COMPLIANT_BITSTREAM;
        }
        if (jpx.cinfo.rec_outbuf_height>JPEG_MAX_SCAN_BLOCK_HEIGHT) {
                if (scan_line) gf_free(scan_line);
                jpeg_destroy_decompress(&jpx.cinfo);
                GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[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(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *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)
{
        longjmp(png_jmpbuf(png_ptr), 1);
}


GF_EXPORT
GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *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;
        
        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_ALPHAGREY;
                break;
        case PNG_COLOR_TYPE_RGB:
                *pixel_format = GF_PIXEL_RGB_24;
                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(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *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_ALPHAGREY:
                nb_comp = 1;
                type = PNG_COLOR_TYPE_GRAY_ALPHA;
                break;
        case GF_PIXEL_RGB_24:
        case GF_PIXEL_BGR_24:
        case GF_PIXEL_RGB_32:
        case GF_PIXEL_BGR_32:
                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;

        /* 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_RGB_32:
                png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
                png_set_bgr(png_ptr);
                break;
        case GF_PIXEL_BGR_32:
                png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
                break;
        case GF_PIXEL_BGR_24:
                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;
}

/* write a png file */
GF_EXPORT
GF_Err gf_img_png_enc_file(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst_file)
{
        GF_Err e;
        FILE *png;
        u32 dst_size = width*height*4;
        char *dst = (char*)gf_malloc(sizeof(char)*dst_size);
        if (!dst) return GF_OUT_OF_MEM;

        e = gf_img_png_enc(data, width, height, stride, pixel_format, dst, &dst_size);
        if (e) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[PNG]: Error encoding image %s\n", gf_error_to_string(e) ));
                goto exit;
        }

        png = gf_fopen(dst_file, "wb");
        if (!png) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[PNG]: Error opening destination file %s\n", dst_file ));
                goto exit;
        }

        gf_fwrite(dst, dst_size, 1, png);
        gf_fclose(png);

exit:
        gf_free(dst);
        return e;
}
#else
GF_EXPORT
GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size)
{
        return GF_NOT_SUPPORTED;
}
GF_EXPORT
GF_Err gf_img_png_enc(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst, u32 *dst_size)
{
        return GF_NOT_SUPPORTED;
}

GF_EXPORT
GF_Err gf_img_png_enc_file(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst_file)
{
        return GF_NOT_SUPPORTED;
}
#endif  /*GPAC_HAS_PNG*/

GF_EXPORT
GF_Err gf_img_file_dec(char *png_filename, u32 *hint_oti, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size)
{
        u32 fsize, read, oti;
        FILE *f;
        char *data;
        GF_Err e;
        f = gf_fopen(png_filename, "rb");
        if (!f) return GF_URL_ERROR;

        oti = 0;
        if (!hint_oti || ! *hint_oti) {
                char *ext = strrchr(png_filename, '.');
                if (!ext) return GF_NOT_SUPPORTED;
                if (!stricmp(ext, ".png")) oti = GPAC_OTI_IMAGE_PNG;
                else if (!stricmp(ext, ".jpg") || !stricmp(ext, ".jpeg")) oti = GPAC_OTI_IMAGE_JPEG;
        } else if (hint_oti) {
                oti = *hint_oti;
        }
        gf_fseek(f, 0, SEEK_END);
        fsize = (u32)gf_ftell(f);
        gf_fseek(f, 0, SEEK_SET);
        data = gf_malloc(fsize);
        read = (s32) fread(data, sizeof(char), fsize, f);
        gf_fclose( f );
        if (read != fsize) return GF_IO_ERR;

        e = GF_NOT_SUPPORTED;
        *dst_size = 0;
        if (oti == GPAC_OTI_IMAGE_JPEG) {
#ifdef GPAC_HAS_JPEG
                e = gf_img_jpeg_dec(data, fsize, width, height, pixel_format, NULL, dst_size, 0);
                if (*dst_size) {
                        *dst = gf_malloc(*dst_size);
                        return gf_img_jpeg_dec(data, fsize, width, height, pixel_format, *dst, dst_size, 0);
                }
#endif
        } else if (oti == GPAC_OTI_IMAGE_PNG) {
#ifdef GPAC_HAS_PNG
                e = gf_img_png_dec(data, fsize, width, height, pixel_format, NULL, dst_size);
                if (*dst_size) {
                        *dst = gf_malloc(*dst_size);
                        return gf_img_png_dec(data, fsize, width, height, pixel_format, *dst, dst_size);
                }
#endif
        }
        return e;
}


/* [<][>][^][v][top][bottom][index][help] */