Here is my code, I am following lazyfoos tutorials on SDL, here is the exact tutorial I am following - http://lazyfoo.net/tutorials/SDL/08_geometry_rendering/index.php
Notice the call to SDL_SetRenderDrawColor. We're using 255 red and 255
green which combine together to make yellow. Remember that call to
SDL_SetRenderDrawColor at the top of the loop? If that wasn't there,
the screen would be cleared with whatever color was last set with
SDL_SetRenderDrawColor, resulting in a yellow background in this case.
Lazyfoo does explain it above but it still doesn't make sense to me.
To draw a filled rectangle to the screen is a pretty trivial task but it's a task that sometimes causes a lot of confusion,for example you need to call SDL_SetRenderDrawColor() not once but twice,once before you clear the renderer,and also once before you call SDL_RenderFillRect().
Why do you need to call SDL_SetRenderDrawColor() twice and why in that order? I noticed if I comment out the first SDL_SetRenderDrawColor() just before the call to SDL_RenderFillRect(),the full window will be the colour you set the rectangle to be,but when you include the two calls to SDL_SetRenderDrawColor() in the order I specified the window shows a coloured rectangle in the centre of the screen with the rest of the screen being white(first SDL_SetRenderDrawColor() call).
Here is my game loop where the calls are made.
while( !quit )
{
while( SDL_PollEvent( &event ) != 0 )
{
if( event.type == SDL_QUIT )
{
quit = true;
}
}
SDL_SetRenderDrawColor( renderer, 255, 255, 255, 0 ); // line of code in question
SDL_RenderClear( renderer );
SDL_Rect fillRect = { 500 / 4, 500 / 4, 500 / 2, 500 / 2 };
SDL_SetRenderDrawColor( renderer, 0x00, 0xFF, 0x00, 0xFF ); // 2nd line of code in question
SDL_RenderFillRect( renderer, &fillRect );
SDL_RenderPresent( renderer );
}
Why do you need to call SDL_SetRenderDrawColor() twice and why in that
order?
The name of SDL_RenderClear is a bit misleading. It doesn't clear the screen to "empty" or anything - it just fills it with whatever color was set by SDL_SetRenderDrawColor. So if you don't change the color between "clearing" and drawing the rectangle, then you won't see the rectangle because you're drawing it with the same color that you just filled the entire screen with.
So here
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
You make the whole screen white. We do this by setting white, and then painting over the entire screen with white.
Then here
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0x00, 0x00, 0xFF );
We set red so the rectangle here
SDL_RenderFillRect( gRenderer, &fillRect );
Will be red (not white).
And if I remember the tutorial correctly, it also draws a line and some other things, every time calling SDL_SetRenderDrawColor right before to set the correct color.
I noticed if I comment out the first SDL_SetRenderDrawColor() just
before the call to SDL_RenderFillRect(),the full window will be the
colour you set the rectangle to be
Very good observation! You see, since you're looping (while(!quit){) you do SDL_RenderClear and then SDL_RenderFillRect... but then SDL_RenderClear comes again, and so on. So when SDL_RenderClear happens, the color was actually set from right before SDL_RenderFillRect in the last run through the loop. Hence why it has that color, too.
So actually, I don't know what the color is at the very first time because it's not set yet (might be a default value of white, or something), but we probably can't see it because that's just on the first run through the loop anyway. So what happens roughly is:
...
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor( renderer, 0x00, 0xFF, 0x00, 0xFF );
SDL_RenderFillRect( renderer, &fillRect );
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor( renderer, 0x00, 0xFF, 0x00, 0xFF );
SDL_RenderFillRect( renderer, &fillRect );
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor( renderer, 0x00, 0xFF, 0x00, 0xFF );
SDL_RenderFillRect( renderer, &fillRect );
...
So you see, with only that second SDL_SetRenderDrawColor call, both SDL_RenderClear and SDL_RenderFillRect will always draw in green, except the very first SDL_RenderClear call in the first frame.
Related
I've written a program using C++ and SDL2 which:
creates a window
gets the window's surface
creates a renderer for the window
renders some filled rectangles onto the window
creates a texture from the window's surface
clears the screen
renders some filled circles onto the window
creates a second texture from the window's surface
enters an event loop, where every time a key is pressed:
if circles are currently being displayed, SDL_RenderCopy() is used to copy the squares texture to the window.
else if squares are currently being displayed, the circles texture is copied to the window.
The program works perfectly if the renderer is created with the SDL_RENDERER_SOFTWARE flag.
If the renderer is created with the SDL_RENDERER_ACCELERATED flag, I find that while I can render direct to the screen, if I create a couple of different textures from the window's surface and then try to copy them back to the window using SDL_RenderCopy(); all I see is a black window.
I can't find any failing SDL calls.
I've wondered whether there might be some incompatibility between the texture format and the renderer - but I'm not sure how to follow this up.
Any help or suggestions?
My environment is :
Windows 10
Visual Studio Community 2015
SDL2 version 2.0.4
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Additional information and source code :
I've added cut down source code to demonstrate the problem below.
Note, to keep down the size, I've removed all of the error checking and consolidated the relevant code into a single main function.
What I get is that the program works for me as expected if I uncomment line 40 so that I am calling SDL_CreateRenderer with the SDL_RENDERER_SOFTWARE flag.
If I un-comment any of the other SDL_CreateRenderer lines instead (line 41-43 : to use hardware acceleration), I see the red and blue squares when they are initially rendered to screen.
But as I press keys, instead of the window flicking between red and blue squares, I'm looking at a black window.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string>
//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
int main(int argc, char* args[])
{
//The window we'll be rendering to
SDL_Window* gWindow = NULL;
//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;
//And two textures, one for a red square, on for a blue square
SDL_Texture* texture_red = NULL;
SDL_Texture* texture_blue = NULL;
//The window renderer
SDL_Renderer* gRenderer = NULL;
//Initialize SDL
SDL_Init(SDL_INIT_VIDEO);
//Create window
gWindow = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
//Get the screen surface
gScreenSurface = SDL_GetWindowSurface(gWindow);
//Create renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_SOFTWARE);
//gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
//gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_PRESENTVSYNC);
//gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
/*###########################################################################################
# I can not figure out how to make this program work with hardware acceleration. It works #
# fine when I define the renderer using SDL_RENDERER_SOFTWARE, but doesn't display anything #
# if I define the renerer using the SDL_RENDERER_ACCELERATED flag #
###########################################################################################*/
//Initialize renderer color
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
//Clear screen
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(gRenderer);
//Render red filled quad
SDL_Rect fillRect = { 100, 75, 100, 100 };
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(gRenderer, &fillRect);
//Update the rendered image on screen
SDL_RenderPresent(gRenderer);
//Pause long enough to see it
SDL_Delay(200);
//Create texture_red texture from the screen surface
texture_red = SDL_CreateTextureFromSurface(gRenderer, gScreenSurface);
//Clear screen
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(gRenderer);
//Render blue filled quad
fillRect = { 225, 250, 100, 100 };
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0xFF, 0xFF);
SDL_RenderFillRect(gRenderer, &fillRect);
//Update the rendered image on screen
SDL_RenderPresent(gRenderer);
//Pause long enough to see it
SDL_Delay(200);
//Create texture_red texture from the screen surface
texture_blue = SDL_CreateTextureFromSurface(gRenderer, gScreenSurface);
//Main loop flag
bool quit = false;
//Flag to keep track of which colour we're currently looking at
bool blue = true;
//Event handler
SDL_Event e;
//While application is running
while (!quit)
{
//Handle events on queue
while (SDL_PollEvent(&e) != 0)
{
//User requests quit
if (e.type == SDL_QUIT)
{
quit = true;
}
//User presses a key
else if (e.type == SDL_KEYDOWN)
{
//Select surfaces based on key press
switch (e.key.keysym.sym)
{
case SDLK_ESCAPE:
quit = true;
break;
default:
if (blue)
{
//Copy surface used to store red image onto the screen surface
SDL_RenderCopy(gRenderer, texture_red, NULL, NULL);
//Update current colour flag
blue = false;
}
else
{
//Copy surface used to store blue image onto the screen surface
SDL_RenderCopy(gRenderer, texture_blue, NULL, NULL);
//Update current colour flag
blue = true;
}
//Update the screen with recent render activity
SDL_RenderPresent(gRenderer);
break;
}
}
}
}
//Deallocate surfaces
SDL_FreeSurface(gScreenSurface);
//Destroy window
SDL_DestroyWindow(gWindow);
gWindow = NULL;
//Quit SDL subsystems
SDL_Quit();
return 0;
}
The SDL_GetWindowSurface() header comment specifically prohibits using it with the SDL_Renderer functionality:
/**
* \brief Get the SDL surface associated with the window.
*
* \return The window's framebuffer surface, or NULL on error.
*
* A new surface will be created with the optimal format for the window,
* if necessary. This surface will be freed when the window is destroyed.
*
* \note You may not combine this with 3D or the rendering API on this window.
*
* \sa SDL_UpdateWindowSurface()
* \sa SDL_UpdateWindowSurfaceRects()
*/
extern DECLSPEC SDL_Surface * SDLCALL SDL_GetWindowSurface(SDL_Window * window);
Use SDL_RENDERER_TARGETTEXTURE & SDL_SetRenderTarget() if you want to capture SDL_Renderer output.
You can use SDL_GetRendererInfo() to query a compatible texture format. Or just blast ahead with SDL_PIXELFORMAT_ARGB8888 like testrendertarget.c does and hope for the best :)
Try adding SDL_RENDERER_PRESENTVSYNC to your renderer's flag (Proposed by someone here).
C++ seems to be the programming language you're using but for those looking for a fully working SDL2.0.8-based C program that does not use GetWindowSurface(), please take a look here:
Mainly,
initWindowAndRenderer
drawText
drawImage
drawVideo
setBgColor
drawVideo
...
Here is an extract with error-checking removed:
// Create a window usable with OpenGL context
window = SDL_CreateWindow("Title", x, y, w, h, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN);
// Select render driver
// - A render driver that supports HW acceleration is used when available
// - Otherwise a render driver supporting software fallback is selected
SDL_RendererInfo renderDriverInfo;
uint32_t rendererFlags = SDL_RENDERER_TARGETTEXTURE;
int32_t nbRenderDrivers = SDL_GetNumRenderDrivers(), index = 0;
while (index < nbRenderDrivers)
{
if (SDL_GetRenderDriverInfo(index, &renderDriverInfo) == 0)
{
if (((renderDriverInfo.flags & rendererFlags) == rendererFlags)
&& ((renderDriverInfo.flags & SDL_RENDERER_ACCELERATED) == SDL_RENDERER_ACCELERATED))
{
// Using render driver with HW acceleration
rendererFlags |= SDL_RENDERER_ACCELERATED;
SDL_SetHint(SDL_HINT_RENDER_DRIVER, renderDriverInfo.name);
break;
}
}
++index;
}
if (index == nbRenderDrivers)
{
// Let SDL use the first render driver supporting software fallback
rendererFlags |= SDL_RENDERER_SOFTWARE;
index = -1;
}
// Create renderer
renderer = SDL_CreateRenderer(window, index, rendererFlags);
I try to do it by using SDL_RenderCopy()
But I just get a black box.
This is my code.
static SDL_Texture* GetAreaTextrue(SDL_Rect rect, SDL_Renderer* renderer, SDL_Texture* source)
{
SDL_Texture* result = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect.w, rect.h);
SDL_SetRenderTarget(renderer, result);
SDL_RenderCopy(renderer, source, &rect, NULL);
SDL_RenderPresent(renderer);
return result;
}
What is the proper operation?
EDIT:
What you want to do here is render a part of the texture to the screen.
There is a way to do this by using SDL_RenderCopy, but by doing this you just "take" the part you want from your texture and "slap" it onto your screen.
What you want (by my understanding) is to take a part of a texture and save it into another texture variable that afterwards can be rendered to the screen.
The first solution to the problem goes like this:
// load your image in a SDL_Texture variable (myTexture for example)
// if the image is (256,128) and you want to render only the first half
// you need a rectangle of the same size as that part of the image
SDL_Rect imgPartRect;
imgPartRect.x = 0;
imgPartRect.y = 0;
imgPartRect.w = 32;
imgPartRect.h = 32;
// and the program loop should have a draw block looking like this:
SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderClear( renderer );
SDL_RenderCopy( renderer, myTexture, &imgPartRect, NULL );
SDL_RenderPresent( renderer );
The approach you are trying to use has an intermediate texture that you render on and afterwards you render that texture to the screen. The problem here is that you set the renderer to draw on the texture you just created but you never reset your renderer to use the default target (the screen).
As you can see in the SDL documentation here the second parameter receives the texture that you want your renerer to draw on, or NULL if you want to reset it to the default target (the screen).
int SDL_SetRenderTarget(SDL_Renderer* renderer, SDL_Texture* texture)
Where:
renderer
the rendering context
texture
the targeted texture, which must be created with the SDL_TEXTUREACCESS_TARGET flag, or NULL for the default render target
Lets use the same example as before:
// first off let's build the function that you speak of
SDL_Texture* GetAreaTextrue(SDL_Rect rect, SDL_Renderer* renderer, SDL_Texture* source)
{
SDL_Texture* result = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect.w, rect.h);
SDL_SetRenderTarget(renderer, result);
SDL_RenderCopy(renderer, source, &rect, NULL);
// the folowing line should reset the target to default(the screen)
SDL_SetRenderTarget(renderer, NULL);
// I also removed the RenderPresent funcion as it is not needed here
return result;
}
// load your image in a SDL_Texture variable (myTexture for example)
// if the image is (256,128) and you want to render only the first half
// you need a rectangle of the same size as that part of the image
SDL_Rect imgPartRect;
imgPartRect.x = 0;
imgPartRect.y = 0;
imgPartRect.w = 32;
imgPartRect.h = 32;
// now we use the function from above to build another texture
SDL_Texture* myTexturePart = GetAreaTextrue( imgPartRect, renderer, myTexture );
// and the program loop should have a draw block looking like this:
SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderClear( renderer );
// here we render the whole newly created texture as it contains only a part of myTexture
SDL_RenderCopy( renderer, myTexturePart, NULL, NULL );
SDL_RenderPresent( renderer );
I don't know what you want to do, but I highly recommend the first way though.
I'm trying to change the background color of my window in SDL with pixel level access (I'm making a particle explosion), but the background is staying white.
First I tried using memset to set the memory of the pixels to 0x00 to change the color to black (I used memset because I am following a tutorial that used it)
memset(buffer, 0x00, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
Since that didn't work I looked up how to change the draw color of the renderer and came up with:
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 255);
Here is my Texture, Renderer, and Buffer code, and how I set up the window (SCREEN_WIDTH and SCREEN_HEIGHT are const ints set to 800 and 600 respectively):
SDL_Window* window = SDL_CreateWindow("Fire Particle Explosion",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
Uint32* buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
SDL_UpdateTexture(texture, NULL, buffer, SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
How would I properly change the background pixel by pixel, and why do neither of the ways I attempted work?
You don't need to manipulate a buffer in memory to achieve that, and you may not need a texture. The recommended way is to use the "2D Accelerated Rendering" functions in SDL2.
First of all, start without the "SDL_RENDERER_PRESENTVSYNC" parameter, use '0' instead. On Linux, some old graphics drivers produce flickering with that flag.
This should erase the screen in black:
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 255);
SDL_RenderClear(renderer);
I strongly advise you to use SDL_RenderDrawPoint() in order to update the screen pixel by pixel. It is hardware accelerated.
Basically, here is what happens in your game loop:
- set the color to black, then erase the screen with SDL_RenderClear()
- for each point to draw, first set the draw color, then draw the point
- use SDL_RenderPresent()
I have written a small example of SDL2 code in C for making particle explosions: drawpoints
If i use this code:
SDL_SetRenderDrawColor( renderer.get(), 0x00, 0x00, 0xFF, 0xFF );
SDL_RenderDrawLine( renderer.get(), 0, 480 / 2, 640, 480 / 2 );
//Draw vertical line of yellow dots
SDL_SetRenderDrawColor( renderer.get(), 0xFF, 0xFF, 0x00, 0xFF );
for( int i = 0; i < 480; i += 4 )
{
SDL_RenderDrawPoint( renderer.get(), 640 / 2, i );
}
It will draw yellow points and blue line, but if i comment SDL_RenderDrawLine, it will draw nothing. What is wrong?
This will happen too, if i use this code and somewhere in code (after a while) i want to use SDL_RenderDrawPoint or SDL_RenderDrawPoints.
I am using lastest SDL library and Linux Mint.
The following code is intended to display a green square on a black background. It executes, but the green square does not show up. However, if I change SDL_DisplayFormatAlpha to SDL_DisplayFormat the square is rendered correctly.
So what don't I understand? It seems to me that I am creating *surface with an alpha mask and I am using SDL_MapRGBA to map my green color, so it would be consistent to use SDL_DisplayFormatAlpha as well.
(I removed error-checking for clarity, but none of the SDL API calls fail in this example.)
#include <SDL.h>
int main(int argc, const char *argv[])
{
SDL_Init( SDL_INIT_EVERYTHING );
SDL_Surface *screen = SDL_SetVideoMode(
640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF
);
SDL_Surface *temp = SDL_CreateRGBSurface(
SDL_HWSURFACE, 100, 100, 32, 0, 0, 0,
( SDL_BYTEORDER == SDL_BIG_ENDIAN ? 0x000000ff : 0xff000000 )
);
SDL_Surface *surface = SDL_DisplayFormatAlpha( temp );
SDL_FreeSurface( temp );
SDL_FillRect(
surface, &surface->clip_rect, SDL_MapRGBA(
screen->format, 0x00, 0xff, 0x00, 0xff
)
);
SDL_Rect r;
r.x = 50;
r.y = 50;
SDL_BlitSurface( surface, NULL, screen, &r );
SDL_Flip( screen );
SDL_Delay( 1000 );
SDL_Quit();
return 0;
}
I was using the wrong format for SDL_MapRGBA. Should have been
SDL_FillRect(
surface, NULL, SDL_MapRGBA(
surface->format, 0xff, 0xff, 0x00, 0xff
)
);
(surface->format instead of screen->format.) I thought the two would be equivalent. And they are after calling SDL_DisplayFormat(), but not after calling SDL_DisplayFormatAlpha(). The screen surface doesn't have an alpha channel, so the format is different between the two.
(Cross-posted from gamedev.stackexchange.com)