うーびのメモ

メモとか。

C言語 メモリ上にある、PNGデータをlibpngを介して読み込むサンプル。

メモリ上に確保したPNGデータをlibpngに読み込んでもらってlibpngから画像データを取得してみる感じです。

/* read_png_from_mem.c
 * メモリ上にある、PNGデータをlibpngを介して読み込むサンプル。
 * 参考:
 * http://d.hatena.ne.jp/yoya/20080714
 * http://diary.awm.jp/~yoya/data/2008/07/14/png_test.c
 * http://gmoon.jp/png/
 */
#include <stdio.h>
#include <stdlib.h> 
#include <png.h>



typedef struct my_png_buffer_ {
    unsigned char *data;    /* PNGのデータが入ったメモリへのポインタ */
    unsigned long data_len; /* PNGのデータの長さ。 */
    unsigned long data_offset;  /* 読み込み時用のオフセット */
} my_png_buffer;

void test(void);

void png_memread_func(png_structp png_ptr, png_bytep buf, png_size_t size);

int main(void) {
    
    test();
    
    return 0;
}


/* メモリ上のPNGデータをlibpngの指定するメモリにコピー(読み込み)。
 * 
 * この関数は、png_set_read_fn関数の第三引数に指定される事により、PNG画像の
 * データ読み込み時にコールバック(実行)されるようになります。
 * 
 * png_structp  png_ptr     png_set_read_fnで一番目の引数に指定された
 *              png_structp構造体。
 *              この構造体からpng_set_read_fnで
 *              二番目の引数に指定されたデータ
 *              (つまり、ストリームへのポインタ)を
 *              png_get_io_ptr()で取得できます。
 * 
 * png_bytep    buf     コピー(読み込み)先となるメモリ。
 * 
 * png_size_t   size        コピーすべき(読み込むべき)長さ(バイト)。
 */
void png_memread_func(png_structp png_ptr, png_bytep buf, png_size_t size){
    /* 読み込むメモリの長さを表示。 */
    printf("memRead - size =\t%04ld bytes\n", size);
    /* ストリームとして指定したデータを取得。 */
    my_png_buffer *png_buff = (my_png_buffer *)png_get_io_ptr(png_ptr);
    /* 読み込むメモリの長さとオフセット(今までに読み込んだメモリの長さ)が
     * データの長さを越えていないかチェック。 */
    if (png_buff->data_offset + size <= png_buff->data_len) {
        /* メモリのコピー */
        memcpy(buf, png_buff->data + png_buff->data_offset, size);
        /* オフセットに今し方読み込んだメモリの長さを追加。 */
        png_buff->data_offset += size;
        /* オフセットを表示 */
        //printf("memRead - offset =\t%08ld bytes\n", png_buff->data_offset);
    } else {
        png_error(png_ptr,"png_mem_read_func failed");
    }
}

void test(void)
{
    png_structp png_ptr;
    png_infop   info_ptr;
    my_png_buffer png_buf;
    FILE *fp;
    
    /* ファイルオープン */
    if((fp = fopen("image.png", "rb")) == NULL){
        fprintf(stderr, "%s%s\n","error::", "can not open image.");
        exit(EXIT_FAILURE);
    }
    
    /* ファイルサイズの取得 */
    fseek(fp, 0L, SEEK_END);
    fgetpos(fp, &(png_buf.data_len));
    fseek(fp, 0L, SEEK_SET);
    printf("filesize = %ld\n", png_buf.data_len);
    
    /* PNGデータ用のメモリ確保 */
    png_buf.data = calloc(png_buf.data_len, 1);
    /* ファイル読み込み */
    fread(png_buf.data, 1, png_buf.data_len, fp);
    /* オフセットはまだゼロ。 */
    png_buf.data_offset = 0;
    /* ファイルを閉じます */
    fclose(fp);
    
    
    /* PNGファイルかどうかの確認 */
    int is_png;
    is_png = png_check_sig((png_bytep)png_buf.data, 8);
    if (! is_png) {
        fprintf(stderr, "%s%s\n", "error::", "is not PNG!");
        exit(EXIT_FAILURE);
    }
    
    /* png_ptr構造体を確保・初期化します */
    png_ptr = png_create_read_struct(
        PNG_LIBPNG_VER_STRING,
        NULL,
        NULL,
        NULL);
    if(png_ptr==NULL){
        exit(EXIT_FAILURE);
    }
    
    /* info_ptr構造体を確保・初期化します */
    info_ptr=png_create_info_struct(png_ptr);
    if(info_ptr==NULL){
        png_destroy_read_struct(&png_ptr,NULL,NULL);
        exit(EXIT_FAILURE);
    }
    
    /* ファイルの読み込み時に使用するデータ及び、関数を指定します。 */
    png_set_read_fn(png_ptr,(png_voidp)&png_buf,
                (png_rw_ptr)png_memread_func);
    
    
    /* 画像情報の取得 */
    png_uint_32 width,height;
    int bit_depth,color_type,interlace_type;
    int compression_type,filter_type;
    /* 画像情報を読み込みます。 */
    printf("\nstart\treading information.\n");
    png_read_info(png_ptr,info_ptr);
    printf("exit\treading information.\n\n");
    printf("\nstart\tgetting IHDR chunk.\n");
    png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,&color_type,
    &interlace_type,&compression_type,&filter_type);
    printf("exit\tgetting IHDR chunk.\n\n");
    printf("(width, height)=(%ld,%ld) bpp=%d color_type=%d\n",
            width, height, bit_depth, color_type);
    //printf("\n", width, height);
    
    
    
    /* 画像イメージの取得 */
    unsigned char **image;
    int i, j;
    /* 以下3行は2次元配列を確保します */
    image = (png_bytepp)malloc(height * sizeof(png_bytep));
    for (i = 0; i < height; i++)
        image[i] = (png_bytep)malloc(
                    png_get_rowbytes(png_ptr, info_ptr));
    
    /* イメージを読み込みます */
    printf("\nstart\treading image.\n");
    png_read_image(png_ptr, image);
    printf("exit\treading image.\n\n");
    /* 読み込みが終了したのでPNGデータの入ったメモリを開放。 */
    free(png_buf.data);
    for(i = 0; i < height; i+=height/38){
        //printf("%d, %d, ", i, j);
        for(j = 0; j < width; j+=width/38) {
            printf("%02x", image[i][j*4]);
        }
        printf("\n");
    }
    
    
    /* 以下2行は2次元配列を解放します */
    for (i = 0; i < height; i++)
        free(image[i]);
    free(image);
    png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL);


}

参考:
(ファイルでなく)配列変数で PNGデータを libpng に渡す方法 - yoyaのメモ http://diary.awm.jp/~yoya/data/2008/07/14/png_test.c
PNG 利用術