How to display two-dimensional RGB array?

I would like to create a large two-dimensional RGB array in memory. Each array element consist to 3 bytes (Red,Green,Blue) representing one pixel in some picture.
How can I display this array on screen in a efficient way? Can this be done using an Image object? How to make sure is is displayed with one array entree corresponding with one pixel?

Yes, juce::Image is a good way to go. You can create the empty image with:

juce::Image myImage { Image (Image::RGB, width, height, true) }; // last parameter indicates if you want to preclear the data

And assuming you want to access the underlying data directly, use juce::Image::BitmapData:

juce::Image::BitmapData myImageData (myImage, juce::Image::BitmapData::readWrite);

and then you can get a pointer to the underlying buffer with:

uint8* myImageDataPtr { myImageData.data };

juce::Image::BitmapData has member variables available for lineStride and pixelStride too.

I use this in my Photo Mandala app.

I hope this helps.

Also, for displaying the image on the screen, one image pixel per each screen point, you can use Graphics::drawImageAt().

If you are new to JUCE and need to know more about how Graphics can be used to paint stuff, check the following tutorial: JUCE: Tutorial: The Graphics class

Try this:

// Create an Image object somewhere
Image SRC = Image(Image::RGB, width, height, true);

// Assuming you have an array of unsigned char type where bytes are in order RGB1 RGB2 RGB2 ... RGBn 
// Copy each pixel to the Image object
for (int y = 0; y < height; y++)
{
	for (int x = 0; x < width; x++)
	{
		auto R = *BMP++;
		auto G = *BMP++;
		auto B = *BMP++;
		SRC.setPixelAt(x, y, Colour::fromRGB(R, G, B));
	}
}

// Display the image in the paint() function
void paint(Graphics& g) override
{
	g.ImageAt(SRC, 0, 0);
}

Using setPixelAt is safer/easier, but slower. In case that is an issue.

I do not want to call setPixelAt() for each pixel. I need to set all pixels!

Yes that is an issue. I want to be able to write directly to a big array of RGB values.
So I prefer the method you mentioned.
But I’m a bit worried about the efficiency too. Because if I understand the documentation correctly, then the whole image may be copied twice. Once when getting the pointer and once when done with the pointer. Is this correct?

Ah, I just reread the docs and I see what you are referring to. I just looked at the JUCE code, and for all contexts except OpenGL, there is no copy done.

So when getting this data pointer and changing the image using this pointer, you can directly change the image pixel data? Without any copying involved? That would be nice.

Yes, that is my belief. I looked at the Image::BitmapData constructor, and it’s correpsonding call to the ImagePixelData::initialiseBitmapData function. In all of the implementations of initialiseBitmapData, only the implementation in modules\juce_opengl\opengl\juce_OpenGLImage.cpp does a copy. I encourage you to do this exercise so that you can see for yourself.

Being able to examine the underlying source code is a great feature of JUCE. I often don’t need to ask questons of the community/developers, because I can answer them myself. Either through direct examination of the code, or by stepping through things in the debugger.

Hello

I can confirm that if you know how to work with pointers, Image::BitmapData is the most efficient way to deal with your pixels.
You change directly the value of the pixels, thus no time wasted !

Well, if you want to handle bitmap data directly, which is indeed much faster, this is the code:

// Create an Image object somewhere
Image SRC = Image(Image::RGB, width, height, true);

// Get the BitmapData object
Image::BitmapData data(SRC, Image::BitmapData::writeOnly);

// Assuming you have an array of unsigned char type where bytes are in order RGB1 RGB2 RGB2 ... RGBn 
// Copy each pixel to the Image object
for (int y = 0; y < height; y++)
{
	for (int x = 0; x < width; x++)
	{
		auto R = *BMP++;
		auto G = *BMP++;
		auto B = *BMP++;
		//SRC.setPixelAt(x, y, Colour::fromRGB(R, G, B));
		
		// Copy pixels - This is of RGB type without alpha, otherwise you'd need to write the 4th byte for the alpha channel
		*data.data++ = R;
		*data.data++ = G;
		*data.data++ = B;
}

// Display the image in the paint() function
void paint(Graphics& g) override
{
	g.ImageAt(SRC, 0, 0);
}

Just check that the number of bytes matches between your array and the Image object, according to the Image type (RGB, ARGB or Single Channel).

Of course, if you don’t need to manipulate the RGB colors for each pixel, and just want to make a raw copy instead, you can use something like memcpy or Juce’s MemoryBlock functions or even reassign the pointer from *BMP to *data.data.

Pretty much what cpr2323 already said :joy: