How can I modify pixels using SDL? - c++

I have a slight problem: I can't modify the pixels of an SDL screen.
Specifically, the following code doesn't work.
Uint32 * pixels = (Uint32 *) screen -> pixels;
screen -> pixels = pixels;
This compiles, but it doesn't show anything. What am I missing?

I had the following functions lying around for setting pixels in an SDL_Surface. There are two versions each for 32-bit, 24-bit, 16-bit and 8-bit surfaces. If you just want to set a single pixel, you would use the normal versions. But if you want to set a bunch of pixels, first you lock the surface, then you use the nolock version(named so because it does not lock the surface), then you unlock. This way you aren't repeatedly locking and unlocking the surface, which is supposed to be an expensive operation, though I don't think I ever actually tested it.
void PutPixel32_nolock(SDL_Surface * surface, int x, int y, Uint32 color)
{
Uint8 * pixel = (Uint8*)surface->pixels;
pixel += (y * surface->pitch) + (x * sizeof(Uint32));
*((Uint32*)pixel) = color;
}
void PutPixel24_nolock(SDL_Surface * surface, int x, int y, Uint32 color)
{
Uint8 * pixel = (Uint8*)surface->pixels;
pixel += (y * surface->pitch) + (x * sizeof(Uint8) * 3);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
pixel[0] = (color >> 24) & 0xFF;
pixel[1] = (color >> 16) & 0xFF;
pixel[2] = (color >> 8) & 0xFF;
#else
pixel[0] = color & 0xFF;
pixel[1] = (color >> 8) & 0xFF;
pixel[2] = (color >> 16) & 0xFF;
#endif
}
void PutPixel16_nolock(SDL_Surface * surface, int x, int y, Uint32 color)
{
Uint8 * pixel = (Uint8*)surface->pixels;
pixel += (y * surface->pitch) + (x * sizeof(Uint16));
*((Uint16*)pixel) = color & 0xFFFF;
}
void PutPixel8_nolock(SDL_Surface * surface, int x, int y, Uint32 color)
{
Uint8 * pixel = (Uint8*)surface->pixels;
pixel += (y * surface->pitch) + (x * sizeof(Uint8));
*pixel = color & 0xFF;
}
void PutPixel32(SDL_Surface * surface, int x, int y, Uint32 color)
{
if( SDL_MUSTLOCK(surface) )
SDL_LockSurface(surface);
PutPixel32_nolock(surface, x, y, color);
if( SDL_MUSTLOCK(surface) )
SDL_UnlockSurface(surface);
}
void PutPixel24(SDL_Surface * surface, int x, int y, Uint32 color)
{
if( SDL_MUSTLOCK(surface) )
SDL_LockSurface(surface);
PutPixel24_nolock(surface, x, y, color);
if( SDL_MUSTLOCK(surface) )
SDL_LockSurface(surface);
}
void PutPixel16(SDL_Surface * surface, int x, int y, Uint32 color)
{
if( SDL_MUSTLOCK(surface) )
SDL_LockSurface(surface);
PutPixel16_nolock(surface, x, y, color);
if( SDL_MUSTLOCK(surface) )
SDL_UnlockSurface(surface);
}
void PutPixel8(SDL_Surface * surface, int x, int y, Uint32 color)
{
if( SDL_MUSTLOCK(surface) )
SDL_LockSurface(surface);
PutPixel8_nolock(surface, x, y, color);
if( SDL_MUSTLOCK(surface) )
SDL_UnlockSurface(surface);
}

Manipulating the contents of screen->pixels will modify pixels, with a couple of caveats.
First, as you've shown in the code snippet, note that screen->pixels is a pointer to the pixel data of the surface. The pixel data itself is accessed as a linear array from that pointer based on the width of the surface (surface->pitch) and the size of the pixel in bytes.
The pixel size (aka depth) is set during initialisation, using SDL_SetVideoMode() and can be found in screen->format->BytesPerPixel.
Locking of the surface before making changes may be necessary.
In addition, depending on the options that were passed to SDL_SetVideoMode() you may also need to call SDL_Flip() to display the changes that you've made.
A working example of pixel manipulation can be found here.
As has been pointed out in the comments, the code listed in the question is not actually going to do anything visible as no changes are being made to the pixel data.

Adding an SDL2 variant manipulating pixels not on a surface but in a renderer (and which does not crash if you try to manipulate pixels outside of your screen, unlike previous answers)
void putPixelRGB(SDL_Renderer* renderer, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
SDL_SetRenderDrawColor(renderer, (Uint8)r, (Uint8)g, (Uint8)b, 255);
SDL_RenderDrawPoint(renderer, x, y);
}

You must not modify the contents of the SDL_Surface structure. If you want to copy the pixels you should malloc() some memory and then memcpy() the pixels.

why change a pixel?
make a new surface & a Rect
// CODE ------------>
SDL_Surface *screen, *PIXEL = NULL;
SDL_Rect PIXELRect;
PIXELRect.h=5;
PIXELRect.w=5;
PIXELRect.x=100;
PIXELRect.y=100;
screen = SDL_SetVideoMode(640, 468, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_ANYFORMAT);
while(running){
SDL_FillRect(screen, &PIXELRect, (#color));
}

Related

SDL2 function to draw a filled circle

I am looking for a function that draws a filled circle using SDL2 without using a renderer at all. I currently have this:
void Circle(int center_x, int center_y, int radius, SDL_Color color) {
eraseOldCircle();
uint32_t *pixels = (uint32_t *) windowSurface->pixels;
SDL_PixelFormat *windowFormat = windowSurface->format;
SDL_LockSurface(windowSurface); // Lock surface for direct pixel access capability
int radiussqrd = radius * radius;
for(int x=center_x-radius; x<=center_x+radius; x++) {
int dx = center_x - x;
for(int y=center_y-radius; y<=center_y+radius; y++) {
int dy = center_y - y;
if((dy * dy + dx * dx) <= radiussqrd) {
pixels[(y * WIDTH + x)] = SDL_MapRGB(windowFormat, color.r, color.g, color.b);
}
}
}
SDL_UnlockSurface(windowSurface);
SDL_UpdateWindowSurface(window);
}
which has been adapted from another function I found here, it draws the pixels directly to the windowSurface after calling eraseOldCircle (which puts the game's background image back to the previous position of the circle, effectively erasing it from there.) but it is still too slow for what I need (probably the maths?). What would be the fastest way to draw a circle using direct pixel access? I need it to be high speed so I can use it in a 2D game. I haven't been able to find anything until now, everything I see uses SDL_Renderer, but I should strictly never use it.
Here is eraseOldCircle() in case it helps:
void eraseOldCircle() {
//Calculate previous position of ball
SDL_Rect pos = {circlePosition.x-(radius+steps), circlePosition.y-(radius+steps), radius*radius, radius*2+steps};
SDL_BlitSurface(backgroundImage, &pos, windowSurface, &pos);
}
I'm not too sure how to do it with surfaces and memory management and all that, but if this helps, here is a version using an SDL_Renderer that runs pretty quickly:
void draw_circle(SDL_Renderer *renderer, int x, int y, int radius, SDL_Color color)
{
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
for (int w = 0; w < radius * 2; w++)
{
for (int h = 0; h < radius * 2; h++)
{
int dx = radius - w; // horizontal offset
int dy = radius - h; // vertical offset
if ((dx*dx + dy*dy) <= (radius * radius))
{
SDL_RenderDrawPoint(renderer, x + dx, y + dy);
}
}
}
}
If you draw many circles, I would guess SDL_UpdateWindowSurface is where you spend the most time. Try this instead
SDL_LockSurface
// erase and draw all circles (possibly >1000)
SDL_UnlockSurface
SDL_UpdateWindowSurface
You can optimize your circle drawing code a bit, but it is probably fast enough. I also think that SDL_Renderer is probably fast enough.
The documentation for SDL_UpdateWindowSurface says it will copy the surface to the screen. You only need to do this once per frame.

OpenGL heightmap terrain and normals with low resolution

I'm did a basic terrain generation with a heightmap images in openGL, work fine, but the terrain vertices and normals are too low resolution, as in the follow images:
Image of the normals
Image of the vertices in terrain
The terrain mesh generation doesn't metter here, it's just a basic triangles generation, so let's go to the normals and heightmap generation part:
//Calculate the height of the vertex in the terrain
int Terrain::GetPixelHeight(unsigned char* data, int imageWidth, int x, int y)
{
if(x < 0 || x >= 256 || y < 0 || y >= 256) //If coordinates passes the image limit, just return 0
return 0;
else
{
unsigned char* pixelOffset = data + (x + imageWidth * y) * 1; //Get the image
unsigned char r = pixelOffset[0]; //Get R value
unsigned char g = pixelOffset[1]; //Get G value
unsigned char b = pixelOffset[2]; //Get B value
float height = (float)r + (float)g + (float)b; //Put values together to calculate the height
return height / 40; //Return the height with smooth in 40
}
}
Now the normals calculation:
//Calculate the normals based in the terrain height
float heightL = GetPixelHeight(data, y, x-1, z);
float heightR = GetPixelHeight(data, y, x+1, z);
float heightD = GetPixelHeight(data, y, x, z-1);
float heightU = GetPixelHeight(data, y, x, z+1);
glm::vec3 normalVector = glm::normalize(glm::vec3(heightL - heightR, 2.0f, heightD - heightU));
normals.push_back(normalVector.x); normals.push_back(normalVector.y); normals.push_back(normalVector.z);
Shaders are basic too, I can guarantee there are no problems with them, because is the same shaders of the others objects in game, and this objects doesn't have problems with light calculation, so i'm think the problem is the normals calculation with the heightmap.
PS: Terrain size is the same of the image, 256x256, the image just have black and white channels.

Draw a curved line from an arc edge

Here's the screenshot of what I am doing. Currently, I'm stuck from drawing a curved borders into this rectangle.
My first solution was: draw a quartered circle behind the rectangle, but if I adjust the opacity of the shape, as you can see, the quartered circle gets shown.
I know this is pretty basic for you guys but I'm not really good at math.
I did try to reuse the computed edges of the arc and add the size of border but I got this as a result.
I also think of bezier curves as a replacement but I think it is more efficient to just reuse the computed vertices and add all the missing ones. Also, I don't know how to compute for the curved points of bezier curves and finding the right amount of t would be very computationally expensive so I don't implement it.
Here's the code how I draw the inner quartered circle and I think I can just reuse it.
void drawArc(int x, int y,
int startAngle, int endAngle,
uint32_t radiusX, uint32_t radiusY,
int border_x, int border_y,
const rgb color,
const rgb bcX, const rgb bcY,
uint8_t opacity)
{
if (radiusX <= 0 || radiusY <= 0) return;
static constexpr float DTR = 3.14159 / 180;
float cx, cy;
int step;
static std::vector<float> verts;
static std::vector<uint8_t> colors;
if (startAngle < endAngle)
{
step = +1;
++ endAngle;
} else
{
step = -1;
-- endAngle;
}
verts.clear();
colors.clear();
verts.push_back(x);
verts.push_back(y);
colors.push_back(color[R]);
colors.push_back(color[G]);
colors.push_back(color[B]);
colors.push_back(opacity);
while (startAngle != endAngle)
{
cx = cos(DTR * startAngle) * radiusX;
cy = sin(DTR * startAngle) * radiusY;
verts.push_back(x + cx);
verts.push_back(y - cy);
colors.push_back(color[R]);
colors.push_back(color[G]);
colors.push_back(color[B]);
colors.push_back(opacity);
startAngle += step;
}
drawElements(GL_POLYGON, sizeof(arcIndices) / sizeof(arcIndices[0]), GL_FLOAT,
&verts[0], &colors[0], &arcIndices[0]);
if (border_x != 0 || border_y != 0)
{
//remove (x, y)
verts.erase(verts.begin(), verts.begin() + 2);
// float px, py;
//
// px = *(verts.begin() + 0);
// py = *(verts.begin() + 1);
//
// glPointSize(5);
//
// glBegin(GL_POINTS);
//
// glColor3ub(0,0,255);
// glVertex2i(px, py);
//
// px = *(verts.end() - 2);
// py = *(verts.end() - 1);
//
// glColor3ub(255,0,0);
// glVertex2i(px , py);
// glEnd();
//attempting to reuse the edges
//I think the last vertices are opposed
//that's why I got a crossed out lines??
for (int i = 0;i <= 90; ++i)
{
verts.push_back(verts[i + 0] + border_x);
verts.push_back(verts[i + 1] + border_y);
colors.push_back(bcX[R]);
colors.push_back(bcX[G]);
colors.push_back(bcX[B]);
colors.push_back(opacity);
}
//91 = steps from 0-90 degree revolution
//182 = 91 * 2
unsigned int index[182 + 91 * 2];
for (int i = 0;i < 182 + 91 * 2; ++i)
index[i] = i;
drawElements(GL_LINE_LOOP, verts.size() / 2, GL_FLOAT,
&verts[0], &colors[0], &index[0]);
}
}
Edit:
Can't I just reuse the pre-calculated (x,y) before?
Sorry for too much use of pictures
The red dots are pre-calculated (x, y) I'm referring to and just append the next arc base on this.
I'm gonna render many of this kind so I need as efficient as possible(w/o too much use to trigo functions).
Update:
And here is the result I got from using stencil buffer as what Andon M. Coleman suggested:
Btw, as you can see, I am trying to emulate my own UI using OpenGL :D
You expressed an interest in seeing how this could be solved using the stencil buffer yesterday, so I am following up with some basic pseudo-code.
glClearStencil (0x0);
glClear (GL_STENCIL_BUFFER_BIT);
glEnable (GL_STENCIL_TEST);
glStencilFunc (GL_ALWAYS, 0x0, 0x0);
// Add 1 to stencil buffer at every location the object to be bordered is visible
glStencilOp (GL_KEEP, GL_KEEP, GL_INCR);
// Draw your grey object
// Only draw the red border where the grey object was never drawn (stencil = 0x0)
glStencilFunc (GL_EQUAL, 0x0, 0xff);
// Draw your red quarter circles
glDisable (GL_STENCIL_TEST);
Clearing the stencil buffer everytime you draw your outlined object is probably overkill. If you opt to clear the stencil buffer once per-frame instead, you can do some pretty interesting things. For instance, if you drew the outlines as a separate pass after all non-outlined shapes are drawn you could use this stencil buffer setup to outline the union (instead of including the intersection of objects as part of the drawn outline) of any overlapping objects.. this would allow you to construct more complicated shapes from your simple rounded rectangles.
Of course for this to work, your pixel format must have a stencil buffer. I will have to leave that part up to you, because the process of setting that up is implementation specific.
GL_POLYGON is only for convex polygons.
Link together the vertices on your inner and outer radii to form quads/triangles:
#include <GL/glut.h>
#include <cmath>
void Torus2d
(
float angle, // starting angle in radians
float length, // length of arc in radians, >0
float radius, // inner radius, >0
float width, // width of torus, >0
unsigned int samples // number of circle samples, >=3
)
{
if( samples < 3 ) samples = 3;
const float outer = radius + width;
glBegin( GL_QUAD_STRIP );
for( unsigned int i = 0; i <= samples; ++i )
{
float a = angle + ( i / (float)samples ) * length;
glVertex2f( radius * cos( a ), radius * sin( a ) );
glVertex2f( outer * cos( a ), outer * sin( a ) );
}
glEnd();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -4 * ar, 4 * ar, -4, 4, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
Torus2d( 0, 1.57079633, 2, 1, 20 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}

SDL_Texture opacity

How can I change opacity of the SDL_Texture? And I don't know how to apply the opacity number in my function.
My code
void drawTexture(SDL_Texture *img, int x, int y, int width, int height, double opacity)
{
SDL_Rect SrcR;
SDL_Rect DestR;
SrcR.x = 0;
SrcR.y = 0;
SrcR.w = width;
SrcR.h = height;
DestR.x = x;
DestR.y = y;
DestR.w = width;
DestR.h = height;
SDL_RenderCopy(_main::_main_renderer, img, &SrcR, &DestR);
}
Use SDL_SetTextureAlphaMod:
SDL_SetTextureAlphaMod(img, opacity);
This will set the opacity (alpha) of the texture, the alpha value must be a Uint8 from 0 (totally transparent aka invisible) to 255 (fully opaque).
Opening question doesn't have info on the origin of img texture so the chosen answer it not correct it the texture is created from raw pixel data that doesn't have Alpha, i.e. using this:
SDL_UpdateTexture(img, NULL, pixels, pitch);
If pixels contains raw pixel data without alpha, i.e. ARGB with A 0x00, even if you do this
SDL_UpdateTexture(img, NULL, pixels, pitch);
SDL_SetTextureAlphaMod(img, opacity);
you will not see the texture (in this case alpha is 0x00) or you'll see garbage

get and set color of pixel

I need to set color for pixel.
When I try to set color of a certain pixel(by clicking left mouse button). My mouse function.
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
pixel *p = new pixel();
p->x = x;
p->y = HEIGHT - y;
stack.push(p);
float arr[3];
readPixel(p->x, p->y, arr);
std::cout<<"pixel color: ";
std::cout<<arr[0]<<" "<<arr[1]<<" "<<arr[2]<<std::endl;
drawPixel(p->x, p->y);
}
}
Here readPixel method
void readPixel(int x, int y, float (&a)[3]) {
GLubyte arr[3];
glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, arr);
a[0] = (float)arr[0] / 255.0f;
a[1] = (float)arr[1] / 255.0f;
a[2] = (float)arr[2] / 255.0f;
};
The problem is with setting color for pixel.
I create structure pixel with fields x and y. When I click left button, object pixel is added to stack.
When I try to set color for pixel(draw it) - pixel does not change its color in method drawPixel
void draw() {
glBegin(GL_POINTS);
if (!stack.empty()) {
drawPixel(stack.top()->x, stack.top()->y);
stack.pop();
}
glEnd();
glFlush();
};
void drawPixel(int x, int y) {
glRasterPos2i(x, y);
glDrawPixels(1, 1, GL_RGB, GL_UNSIGNED_BYTE, &val);
};
Where &val is float val[3] = { 1.0, 1.0, 0.0 };
So the question is how to set color for pixel with coordinates x and y?
The solution is to change GL_UNSIGNED_BYTE to GL_FLOAT and not pop items from stack