I want to read pixels from a texture using SDL2 library.
I know that it's not possible to directly read a pixel from a texture, but you can use the following function:
int SDL_RenderReadPixels(SDL_Renderer* renderer,
const SDL_Rect* rect,
Uint32 format,
void* pixels,
int pitch)
Basically, what I want to do in my application is to read a single pixel from the window screen and detect if it's a transparent pixel or not.
How can I use that function?
What is the pitch attribute, how I define the SDL_Rect and how I get the pixel with the correct format?
I'm detecting the mouse left click event and passing the x and y coordinates to a method as you can see:
void detectElementOnXandY(SDL_Renderer* renderer, int x, int y){
// x represents the coordinate x.
// y represents the coordinate y.
// Here i have to call the function
int result = SDL_RenderReadPixels(renderer, rect, format, pixels, pitch);
}
The pitch seems to be the length of a scanline in bytes. So depending of the format of your texture : 1 * bytesPerPixel. (probably 4 bytes per pixel)
The rect should be something like (x,y,x+1,y+1)
and the format should be something like SDL_PIXELFORMAT_RGBA8888 or the equivalent for your particular texture.
You may have to inverse the y coordinate depending if SDL uses the OpenGL(bottom to top) convention or the Windows(top to bottom) convention.
Related
I'm using a library called Awesomium and it has the following function:
void Awesomium::BitmapSurface::CopyTo ( unsigned char * dest_buffer, // output
int dest_row_span, // input that I can select
int dest_depth, // input that I can select
bool convert_to_rgba, // input that I can select
bool flip_y // input that I can select
) const
Copy this bitmap to a certain destination. Will also set the dirty bit to False.
Parameters
dest_buffer A pointer to the destination pixel buffer.
dest_row_span The number of bytes per-row of the destination.
dest_depth The depth (number of bytes per pixel, is usually 4 for BGRA surfaces and 3 for BGR surfaces).
convert_to_rgba Whether or not we should convert BGRA to RGBA.
flip_y Whether or not we should invert the bitmap vertically.
This is great because it gives me an unsigned char * dest_buffer which contains raw bitmap data. I've been trying for several hours to convert this raw bitmap data into some sort of usable format that I can use in SDL but I'm having trouble. =[ Is there any way I can load it into a SDL texture or surface? It would be ideal to have examples for both but if I only get one example (either texture or surface), that is sufficient and I will be very grateful. :) I tried to use SDL_LoadBMP_RW but that crashed. I'm not even sure if I should be using that method.
SDL_LoadBMP_RW is for loading an image in the BMP file format. And it expects an SDL_RWops*, which is a file stream, not a pixel buffer. The function you want is SDL_CreateRGBSurfaceFrom. I believe this call should work for your purposes:
SDL_Surface* surface =
SDL_CreateRGBSurfaceFrom(
pixels, // dest_buffer from CopyTo
width, // in pixels
height, // in pixels
depth, // in bits, so should be dest_depth * 8
pitch, // dest_row_span from CopyTo
Rmask, // RGBA masks, see docs
Gmask,
Bmask,
Amask
);
I am currently reading image from a decoder and paint each frame of it in a widget.
This is what I am doing now:
paintEvent(...){
...
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setRenderHint(QPainter::HighQualityAntialiasing, false);
QPixmap pmap = QPixmap::fromImage(glImage).scaledToWidth(width());
painter.drawPixmap(0, (height() - pmap.height()) / 2, pmap);
...
}
However, I found it to be computationally expensive...
Is there any solution to this without using the OpenGL in Qt?
You could try to use QPainter::drawImage instead of doing manual conversion between image representation (QImage -> QPixmap). Refering to documentation it should still provide way to scale the image -"Note: The image is scaled to fit the rectangle, if both the image and rectangle size disagree.".
First of all, there is no need to scale your pixmap before painting. You can pass the desired width and height as an argument to painter.drawPixmap. This will scale the image while painting which is (probably) faster.
QPixmap pmap = QPixmap::fromImage(glImage);
int w = width();
// "scaledToWidth"
int h = w * pmap.height() / (double)pmap.width();
painter.drawPixmap(0, (height() - h) / 2, w, h, pmap);
Then, you could try to draw the image directly. Depending on which operating system you are using, this might be slower or faster.
On Windows, for example, QPixmap is internally represented by a QImage anyway. And therefore, QPixmap::fromImage will basically create a (possible unnecessary) copy of that image.
int w = width();
int h = w * glImage.height() / (double)glImage.width();
painter.drawImage(0, (height() - h) / 2, w, h, glImage);
If you draw the image directly, alpha blending can become quite expensive. So if possible, use a pixel format without alpha channel or with premultiplied alpha. (In the premultiplied format the red, green, and blue channels are multiplied by the alpha component divided by 255.) (See also: QImage::Format_ARGB32_Premultiplied is your friend).
Bonus fact: That's basically what QPixmap::fromImage on Windows does. If you pass a QImage with alpha channel to that function, the internal QImage will be stored with premultiplied alpha to optimize render performance. See source code.
I'm working on a NES emulator right now and I'm having trouble figuring out how to render the pixels. I am using a 3 dimensional array to hold the RGB value of each pixel. The array definition looks like this for the 256 x 224 screen size:
byte screenData[224][256][3];
For example, [0][0][0] holds the blue value, [0][0][1] holds the green values and [0][0][2] holds the red value of the pixel at screen position [0][0].
When the vblank flag goes high, I need to render the screen. When SDL goes to render the screen, the screenData array will be full of the RGB values for each pixel. I was able to find a function named SDL_CreateRGBSurfaceFrom that looked like it may work for what I want to do. However, all of the examples I have seen use 1 dimensional arrays for the RGB values and not a 3 dimensional array.
What would be the best way for me to render my pixels? It would also be nice if the function allowed me to resize the surface somehow so I didn't have to use a 256 x 224 window size.
You need to store the data as an unidimensional char array:
int channels = 3; // for a RGB image
char* pixels = new char[img_width * img_height * channels];
// populate pixels with real data ...
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom((void*)pixels,
img_width,
img_height,
channels * 8, // bits per pixel = 24
img_width * channels, // pitch
0x0000FF, // red mask
0x00FF00, // green mask
0xFF0000, // blue mask
0); // alpha mask (none)
In 2.0, use SDL_Texture + SDL_TEXTUREACCESS_STREAMING + SDL_RenderCopy, it's faster than SDL_RenderPoint.
See:
official example: http://hg.libsdl.org/SDL/file/e12c38730512/test/teststreaming.c
my derived example which does not require blob data and compares both methods: https://github.com/cirosantilli/cpp-cheat/blob/0607da1236030d2e1ec56256a0d12cadb6924a41/sdl/plot2d.c
Related: Why do I get bad performance with SDL2 and SDL_RenderCopy inside a double for loop over all pixels?
I have *.png files and I want to get different 8x8 px parts from textures and place them on bitmap (SDL_Surface, I guess, but maybe not), smth like this:
Now I'm rendering that without bitmap, i.e. I call each texture and draw part directly on screen each frame, and it's too slow. I guess I need to load each *.png to separate bitmap and use them passing video memory, then call just one big bitmap, but maybe I'm wrong. I need the fastest way of doing that, I need code of this (SDL 2, not SDL 1.3).
Also maybe I need to use clear OpenGL here?
Update:
Or maybe I need to load *.png's to int arrays somehow and use them just like usual numbers and place them to one big int array, and then convert it to SDL_Surface/SDL_Texture? It seems this is the best way, but how to write this?
Update 2:
Colors of pixels in each block are not the same as it presented at the picture and also can they be transparent. Picture is just an example.
Assumming you already have your bitmaps loaded up as SDL_Texture(s), composing them into a different texture is done via SDL_SetRenderTarget .
SDL_SetRenderTarget(renderer, target_texture);
SDL_RenderCopy(renderer, texture1, ...);
SDL_RenderCopy(renderer, texture2, ...);
...
SDL_SetRenderTarget(renderer, NULL);
Every render operation you perform between setting your Render Target and resetting it (by calling SDL_SetRenderTarget with a NULL texture parameter) will be renderer to the designated texture. You can then use this texture as you would use any other.
Ok so, when I asked about "solid colour", I meant - "in that 8x8 pixel area in the .png that you are copying from, do all 64 pixels have the same identical RGB value?" It looks that way in your diagram, so how about this:
How about creating an SDL_Surface, and directly painting 8x8 pixel areas of the memory pointed to by the pixels member of that SDL_Surface with the values read from the original .png.
And then when you're done, convert that surface to an SDL_Texture and render that?
You would avoid all the SDL_UpdateTexture() calls.
Anyway here is some example code. Let's say that you create a class called EightByEight.
class EightByEight
{
public:
EightByEight( SDL_Surface * pDest, Uint8 r, Uint8 g, Uint8 b):
m_pSurface(pDest),
m_red(r),
m_green(g),
m_blue(b){}
void BlitToSurface( int column, int row );
private:
SDL_Surface * m_pSurface;
Uint8 m_red;
Uint8 m_green;
Uint8 m_blue;
};
You construct an object of type EightByEight by passing it a pointer to an SDL_Surface and also some values for red, green and blue. This RGB corresponds to the RGB value taken from the particular 8x8 pixel area of the .png you are currently reading from. You will paint a particular 8x8 pixel area of the SDL_Surface pixels with this RGB value.
So now when you want to paint an area of the SDL_Surface, you use the function BlitToSurface() and pass in a column and row value. For example, if you divided the SDL_Surface into 8x8 pixel squares, BlitToSurface(3,5) means the paint the square at the 4th column, and 5th row with the RGB value that I set on construction.
The BlitToSurface() looks like this:
void EightByEight::BlitToSurface(int column, int row)
{
Uint32 * pixel = (Uint32*)m_pSurface->pixels+(row*(m_pSurface->pitch/4))+column;
// now pixel is pointing to the first pixel in the correct 8x8 pixel square
// of the Surface's pixel memory. Now you need to paint a 8 rows of 8 pixels,
// but be careful - you need to add m_pSurface->pitch - 8 each time
for(int y = 0; y < 8; y++)
{
// paint a row
for(int i = 0; i < 8; i++)
{
*pixel++ = SDL_MapRGB(m_pSurface->format, m_red, m_green, m_blue);
}
// advance pixel pointer by pitch-8, to get the next "row".
pixel += (m_pSurface->pitch - 8);
}
}
I'm sure you could probably speed things up further by pre-calculating an RGB value on construction. Or if you're reading a pixel from the texture, you could probably dispense with the SDL_MapRGB() (but it's just there in case the Surface has different pixel format to the .png).
memcpy is probably faster than 8 individual assignments to the RGB value - but I just want to demonstrate the technique. You could experiment.
So, all the EightByEight objects you create, all point to the same SDL_Surface.
And then, when you're done, you just convert that SDL_Surface to an SDL_Texture and blit that.
Thanks to everyone who took part, but we solved it with my friends. So here is an example (source code is too big and unnecessary here, I'll just describe the main idea):
int pitch, *pixels;
SDL_Texture *texture;
...
if (!SDL_LockTexture(texture, 0, (void **)&pixels, &pitch))
{
for (/*Conditions*/)
memcpy(/*Params*/);
SDL_UnlockTexture(texture);
}
SDL_RenderCopy(renderer, texture, 0, 0);
I'm making a rts game, and have created a 2d array containing the map tiles. I'd like to transfer this to an image (an unsigned int or a sdl surface?) and I would likely draw this onto a gl Quad. I'd likely just use 2 for loops to draw the entire map each frame. The problem is, I don't know the syntax of how to do this.
I'd like the map size to be flexible (probably always a square), and therefore the minimap has to also be flexible.
If I can find out the syntax of how to create an image from scratch (or understand how an unsigned int can be interpreted as an image?) and draw each pixel, this would completely resolve my issue.
You can first create a SDL_Surface using SDL_CreateRGBSurface( link has a tutorial ) with the desired height and width of the map.
SDL_Surface *map = SDL_CreateRGBSurface(Uint32 flags, int width, int height, int bitsPerPixel, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);
After you have the surface you can access the pixels of the surface with
map->pixels //pointer to the start of pixel data for map
When you want to resize the map, you create a new SDL_Surface with the new size and transform pixels using image scaling algorithms to it then delete the old surface and use the new one as the map