0

I am writing a program in C which will read in an image in Bitmap format, and adjust some image parameters like colours, making the image greyscale, etc. I have the image loaded in, and have stored the image headers in 2 structs following Microsoft's documentation, here. I'm having trouble dynamically allocating memory for a 2D array which will serve as the new image to be written out. Each index in the matrix will be an RGB struct PX_RGB:

typedef struct RGB {
    BYTE rgbPK_BLUE;
    BYTE rgbPK_GREEN;
    BYTE rgbPK_RED;
} PX_RGB;

I've read a number of threads, most helpfully this one: Calloc a Two-Dimensional Array, and less helpfully this one: I have problems making a function to handle a matrix using calloc. (BYTE is just an alias for a uint8_t). If I naively allocate based solely off of the number of bytes in the input image and the size of the structs:

PX_RGB *new_image = calloc(img_information.bi_size_image, sizeof(PX_RGB));

I'm unable to index it as I would like to, like a matrix (i.e. new_image[i][j] = ... The comments and responses from the first thread seem to indicate placing one of the image dimensions on the left-hand side and allocating this way:

PX_RGB (*new_image)[image_width] = calloc(image_height, image_width * sizeof(PX_RGB));

But the compiler isn't happy about this, as the size of any potential image isn't known at compile time. There's also a different solution, but this just doesn't seem to do anything - neither the check for NULL neither the code trying to index it seems to do anything:

PX_RGB **new_image;
new_image = calloc(image_height, image_width * sizeof(new_image));

if (new_image == NULL) { printf("\nError: Could not allocate image memory.\n"); return -1; }
for (int i = 0; i < image_height; i++)
{
    for (int j = 0; j < image_width; j++)
    {
        new_image[i][j].rgbPK_BLUE = j;
        printf("\nIndex [%d][%d] = %d", i, j, new_image[i][j].rgbPK_BLUE);
    }
}

I had seen this used as an answer in the first thread, but there the number of columns needed was known and hard-coded in ([3]). I came across this formula for getting the correct row offset here, and with this test code seems to work fine, I think:

PX_RGB *new_image = calloc(image_height, image_width * sizeof(new_image));

int offset = 0;

for (int i = 0; i < image_height; i++)
{
    for (int j = 0; j < image_width; j++)
    {
        offset = (j * image_width) + i;
        new_image[offset].rgbPK_BLUE = j;
        printf("\nIndex [%d][%d] = %d", i, j, new_image[offset].rgbPK_BLUE);
    }
}

So, is there no way to index dynamically allocated memory the way I want to? I would ideally like to pass my new image to the manipulation functions like this:

void grayscale(int image_height, int image_width, PX_RGB new_image[image_height][image_width])
{
    return;
}

because I have seen other code doing this and I'm stumped why I can't get my version working, and what I'm doing wrong. I should say I got the idea for this project from an open lecture series on computer science by Harvard on edX, where they seemed to have implemented this logic in the way that I want to. Help would be hugely appreciated, cheers!

Rowan
  • 351
  • 2
  • 13
  • "compiler isn't happy about this, as the size of any potential image isn't known at compile time" --> One way out, use a C99 compiler or later that supports [VLA](https://en.wikipedia.org/wiki/Variable-length_array#C99). – chux - Reinstate Monica Jun 25 '20 at 17:01
  • @chux-ReinstateMonica Thanks for the response - I had seen this mentioned in another thread. I checked my compiler version with `gcc -v`, and I'm on `mingw-w64 8.1.0`, on Windows. I remember downloading this at least in the last 6 months. Are there any compiler flags I need to pass to specify a C version? – Rowan Jun 25 '20 at 18:23
  • Try [How enable c99 mode in gcc with terminal](https://stackoverflow.com/questions/25566597/how-enable-c99-mode-in-gcc-with-terminal/25566648) – chux - Reinstate Monica Jun 25 '20 at 18:26
  • GCC 8 defaults to C11 conformance + extensions, including support for the optional (in C11) VLA feature. It should accept the `PX_RGB (*new_image)[image_width]` variation, provided that a declaration of `image_width` as an integer variable is in scope, and that variable has been assigned a positive value. This is among the cleanest options available, so if it's not working for you then a bit more detail on exactly what you're trying and on what GCC says about it would be in order. – John Bollinger Jun 25 '20 at 18:54

1 Answers1

0

You can access it as matrix with little trick as :

new_image[i][j] == *(new_image + (i*num_of_columns) + j);

Because two dimention array is nothing but a linear array with continuous memory allocation. Or otherwise, you can typecast it as

typedef Px_RGB (*ptype)[no_of_columns];
ptype new_image_indexed = (ptype)new_image;

Now new_image_indexed can be accessed as

new_image_indexed[i][j];
  • 1
    Thanks for the reply, I think I mentioned the first row-wise indexing method you mentioned somewhere in my original post. Your typedef idea is pretty neat though, I may use that as a backup up all else fails. – Rowan Jun 25 '20 at 19:54