lock
lock

Reputation: 421

PNG file are truncated and cannot be shown by every application

I have a code that produce PNG file (using libpng). I can open these files with EOG (Eye Of Gnome) but with GIMP, imagemagic and others I have an error. Exiftool tells me that png file is truncated, but I don't see where. On EOG everything is ok.

The code:

int savepng(const char *name, fits *fit, uint32_t bytes_per_sample,
        gboolean is_colour) {
    int32_t ret = -1;
    png_structp png_ptr;
    png_infop info_ptr;
    const uint32_t width = fit->rx;
    const uint32_t height = fit->ry;

    char *filename = strdup(name);
    if (!ends_with(filename, ".png")) {
        filename = str_append(&filename, ".png");
    }

    FILE *p_png_file = g_fopen(name, "wb");
    if (p_png_file == NULL) {
        return ret;
    }

    /* Create and initialize the png_struct with the desired error handler
     * functions.  If you want to use the default stderr and longjump method,
     * you can supply NULL for the last three parameters.  We also check that
     * the library version is compatible with the one used at compile time,
     * in case we are using dynamically linked libraries.  REQUIRED.
     */
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        fclose(p_png_file);
        return ret;
    }

    /* Allocate/initialize the image information data.  REQUIRED */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        fclose(p_png_file);
        png_destroy_write_struct(&png_ptr, NULL);
        return ret;
    }

    /* 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))) {
        /* If we get here, we had a problem writing the file */
        fclose(p_png_file);
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return ret;
    }

    /* Set up the output control if you are using standard C streams */
    png_init_io(png_ptr, p_png_file);

    /* Set the image information here.  Width and height are up to 2^31,
     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
     * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
     * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
     * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
     * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
     * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
     */
    if (is_colour) {
        png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
                PNG_COLOR_TYPE_RGB,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_DEFAULT);
        uint32_t profile_len;
        const unsigned char *profile = get_sRGB_profile_data(&profile_len);

        if (profile_len > 0) {
            png_set_iCCP(png_ptr, info_ptr, *name ? name : "icc", 0, (png_const_bytep) profile, profile_len);
        }
    } else {
        png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
                PNG_COLOR_TYPE_GRAY,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_DEFAULT);
    }

    /* Write the file header information.  REQUIRED */
    png_write_info(png_ptr, info_ptr);

    png_bytep *row_pointers = malloc((size_t) height * sizeof(png_bytep));

    int samples_per_pixel;
    if (is_colour) {
        samples_per_pixel = 3;
    } else {
        samples_per_pixel = 1;
    }

    if (bytes_per_sample == 2) {
        /* swap bytes of 16 bit files to most significant bit first */
        png_set_swap(png_ptr);
        WORD *data = convert_data(fit);
        for (unsigned i = 0, j = height - 1; i < height; i++)
            row_pointers[j--] = (png_bytep) ((uint16_t*) data + (size_t) samples_per_pixel * i * width);
    } else {
        uint8_t *data = convert_data8(fit);
        for (unsigned i = 0, j = height - 1; i < height; i++)
            row_pointers[j--] = (uint8_t*) data + (size_t) samples_per_pixel * i * width;
    }

    png_write_image(png_ptr, row_pointers);

    /* Clean up after the write, and free any memory allocated */
    png_destroy_write_struct(&png_ptr, &info_ptr);


    /* Close the file */
    fclose(p_png_file);
    free(row_pointers);
    free(filename);
    return 0;
}

Could someone point me out my error? Software that can't read the image just display an error dialog. That's all. So it is difficult for me to know where is the error.

Upvotes: 0

Views: 572

Answers (2)

lock
lock

Reputation: 421

As proposed by @MarkSetchell, one line was missing:

/* Clean up after the write, and free any memory allocated */
    png_write_end(png_ptr, info_ptr); // this line was missing
    png_destroy_write_struct(&png_ptr, &info_ptr);

That's all.

Thank you.

Upvotes: 1

Kevin Boone
Kevin Boone

Reputation: 4307

I think that png_write_image() only writes the image row data, so various headers or elements of meta-data are missing. I normally use png_write_png() to write the whole file.

I've attached some code that definitely works for me, in that the output can be read by Gimp, etc. I don't claim it's production-quality ;)

int bitmap_write_png (const bitmap_t *bitmap, const char *path)
  {
  int ret = -1;

  size_t x, y;
  int pixel_size = 3;
  int depth = 8;

  FILE *fp = fopen (path, "wb");
  if (fp)
    {
    ret = 0;
    png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
      NULL, NULL, NULL); 
    png_infop info_ptr = info_ptr = png_create_info_struct (png_ptr);

    png_set_IHDR (png_ptr,
                  info_ptr,
                  bitmap->width,
                  bitmap->height,
                  depth,
                  PNG_COLOR_TYPE_RGB,
                  PNG_INTERLACE_NONE,
                  PNG_COMPRESSION_TYPE_DEFAULT,
                  PNG_FILTER_TYPE_DEFAULT);

    png_byte ** row_pointers = png_malloc (png_ptr,
        bitmap->height * sizeof (png_byte *));
    for (y = 0; y < bitmap->height; y++)
      {
      png_byte *row =  
            png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
      row_pointers[y] = row;
      for (x = 0; x < bitmap->width; x++)
        {
        pixel_t *pixel = pixel_at_const (bitmap, x, y);
        *row++ = pixel->red * 255;
        *row++ = pixel->green * 255;
        *row++ = pixel->blue * 255;
        }
      }

    png_init_io (png_ptr, fp);
    png_set_rows (png_ptr, info_ptr, row_pointers);
    png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

    for (y = 0; y < bitmap->height; y++)
      {
      png_free (png_ptr, row_pointers[y]);
      }
    png_free (png_ptr, row_pointers);
    close (fp);
    }
  else
    {
    ret = errno;
    }
  return ret;
  }

Upvotes: 0

Related Questions