I'm working on an image renderer in C that I wrote from scratch (I don't want to use anything but standard libraries), but I'm having some trouble when trying to store the image. The class I use to store images looks like this:
class RawImage
{
private:
RGB pixels[][][3] = {};
public:
int width = 0;
int height = 0;
RawImage(int width, int height)
{
this->width = width;
this->height = height;
};
RGB GetPixel(int x, int y)
{
if (x < 0 || x > width - 1)
return RGB(0.f, 0.f, 0.f);
if (y < 0 || y > height - 1)
return RGB(0.f, 0.f, 0.f);
return pixels[x][y];
};
int SetPixel(int x, int y, RGB color)
{
if (x < 0 || x > width - 1)
return -1;
if (y < 0 || y > height - 1)
return -1;
this->pixels[x][y] = color;
return 0;
}
};
When I try to compile this code, the g compiler gives the following error:
declaration of ‘pixels’ as multidimensional array must have bounds for all dimensions except the first.
How do I use a multidimensional array of which the 2 first dimensions vary in size, but the third dimension is of a fixed size?
CodePudding user response:
Assuming (as you have confirmed in the comments) that your RGB
type is a class or structure with three components, with a constructor of the form used in your GetPixel
function, then you actually want a 2D array. However (as also mentioned in the comments), it is generally more efficient to store bitmaps as flattened, one-dimensional arrays of size width × height
. The appropriate element in that array can then be indexed using the formula array[y * width x]
(assuming a row-major order and y-ordinates that increase down the bitmap).
You still have the issue of a dimension that is not known at compile time, so you can't use a normal array. But the std::vector
container is ideal for this: just resize it in your RawImage
constructor, and it can then be used in much the same way as a plain array. Also, the memory used will be automatically freed when an object of the RawImage
class is destroyed.
Here is a possible implementation of your class using such a std::vector
:
#include <vector>
class RawImage {
private:
std::vector<RGB> pixels;
public:
int width = 0;
int height = 0;
RawImage(int width, int height)
{
this->width = width;
this->height = height;
pixels.resize(width * height);
};
RGB GetPixel(int x, int y)
{
if (x < 0 || x >= width )
return RGB(0.f, 0.f, 0.f);
if (y < 0 || y >= height)
return RGB(0.f, 0.f, 0.f);
return pixels[y * width x];
};
int SetPixel(int x, int y, RGB color)
{
if (x < 0 || x >= width)
return -1;
if (y < 0 || y >= height)
return -1;
pixels[y * width x] = color;
return 0;
}
};
Important Note: In order to use the std::vector<RGB>
container like this, the RGB
class/structure must have a default constructor. I don't know exactly how you have implemented that class, but something like the following would work:
struct RGB {
float r, g, b;
RGB(float fr, float fg, float fb) : r{ fr }, g{ fg }, b{ fb } { }
RGB() : r{ 0 }, g{ 0 }, b{ 0 } { } // Default c'tor required by std::vector
};
Or, for brevity, you could 'merge' your default constructor into the one that takes three float
arguments by providing default vales for each of those arguments:
struct RGB {
float r, g, b;
RGB(float fr = 0, float fg = 0, float fb = 0) : r{ fr }, g{ fg }, b{ fb } { }
};
CodePudding user response:
Set the bounds of an array after object initialisation in cpp
The size of an array never changes through its lifetime. It's set upon creation. Technically this isn't a problem for you because you can initialise the array in the constructor.
But, size of an array variable must be compile time constant, so you cannot accept the size as a constructor parameter.
You can use a dynamic array. Most convenient way is to use std::vector
.
CodePudding user response:
Arrays are not really first size citizens in C language, and multi-dimensional arrays are not at all. There is no way to declare a multi-dimensional array where more than first dimension is not a compile time constant, full stop. The rationale is that plain arrays are low level objects and are intended to only be used in higher level containers. Unfortunately, building true multi-level containers wrapping a multidimensional array whose dimension are only known at compile time is far from trivial because of the way iterators work. A simple way if you can accept it, is to use operator ()
as an accessor method: pixels(x, y)
instead of pixels[x][y]
in a container aware of the dynamic dimensions.