0
votes

I'm currently working on PNG formats. And, I'm trying to understand its characteristics:

width, height, bytewidth, bytes per width, RGB pixel (red, green, blue)

Refering to this and this, width is the resolution width of the image in pixels, height is the resolution height of the image in pixels, bytes per width takes only 8 or 16 as value, RGB pixel's three items takes 0 to 255 as integer value.

But, I cannot understand what's a bytewidth?

Because when I try to convert one bmp image to a png one in C++ I get the following error:

Unhandled exception at 0x01038F6C in Project.exe: 0xC0000005: Access violation writing location 0x00BB9000.

I get this when I initialize the output png characteristics to:

width = 1600, height = 900, bytewidth = 1, bytes per width = 1, RGB pixel (red = 255, green = 255, blue = 255)

using this code:

#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_DEPRECATE

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

typedef struct _RGBPixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} RGBPixel;

/* Structure for containing decompressed bitmaps. */
typedef struct _RGBBitmap {
    RGBPixel *pixels;
    size_t width;
    size_t height;
    size_t bytewidth;
    uint8_t bytes_per_pixel;
} RGBBitmap;

/* Returns pixel of bitmap at given point. */
#define RGBPixelAtPoint(image, x, y) \
    *(((image)->pixels) + (((image)->bytewidth * (y)) \
                        + ((x) * (image)->bytes_per_pixel)))

/* Attempts to save PNG to file; returns 0 on success, non-zero on error. */
int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
    FILE *fp = fopen(path, "wb");
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    size_t x, y;
    png_uint_32 bytes_per_row;
    png_byte **row_pointers = NULL;

    if (fp == NULL) return -1;

    /* Initialize the write struct. */
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        fclose(fp);
        return -1;
    }

    /* Initialize the info struct. */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        png_destroy_write_struct(&png_ptr, NULL);
        fclose(fp);
        return -1;
    }

    /* Set up error handling. */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(fp);
        return -1;
    }

    /* Set image attributes. */
    png_set_IHDR(png_ptr,
                 info_ptr,
                 bitmap->width,
                 bitmap->height,
                 8,
                 PNG_COLOR_TYPE_RGB,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);

    /* Initialize rows of PNG. */
    bytes_per_row = bitmap->width * bitmap->bytes_per_pixel;
    row_pointers = (png_byte **)png_malloc(png_ptr, bitmap->height * sizeof(png_byte *));
    for (y = 0; y < bitmap->height; ++y) {
        uint8_t *row = (uint8_t *)png_malloc(png_ptr, sizeof(uint8_t)* bitmap->bytes_per_pixel);
        row_pointers[y] = (png_byte *)row;
        for (x = 0; x < bitmap->width; ++x) {
            RGBPixel color = RGBPixelAtPoint(bitmap, x, y);
            *row++ = color.red;
            *row++ = color.green;  /************* MARKED LINE ***************/
            *row++ = color.blue;
        }
    }

    /* Actually write the image data. */
    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);

    /* Cleanup. */
    for (y = 0; y < bitmap->height; y++) {
        png_free(png_ptr, row_pointers[y]);
    }
    png_free(png_ptr, row_pointers);

    /* Finish writing. */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return 0;
}

int main()
{
    RGBBitmap rgbbitmap;
    rgbbitmap.height = 1600;
    rgbbitmap.width = 900;
    rgbbitmap.bytes_per_pixel = 1;
    rgbbitmap.bytewidth = 1;

    RGBPixel rgbpixel;
    rgbpixel.blue = 255;
    rgbpixel.green = 255;
    rgbpixel.red = 255;
    rgbbitmap.pixels = &rgbpixel;

    save_png_to_file(&rgbbitmap, "abc.bmp");

        return 0;
}

I'm actually getting that error on the marked line.

Any brilliant suggestion, please?

3
Should you not put a boundary check before row++?HAL
bytewidth may mean "width of a byte in pixels" and then should be 8.Peter - Reinstate Monica
@PeterSchneider, When I do so, I get the same posted error on RGBPixel color = RGBPixelAtPoint(bitmap, x, y); line.user3472134
@PeterSchneider, may this is comming from RGB colors?user3472134

3 Answers

0
votes

This

uint8_t *row = (uint8_t *)png_malloc(png_ptr, sizeof(uint8_t)* bitmap->bytes_per_pixel);

looks suspicious - I don't know how png_malloc() works, but I guess you're allocating sizeof(uint8_t) * BytesPerPixel (which is probably sizeof(uint8_t) * 4) instead of sizeof(uint8_t) * NumberOfPixels (which should be considerably larger).

0
votes

I think your rgbbitmap.bytes_per_pixel = 1 is wrong. If your work in RGB8 your bytes_per_pixel = 3, in RGBA8 is 4 and so on.

0
votes

After setting

rgbbitmap.bytes_per_pixel = 3;  // was 1 which is probably wrong

Surely

uint8_t *row = (uint8_t *)png_malloc(png_ptr, sizeof(uint8_t)* bitmap->bytes_per_pixel);

must be

uint8_t *row = (uint8_t *)png_malloc(png_ptr, sizeof(uint8_t)* bitmap->bytes_per_pixel*bitmap->width);

, i.e. each row must hold enough space for all bytes in that row ;-), which is 3 bytes per pixel times pixels in a row. You actually compute that value, bytes_per_row, but for some reason fail to use it.