SDL_FillRect Not Drawing? - c++

I'm making a game in C++ with SDL, and I want to render particles with SDL_FillRect().
I've played with the code for hours, but no matter what I do, the particles are not drawing.
This is the code in my Render function (I made sure that I was in fact calling the function):
void Particle::Render()
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
//rect.w = Particle::Particle_Size;
//rect.h = Particle::Particle_Size;
rect.w = 8;
rect.h = 8;
surface = SDL_CreateRGBSurface(SDL_SWSURFACE,8,8,32,0,0,0,0);
if(SDL_FillRect(surface,&rect,SDL_MapRGB(surface->format,0,0,0)) != 0) printf("Error");
//SDL_RenderCopy(renderer,texture,NULL,&rect);
}
The console isn't printing "Error", so the SDL_FillRect() is successful. However, no rects are being drawn to the screen.
I tried creating a texture with SDL_CreateTextureFromSurface() with that surface passed in, and then used SDL_RenderCopy, which is commented out in the above function, but it worked before I commented it out. I want to use SDL_FillRect so I could have colored textures though.
Am I missing anything?

I think you could use this function to do what you are looking for:
SDL_RenderFillRect()
https://wiki.libsdl.org/SDL_RenderFillRect
You would have to set the renderer color before with:
SDL_SetRenderDrawColor();
I think you could also update the window surface to get what you have to work.
That would use
SDL_UpdateWindowSurface().
https://wiki.libsdl.org/SDL_UpdateWindowSurface?highlight=%28%5CbCategoryVideo%5Cb%29%7C%28CategoryEnum%29%7C%28CategoryStruct%29
Hope it helps!

The SDL_CreateRGBSurface() function creates an off-screen surface. If you want to draw to the screen, you will have to draw to the surface returned by SDL_GetWindowSurface().
That is, if you are using SDL 2.0.

Related

Drawing keeps getting stuck to the screen in SDL

In my program what I am drawing gets stuck on to the screen I am drawing on, by this I mean that what I previously drawed onto the screen stays after I call SDL_UpdateWindowSurface(). Here is my code.
void tower_manager::render()
{
m_tower.draw(camx, camy,m_screen);
//SDL_BlitSurface(test, NULL, m_screen, NULL);
SDL_Rect rect = { 32, 32, 32, 32 };
//draw the tower walls;
for (int x = 0; x < towerWidth; x++)
{
for (int y = 0; y < towerHeight * 2; y += 2)
{
rect.x = x*blockSize - camx;
rect.y = y*blockSize - camy;
SDL_BlitSurface(test, NULL, m_screen, &rect);
}
}
SDL_UpdateWindowSurface(m_window);
}
Apparently I need at least 10 reputation to post images so I cant post a screen shot but here is an example, you know what happens to the desktop when a windows application freezes and it keeps drawing the same window over and over again and you can draw it around to make art and stuff? That's exactly what it looks like is happening here. Also I have another issue when I call the tower objects method that is originally going to draw the tower using the same code it does not draw or do anything at all(i am passing in a pointer to the screen I am drawing to in its parameter).
You would want to clear the surface regions that you are drawing to. If you don't, then the screen surface retains the old renderings from previous frames and you are drawing on top of them. This causes a smearing artifact.
An old optimization (no longer so useful with SDL2 or OpenGL) here is to keep track of dirty rectangles and clear each of them, but the simplest way is to just clear the entire surface each frame before rendering.
So, once per frame do something like this:
SDL_FillRect(m_screen, NULL, 0x000000);

SDL Resetting Surfaces

I draw some text to a surface (using SDL_ttf) and then I want to change the text on the surface. If I just redraw the surface the text does not go away. I have looked at several forum posts on how to fix the problem but I just cannot seem to figure it out. In particular I cannot understand why this solution does not work: (code is long so this just gives the essentials)
In Class file declared:
SDL_Surface* box; // These two are initialised to the
SDL_Surface* boxCopy; // same image
At the start of my render function:
*box = *boxCopy; \\Reset box surface
My understanding of pointers and C++ (which is admittedly limited) suggests that this should make the surface pointed at by box equal to the surface pointed at by boxCopy. Instead the boxCopy surface becomes a copy of box. I have no idea how boxCopy can be changed by this line of code but it seems like that is what is happening.
I'm not sure i completely understand your problem but hopefully this can help.. It's easier to update the text whenever the surface it's drawn on is to be updated rather than updating it whenever the actual text is updated. It might not be as optimized performance wise but i would say it's easier in most cases.
A typical program loop would include a re-rendering of a surface representing the screen followed by an SDL_Flip of this surface. You can of course optimize your re-rendering so you only render what has actually been updated since last frame. Is that what you're working on perhaps? If so, and if you use the method below you should be aware that the new text only covers the size of the new text and not the entire old text. I usually solve this by first drawing a filled rectangle and then the new text.
Here is a TTF example showing how text can be drawn on a surface (here called m_Screen, which is the surface flipped to screen every frame) in the simple case where i have one background color only:
void drawText(const char* string, int x, int y,
int fR, int fG, int fB, int bR, int bG, int bB)
{
SDL_Color foregroundColor = { fR, fG, fB };
SDL_Color backgroundColor = { bR, bG, bB };
SDL_Surface* textSurface = TTF_RenderText_Shaded(m_Font, string,
foregroundColor,
backgroundColor);
SDL_Rect textLocation = { x, y, 0, 0 };
SDL_BlitSurface(textSurface, NULL, m_Screen, &textLocation);
SDL_FreeSurface(textSurface);
}
Notice that this has been done before calling drawText (with some suitable font size):
m_Font = TTF_OpenFont("arial.ttf", size);
And this is done at cleanup:
TTF_CloseFont(m_Font);

Passing an SDL_Surface or clip_rect

I'm trying to write a bit of code in which I check to see if the mouse has been clicked on a certain SDL Surface. In the code example below, I have the SDL_Surface background which I am trying to check against the mouse position.
bool checkCollide(int myx, int myy, SDL_Surface* objectOne){
if(myx < objectOne->clip_rect.x) return false;
if(myx > objectOne->clip_rect.x + objectOne->clip_rect.w) return false;
if(myy < objectOne->clip_rect.y) return false;
if(myy > (objectOne->clip_rect.y + objectOne->clip_rect.h)) return false;
return true;
}
void main(){
SDL_Surface* background;
while(SDL_PollEvent(&event)){
if(event.type == SDL_MOUSEBUTTONDOWN){
if(checkCollide(event.button.x, event.button.y, background){
// run events
}
}
}
The problem I'm having is that when I try to run it as shown above, the code compiles fine but doesn't do anything. The collision check always fails. I have tried it in a lot of various combinations, including changing the parameters of checkCollide to SDL_Rect and passing the Surface's clip_rect property. The only completely successful way I've done it is to have a separate SDL_Rect with the same size and location as the SDL_Surface, and to check the collision against that. When I run it that way, it all works as I expect it to. Why isn't the surface (or clip_rect) passing correctly, or if it is, what am I doing wrong in the checkCollide function to cause it to fail?
The clip_rect property is not what you think it is:
From the SDL docs:
The clip_rect field is the clipping rectangle as set by
SDL_SetClipRect.
and SDL_SetClipRect
Sets the clipping rectangle for a surface. When this surface is the
destination of a blit, only the area within the clip rectangle will be
drawn into.
The rectangle pointed to by rect will be clipped to the edges of the
surface so that the clip rectangle for a surface can never fall
outside the edges of the surface.
If rect is NULL the clipping rectangle will be set to the full size of
the surface.
The problem is, that when you test the collision later on, the clip_rect x,y are always set to 0. The surface itself does not know about it's position on the screen.
A solution to this would be to create a Sprite like class that will have a Rect and a Surface.

SDL_Surface Transparency Issues

I have been working with sdl ttf, Now, I'm creating a text renderer function for multiline text rendering.
I am creating a surface for each line, blitting in in a "global" surface, and then converting that "global" surface to a texture.
Actually My problem is the following: If I let the global surface transparent, the blitting does not works (It shows an empty surface), If I set that SDL_FillRect, and then do the SDL_SetColorKey, does not works.
I tried to do that with a per-pixel method, it works, but it's not the result expected.
What I need actually is: Blit surfaces (Text rendered surfaces with TTF_RenderText_Blended), blit them inside a SDL_Surface with a transparent background, that's all.
SDL_Surface * surfed_texture = SDL_CreateRGBSurface(SDL_SWSURFACE,surface_width,surface_height,32,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
Uint32 tormv = SDL_MapRGB(surfed_texture->format,255,0,255);
int possiblecolors[] = { 255,0,255,255,255,255 };
int in_pc = 0;
if(color.r == possiblecolors[0] && color.g == possiblecolors[1] && color.b == possiblecolors[2]){
in_pc=3;
}
/*SDL_FillRect(surfed_texture,NULL,SDL_MapRGB(surfed_texture->format,possiblecolors[in_pc],possiblecolors[in_pc+1],possiblecolors[in_pc+2]));
SDL_SetColorKey(surfed_texture, SDL_SRCCOLORKEY, SDL_MapRGB(surfed_texture->format,possiblecolors[in_pc],possiblecolors[in_pc+1],possiblecolors[in_pc+2]));
*/
SDL_Surface * temporal = NULL;
for(int i=0;i<(int)buff_split.size();i++){
const char* c_text = buff_split.at(i).c_str();
temporal = TTF_RenderText_Blended(font,c_text,color);
int w_fo;
int h_fo;
TTF_SizeText(font,c_text,&w_fo,&h_fo);
SDL_Rect rct;
rct.y=i*lineSkip;
if(txt_align==ALIGN_LEFT){
rct.x=0;
}else if(txt_align==ALIGN_CENTER){
rct.x = surface_width/2 - w_fo/2;
}else if(txt_align==ALIGN_RIGHT){
rct.x = surface_width - w_fo;
}
rct.w=0;
rct.h=0;
// Blit surface
SDL_BlitSurface(temporal,NULL,surfed_texture,&rct);
SDL_FreeSurface(temporal);
}
Blitting into "surfed_texture" with transparent background = Does not shows the blitted surface.
Per-Pixel method: Does not removes all the background.
And the SDL_SetColorKey is not working! (I already tried SDL_DisplayFormatAlpha and stills not working).
Some help?
The solution is the following:
Use the SDL_SetAlpha NOT in the "global" surface, apply a SDL_SetAlpha to each "temporal" surface with no flags, and with a value of 0:
SDL_SetAlpha(temporal,NULL,0);
Now it's working fine!

Want to place several images with transparent colour on the same background

I am desparately searching for place several graphics having a transparent background on the same background with GDI+. I did not have any programming experience with Windows or graphic programming (like games) before, so it is more difficult to find a solution. GDI+ has a transparent colour. GDI+ only uses the transparency information to bitmap this colour properly on another image. Once bitmaped, however, the first image is no more transparent. If you put the same image several times on the same background, you will see that only the first placement was transparent.
My problem is placing several transparent objects on a background at once. You can see the code below that works for one ship (nNrOfShips = 1;). If you write a larger value for this variable, no ship will be placed.
How should I modify the code? I also tried to use Ship arrays, but no ship appears on the screen. You may create your own example by using a background with (slightly) changing colour and just place an image transparently. I hope that that example would help me.
Here the code example...
HDC hdcScreen = GetLockedScreen();
m_hdcShip = CreateCompatibleDC(hdcScreen);
ReleaseLockedScreen();
// Draw the ship image on restored background
Graphics grBkg(m_hdcNewBackground);
grBkg.SetSmoothingMode(SmoothingModeHighQuality);
// Restore new background
BitBlt(m_hdcNewBackground, 0, 0,
GetWtsMetrics(wtsm_ScreenSizeX), GetWtsMetrics(wtsm_ScreenSizeY),
m_hdcSavedBackground, 0, 0, SRCCOPY); // 20100125 SAE
BYTE nNrOfShips = 1; // DATA->GetNrOfShips();
for (BYTE nShipId = 0; nShipId < nNrOfShips; nShipId++)
{
Ship ship = DATA->GetShipList()[nShipId];
ShipModel shipModel = DATA->FindShipModel(ship.nShipModelId); // 20100202 SAE
WORD nCurResId = DATA->FindCurShipResourceId(ship); // 20100131 SAE
WORD nIndex = nCurResId - shipModel.nFirstResId; // 20100131 SAE
assert(nIndex >= 0);
ShipResource shipRes = (*shipModel.pvectResource)[nIndex]; // 20100202 SAE
// 20100126 SAE
// Always take the first (upper left) coordinates of the ship rectangle:
QuadrantVector &wpQuadrants =
*DATA->GetWallpapers()[DATA->SelectWallpaper()].pvectQuadrant;
do
{ // 20100115 SAE: Determine first the coordinates of the ship
ship.vectRectangle = DATA->RandomRectangleCoordinates(
shipModel.nHeight, shipModel.nWidth);
} while (!DATA->AreCoordinatesValid(ship.vectRectangle, wpQuadrants) &&
!DATA->AreShipsTooClose(ship, DATA->GetShipList(), DATA->GetDistance()));
grBkg.TranslateTransform(ship.vectRectangle[0].fX,
ship.vectRectangle[0].fY);
grBkg.RotateTransform(0); // 20100201 SAE
grBkg.DrawImage(shipRes.pimgPicture,
-shipModel.nWidth/2, -shipModel.nHeight/2);
// Determine bounding rectangle of ship after drawing on transformed page
// 20100125 SAE
Rect rcSrc(-shipModel.nWidth/2, -shipModel.nHeight/2,
shipModel.nWidth, shipModel.nHeight);
TransformRect(&grBkg, &m_rcCurShip, &rcSrc,
CoordinateSpacePage, CoordinateSpaceWorld);
} // for
DeleteDC(m_hdcShip);
m_hdcShip = 0;
Use the Bitmap.MakeTransparent() method on the images when you load them. You'll need to select the color that's the background color for those images. Storing the images in the PNG format with the transparency selected in the graphics editor would be another way.