1
\$\begingroup\$

Alright, I have spent a lot of time on this one, so I need some help. I like the ability to draw pixels directly to the screen, sometimes I do this to trace the path of objects for debugging etc. In SDL 1.2 you could do something sort of like this :

//make screen
SDL_Surface * screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );    
//make surface
SDL_Surface * pSurface = SDL_CreateRGBSurface( SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
//get the pixels
Uint32 * pixels = (Uint32 *)pSurface->pixels;
//set a pixel to red
pixels[ x + ( y * pSurface->w ) ] = SDL_MapRGB(pSurface->format, 255, 0, 0);
//blit to screen
SDL_Rect offset = {0,0,0,0};
SDL_BlitSurface( pSurface, NULL, screen, &offset );

This would result in a red pixel being displayed, you could make a simple line across the screen with a loop. Now SDL2 is really blowing my mind at the moment, here is what I've got:

//setup
window = SDL_CreateWindow("test",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_WIDTH, SCREEN_HEIGHT,SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(renderer, 32, 32, 32, 255);
//make surface and texture
SDL_Surface * pSurface = SDL_CreateRGBSurface( 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
SDL_Texture * pTexture = SDL_CreateTexture( renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_STREAMING,SCREEN_WIDTH,SCREEN_HEIGHT);
//from texture into surface
SDL_LockTexture(pTexture, NULL, &pSurface->pixels, &pSurface->pitch);
//get the pixels
Uint32 * pixels = (Uint32 *)pSurface->pixels;
//set a pixel to red
pixels[ x + ( y * pSurface->w ) ] = SDL_MapRGB(pSurface->format, 255, 0, 0);
//surface back to texture?
SDL_UnlockTexture(pTexture);
//clear the screen, then copy the texture to framebuffer, then update
SDL_RenderClear( renderer );
SDL_Rect renderQuad = {0,0,0,0};
SDL_RenderCopy( renderer, source, clip, &renderQuad );
SDL_RenderPresent(renderer);

This does nothing, except to fill the screen with the RenderDrawColor. What am I doing wrong..?

\$\endgroup\$
3
  • \$\begingroup\$ Not using OpenGL, that's what you are doing wrong ;) SDL may be cool but it's spiritual successor needs to go home, it's drunk. \$\endgroup\$
    – MickLH
    Oct 16, 2013 at 17:06
  • 1
    \$\begingroup\$ This question appears to be off-topic because it is about debugging your code for you. \$\endgroup\$
    – Anko
    Oct 16, 2013 at 17:56
  • \$\begingroup\$ You're not rendering your texture in your render calls; in SDL_RenderCopy you are rendering source (where is that from?), whereas you were changing the pixel on the pTexture texture. \$\endgroup\$ Jan 2, 2014 at 5:20

4 Answers 4

4
\$\begingroup\$

If you use the SDL2 Renderer, say goodbye to the idea of accessing pixels directly. Of course, it is possible but everytime you do this you "download" the pixel from your graphics card and then have to push it up again.

I suggest that you do all pixel manipulation before in a SDL_Surface and load the result as texture into you graphics card OR use OpenGL complete.

\$\endgroup\$
0
\$\begingroup\$

You should read the SDL 1.2 to 2.0 Migration Guide, you still can access pixels but they should be in System memory surface as seem you are trying to do. But, Why you are locking the texture? You shoudn't access texture memory directly, in general accessing GPU memory has low performance. You should work with SDL_Surface all time then use SDL_UpdateTexture(sdlTexture, NULL, pSurface->pixels, pSurface->pitch); when your drawing is done, and finally call SDL_RenderCopy(renderer, pTexture, NULL, NULL);

Read the section "If your game wants to do both" in the Migration Guide.

\$\endgroup\$
0
\$\begingroup\$

The problem is in a way you use SDL_LockTexture(). You use it completely wrongly, you somehow assume you can pass a surface buffer there, which is not the case. Instead, this function fills the passed pointer to point to its own internal buffer, and as the result, the surface's pointer is being messed up in your code.

As was already pointed, you could use SDL_UpdateTexture() to copy from surface to texture, but for this you need SDL_TEXTUREACCESS_STATIC, while your code uses SDL_TEXTUREACCESS_STREAMING. For this texture type the surface is not needed at all, as you can draw directly the the streaming texture, which is also much faster than messing with the drawing surface:

void *pixels;
int pitch;
SDL_PixelFormat *fmt;
Uint32 format = SDL_GetWindowPixelFormat(window);
fmt = SDL_AllocFormat(format);
SDL_LockTexture(pTexture, NULL, &pixels, &pitch);
((Uint32*)pixels)[ x + ( y * SCREEN_WIDTH ) ] = SDL_MapRGB(fmt, 255, 0, 0);
SDL_UnlockTexture(pTexture);
SDL_FreeFormat(fmt);
\$\endgroup\$
0
\$\begingroup\$

There's more than one way to manipulate pixels, lines, rectangles, etc. with SDL2 and one of the easiest, and not so fast, ways to do that is to use texture and renderer.

You need to create renderer and then texture with access SDL_TEXTUREACCESS_TARGET. Once that's done, you can use renderer to draw pixels and other (supported) graphics primitives, like this:

// app window size
int width = 800;
int height = 600;
SDL_Window *window = SDL_CreateWindow( "Window Title", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL );
SDL_Renderer *renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );
SDL_Texture *DrawingTexture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height );

// clear all pixels in the drawing texture with given color
SDL_Color color = {0, 0, 0, 0}; // black
SDL_SetRenderTarget(renderer, DrawingTexture);
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, NULL);

// draw one pixel
SDL_SetRenderTarget(renderer, DrawingTexture);
SDL_Color pcolor = {0, 255, 0, 0};
SDL_SetRenderDrawColor(renderer, pcolor.r, pcolor.g, pcolor.b, pcolor.a);
SDL_RenderDrawPoint(renderer, x, y);
// this sets renderer back to default target (the window)
SDL_SetRenderTarget(renderer, NULL);

// draw multiple pixels: (100, 110), (10, 50), (14, 22)
SDL_SetRenderTarget(renderer, DrawingTexture);
SDL_SetRenderDrawColor(renderer, pcolor.r, pcolor.g, pcolor.b, pcolor.a);
SDL_Point points[3] = {{100, 110}, {10, 50}, {14, 22}};
SDL_RenderDrawPoints(renderer, points, 3);
SDL_SetRenderTarget(renderer, NULL);

Of course, you should call SDL_SetRenderTarget() once and then use SDL_SetRenderDrawColor() and SDL_RenderDrawXXX() function(s) as many times as needed, followed at the end with call to SDL_SetRenderTarget(renderer, NULL).

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .