libpng - 错误:不是PNG文件 - png_process_data()

时间:2012-05-03 19:06:44

标签: c libpng

我正在尝试使用libpng编写一个渐进式阅读器。我试图遵循libpng源提供的example.c中的代码,但它不起作用。我也看了libpng doc。这会指示您将png_set_progressive_read_fn()png_process_data()结合使用。设置完文档中指定的内容后。当我开始阅读png文件的过程时,我从libpng得到以下错误。

  

不是PNG文件

单步执行代码会发现当png_process_data()检查PNG文件的签名时,它会识别出它不是png文件。这是非常特殊的行为,因为在我调用我的读取函数之前,我验证文件是一个带有以下代码的PNG文件。

int check_png_sig(const char * file_name)
{
  FILE *fp = fopen(file_name, "rb");
  if(!fp) {
    return (0);
  }
  enum HeaderSize {Size = 8}; 
  png_byte header[Size];
  fread(header, 1, Size, fp);
  int is_png = !png_sig_cmp(header, 0, Size);
  if (!is_png) {
    fclose(fp);
    return (0);
  }
  fclose(fp);
  return (1);
}

我关闭FILE*,然后在我的read函数中重新打开一个新的,所以我不必告诉libpng我已经用png_set_sig_bytes(png_ptr, 8);读取了8个字节。我的阅读功能如下。

int read_png_file(const char * file_name)
{
  fprintf(stdout, "Reading PNG File %s\n", file_name);
  fflush(stdout);

  if (!png_ptr || !info_ptr) {
    return (0);
  }

  FILE *fp = fopen(file_name, "rb");
  if(!fp) {
    return (0);
  }

  png_init_io(png_ptr, fp);
  png_voidp user_chunkp = png_get_user_chunk_ptr(png_ptr);

  /*
   * Tell libpng to call read_chunk_callback if an unknown chunk is
   * encountered
   */
  png_set_read_user_chunk_fn(png_ptr, user_chunkp, read_chunk_callback);

  /*
   * Tell libpng to call read_row_callback if an known chunk is
   * encountered.
   */
  png_set_read_status_fn(png_ptr, read_row_callback);

  /*
   * Tell libpng to call the specified functions on info, rows, or
   * end.
   */
  png_set_progressive_read_fn(png_ptr, user_chunkp, info_callback,
    row_callback, end_callback);

  enum Size {DataSize = 8};
  unsigned char rawbuf[DataSize];
  int process = 1;
  if (setjmp(png_jmpbuf(png_ptr))) {
    free_png_resources();
    process = 0;
    return process;
  }

  /*
   * Check to see if libpng is confused
   */
  //png_set_sig_bytes(png_ptr, 8);

  while (process) {
    memset(rawbuf, 0, DataSize);
    png_process_data(png_ptr, info_ptr, rawbuf, DataSize);
  }

  fclose(fp);
  return (process);
}

我在(non-progressive reader)也有一个非porgressive阅读器的工作版本。它在我的文件上运行得很好。所以这不是我的文件的问题。


完整代码

#include <png.h>
#include <stdlib.h>

static png_structp png_ptr;
static png_infop info_ptr;
static png_bytep old_row = NULL;

/* Variable that loops untill data is read */
int run = 0;

//-------------------------------------------------------------------
// Callbacks
int read_chunk_callback(png_structp png_ptr, png_unknown_chunkp chunk)
{
  struct chunk
  {
    png_byte name[5];
    png_byte *data;
    png_size_t size;
  };

  return (0); /* did not recognize */
}

/*
 * This method will be called each time a row is read
 * and differs from the row_callback in that ...
 */
void read_row_callback(png_structp ptr, png_uint_32 row, int pass)
{
  fprintf(stderr, "read_row_callback\n");
}

int process_data(png_bytep buffer, png_uint_32 length)
{
  fprintf(stderr, "process_data\n");
  png_process_data(png_ptr, info_ptr, buffer, length);
  return 0;
}

void info_callback(png_structp png_ptr, png_infop info)
{
  fprintf(stderr, "info_callback\n");
  png_uint_32 width;
  png_uint_32 height;
  int         bit_depth;
  int         color_type;
  int         interlace_type;
  int         compression_type;
  int         filter_type;
  png_byte    channels;
  png_uint_32 rowbytes;
  png_bytep   signature;

  /*
   * This will get the information stored in the header
   * of the PNG file.
   */
  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
    &interlace_type, &compression_type, &filter_type);

  /*
   * Get the rest of the header information.
   */
  channels = png_get_channels(png_ptr, info_ptr);
  /* This is subject to change with transformations */
  rowbytes = png_get_rowbytes(png_ptr, info_ptr);
  signature = png_get_signature(png_ptr, info_ptr);

  fprintf(stdout,
    "width: %u"
    "height: %u"
    "bit_depth: %d"
    "color_type: %d"
    "interlace_type: %d"
    "compression_type: %d"
    "filter_type: %d"
    "channles: %d"
    "rowbytes: %u"
    "signature: %s", (unsigned int)width, (unsigned int)height, bit_depth, color_type,
    interlace_type, compression_type, filter_type, channels,
    (unsigned int)rowbytes, signature);
}

void row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
  int pass)
{
  fprintf(stderr, "row_callback\n");
  png_progressive_combine_row(png_ptr, old_row, new_row);
}

void end_callback(png_structp png_ptr, png_infop info)
{
  fprintf(stderr, "end_callback\n");
  run = 1;
}

/*
 * Error handler local to his translation unit
 * Used by png_create_read_struct function.
 */
void progressive_reader_error_fn(png_structp png_ptr, png_const_charp msg)
{
  fprintf(stderr, "error: %s\n", msg);
  fflush(stderr);
  longjmp(png_jmpbuf(png_ptr), 1);
  exit(0);
}

//-------------------------------------------------------------------

/*
 * Free PNG resources on close
 */
void free_png_resources() 
{
  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
  png_ptr = NULL;
  info_ptr = NULL;

}

/*
 * All strings to this function must
 * be null terminated -- return 0 if
 * file is not in png format
 */
int check_png_sig(const char * file_name)
{
  FILE *fp = fopen(file_name, "rb");
  if(!fp) {
    return (0);
  }
  enum HeaderSize {Size = 8};
  png_byte header[Size];
  fread(header, 1, Size, fp);
  int is_png = !png_sig_cmp(header, 0, Size);
  if (!is_png) {
    fclose(fp);
    return (0);
  }
  fclose(fp);
  return (1);
}

/*
 * Create the png_structp and png_infop
 */
int create_png_structs()
{
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
    progressive_reader_error_fn, NULL);
  if (!png_ptr) {
    return 0;
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_read_struct(&png_ptr, NULL, NULL);
    return 0;
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    return 0;
  }

  return 1;
}

/*
 * This method does all the work
 */
int read_png_file(const char * file_name)
{
  fprintf(stdout, "Reading PNG File %s\n", file_name);
  fflush(stdout);

  if (!png_ptr || !info_ptr) {
    return (0);
  }

  FILE *fp = fopen(file_name, "rb");
  if(!fp) {
    return (0);
  }

  png_init_io(png_ptr, fp);
  png_voidp user_chunkp = png_get_user_chunk_ptr(png_ptr);

  /*
   * Tell libpng to call read_chunk_callback if an unknown chunk is
   * encountered
   */
  png_set_read_user_chunk_fn(png_ptr, user_chunkp, read_chunk_callback);

  /*
   * Tell libpng to call read_row_callback if an known chunk is
   * encountered.
   */
  png_set_read_status_fn(png_ptr, read_row_callback);

  /*
   * Tell libpng to call the specified functions on info, rows, or
   * end.
   */
  png_set_progressive_read_fn(png_ptr, user_chunkp, info_callback,
    row_callback, end_callback);

  enum Size {DataSize = 8};
  unsigned char rawbuf[DataSize];
  int process = 1;
  if (setjmp(png_jmpbuf(png_ptr))) {
    free_png_resources();
    process = 0;
    return process;
  }

  /*
   * Check to see if libpng is confused
   */
  //png_set_sig_bytes(png_ptr, 8);

  while (process) {
    memset(rawbuf, 0, DataSize);
    png_process_data(png_ptr, info_ptr, rawbuf, DataSize);
  }

  fclose(fp);
  return (process);
}

int main(int argc, char *argv[])
{
  if (check_png_sig("/home/matt6809/Downloads/png_image.png")) {
    if (create_png_structs()) {
      if (read_png_file("/home/matt6809/Downloads/png_image.png")) {
        fprintf(stderr, "SUCCESS!!!\n");
      }   
    }
  } 
  return 0;
}

生成文件

CC = /usr/bin/gcc
LD = /usr/bin/gcc
CFLAGS = -c -Wall -g
LDFLAGS = -lpng

SRC = $(wildcard *.c)
OBJ = $(SRC:%.c=%.o)

TARGET = progressive

all : $(TARGET)

$(TARGET) : $(OBJ)
        $(LD) $(LDFLAGS) $^ -o $(TARGET)

%.o : %.c
        $(CC) $(CFLAGS) -o $@ $<

clean :
        rm -rf *.o $(TARGET)

1 个答案:

答案 0 :(得分:1)

您忘记阅读文件中的内容。

更换:

while (process) {
  memset(rawbuf, 0, DataSize);
  png_process_data(png_ptr, info_ptr, rawbuf, DataSize);
}

size_t read;
while ((read = fread(rawbuf, 1, DataSize, fp))) {
  png_process_data(png_ptr, info_ptr, rawbuf, read);
}

足以允许解码继续进行。

我确实注意到另外一件事:传递给png_set_read_user_chunk_fn()png_set_progressive_read_fn()的第二个参数应该是回调函数稍后可以获取的任意值(通过png_get_user_chunk_ptr()和{分别为{1}}。

可以将它们设置为您喜欢的任何值(如果您不需要它们,则设置为NULL),但您不应该自己调用png_get_progressive_ptr()函数来获取值传递给{{1 }}。 (我认为它是无害的,因为它们会在开始时返回NULL,但它最多会让人感到困惑。)