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
Related
I am trying to make a Paint program that enables the user change background, while foreground color (that is used in drawing shapes) is always black.
Now, when the user changes background color, the HBRUSH paints the whole window with the chosen color and consequently clears all the shapes that were drawn.
To solve the problem I made 3 steps (note that the problem lies in the third step):
saveBeforeBackcolorChange() : a function that saves the window to bitmap before backColor is changed
FillRect(hdc, rect, color): paints the whole window with the new color
loadAfterBackcolorChanged(): ought to take only black pixels from the bitmap (which represent drawings) and consider any color transparent
Here is my implementation for the third step:
void loadAfterBackcolorChanged(HDC hdc){
ULONG_PTR m_gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
Graphics graphics(hdc);
wchar_t* filePathString = new wchar_t[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, TEMP_FILE.c_str(), -1, filePathString, 4096);
Image image(filePathString);
Gdiplus::ImageAttributes imAtt;
imAtt.SetColorKey(
Color(1, 1, 1),
Color(255, 255, 255),
ColorAdjustTypeBitmap);
graphics.DrawImage(
&image,
Rect(0, 0, image.GetWidth(), image.GetHeight()), // dest rect
0, 0, image.GetWidth(), image.GetHeight(), // source rect
UnitPixel,
&imAtt);
}
Unfortunately, the result is that it considers only white color transparent. Which means that it works well if the background is white (which is the initial backColor). Consequently the user can change backColor successfully only once!
I will be grateful if anyone could help.
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.
This is probably rather simple problem, but after an hour of searching and trying I still didn't manage to solve it.
I have two png files. One is a background image and second is foreground. The foreground has an alpha channel. I want to display foreground on top of background.
I'm loading foreground using:
SDL_Surface *clip = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0xff);
SDL_Rect rect = { x, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
SDL_BlitSurface(map, &rect, clip, NULL);
*block = SDL_CreateTextureFromSurface(gRenderer, clip);
Where map is some SDL_Surface.
I'm loadin backgroun using:
SDL_Surface* loadedSurface = IMG_Load(path);
//Create texture from surface pixels
SDL_Texture* newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
SDL_FreeSurface(loadedSurface);
Then I trying to connect them:
SDL_RenderCopy(gRenderer, background, NULL, &cur);
SDL_RenderCopy(gRenderer, map, NULL, &cur);
But it results in foreground image with black background. What am i doing wrong?
You should add these 2 lines,
Uint32 colorkey = SDL_MapRGB(loadedSurface->format, 0, 0, 0);
SDL_SetColorKey(loadedSurface, SDL_TRUE, colorkey);
before this line in your code
SDL_Texture* newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
In a game loop that I have, part of the drawing section is:
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
SDL_RenderFillRect(renderer, &blur);
renderer is my renderer and blur is my rect that takes up the whole screen:
SDL_Rect blur;
blur.x = 0;
blur.y = 0;
blur.w = 640;
blur.h = 480;
My problem is that the rect isn't semi transparent. Whenever it draws it, all there is is black.
You cant even see the text that I have underneath. How do I fix this? Does my renderer not support
alpha?
The reason the alpha value isn't affecting anything is because you need to specify which colour blending method you want to use beforehand with this function:
int SDL_SetRenderDrawBlendMode(SDL_Renderer* renderer, SDL_BlendMode blendMode)
The blendMode parameter controls how colour blending works. For alpha color blending, use SDL_BLENDMODE_BLEND:
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
SDL_RenderFillRect(renderer, &blur);