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);
Related
I have a work-related C/C++ SDL 2.0 project - originally developed and compiled on Win7 computer for x86 platform (With MS Visual Studio) that was working fine for the whole time. Since the program was meant to run also at newer Win10 x64 devices, I moved the entire solution to a newer Win10 x64 computer, changed all SDL2 libraries, header files etc. for the x64 version and rebuilt the solution for x64 architecture.
The program is basically an advanced diagnostic screen with dials, icons and text information, so it uses variety of colors, fonts, geometry etc.
After the recompilation for x64 platform, I noticed that the colors of some elements are absolutely off. For example - a rectangle that should be grey (used SDL_SetRenderDrawColor one step before drawing the rect.) is suddenly green, despite its given grey RGB numbers as input arguments. Even the function SDL_GetRenderDrawColor called before and/or after drawing of the rectangle returns values 195, 195, 195, but the rectangle is shown green.
Rectangle drawing function:
void DrawRectangle(SDL_Color color, int pozX, int pozY, int width, int height)
{
SDL_Rect Proportions = { pozX, pozY, width, height };
SDL_SetRenderDrawColor(gRenderer, color.r, color.g, color.b, 0xFF);
SDL_RenderFillRect(gRenderer, &Proportions);
}
Rectangle drawing call:
DrawRectangle(grey, pocpozX, 182, width, height);
Later I have noticed that this rectangle inherits its color from a text rendered a few steps earlier. If I change the color of this text, it for some strange reason applies also for the rectangle.
Text drawing function:
void DrawText(std::wstring text, TTF_Font *font, int pozX, int pozY, SDL_Color color)
{
typedef std::basic_string<Uint16, std::char_traits<Uint16>, std::allocator<Uint16> > u16string;
std::wstring text_sf_str = text;
u16string utext_sf_str(text_sf_str.begin(), text_sf_str.end());
SDL_Surface* text_sf = TTF_RenderUNICODE_Blended(font, utext_sf_str.c_str(), color);
SDL_Texture* text_Tx = SDL_CreateTextureFromSurface(gRenderer, text_sf);
int text_TxW;
int text_TxH;
SDL_QueryTexture(text_Tx, NULL, NULL, &text_TxW, &text_TxH);
SDL_Rect textrect = { pozX, pozY, text_TxW, text_TxH };
SDL_RenderCopy(gRenderer, text_Tx, NULL, &textrect);
SDL_FreeSurface(text_sf);
SDL_DestroyTexture(text_Tx);
}
Notice all these conversions needed to display a wstring text with some uncommonly used UNICODE characters. I don't quite understand why SDL needs to be passed each character as Uint16 number, this could have been done better. I also don't understand the need of using SDL_QueryTexture just in order to display the text proportions right. I would expect the user to want to display the text not anyhow stretched by default. This all seems to me unnecessarily complicated.
Funny thing is, that if I remove SDL_DestroyTexture tag, there is of course a huge memory leak, but the problem disappears.
Text drawing call - in a for loop, this draws numeric labels onto speedometer axis:
if (i % 20 == 0)
{
if (i <= 160)
{
DrawText(str2, font, posX, posY, grey);
}
else
{
DrawText(str2, font, posX, posY, green);
}
}
... And this very "green" as the last argument of the call is what causes the rectangle to be green also - despite the fact that more elements are drawn between this text and rectangle so SDL_SetRenderDrawColor is called multiple times.
I have also noticed that some shown numbers are for a little while glitching some senseless values every n-th second, so I guess this is a memory-related problem. Of course there are more color problems, this was just for an example. Due to company policy, I cannot be more specific or post the full code here.
If I switch back to x86 configuration (with the right libraries of course), the problem now remains the same.
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.
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.
I'm drawing some arcs using cairo, Here's the function.
cairo_t* drawCircle(cairo_surface_t *container, int x, int y, int r, int cr, int cg, int cb, int ca)
{
cairo_t *cairoInstance; //Create instance
cairoInstance = cairo_create(container);
cairo_set_source_rgba(cairoInstance, 0,0,1,0.5);
cairo_arc(cairoInstance, x, y, r, 0, 2*M_PI);
cairo_stroke_preserve(cairoInstance);
cairo_fill(cairoInstance);
gtk_widget_queue_draw_area(GTK_WIDGET(window), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); //Redraws the window
return cairoInstance;
}
First question: When I change the source_rgba later and then fill, it fills over the previous color. Is there a way to reset the fill before filling again with a different color?
Second of all, If I wanted to unrender/delete the arc/circle later on, how do I do that? I tried to use cairo_destroy but it seems to destroy the instance and not delete the actual object.
Thanks!
Cairo uses the stencil/paint model so drawing is (usually) done on top of the existing image1.
It doesn't maintain layers or objects. Just like drawing on paper, you can't move or modify the circle after its drawn. It has become a pattern in the pixmap.
To delete the circle, fill the whole image with the original background (cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr)), and redraw everything except the circle.
1. You can change the raster opcode to do other types of compositing.
I needed some help in trying to optimize this code portion ... Basically here's the thing .. I'm making this 'calligraphy pen' which gives the calligraphy effect by simply drawing a lot of adjacent slanted lines ... The problem is this: When I update the draw region using update() after every single draw of a slanted line, the output is correct, in the sense that updates are done in a timely manner, so that everything 'drawn' using the pen is immediately 'seen' the drawing.. however, because a lot (100s of them) of updates are done, the program slows down a little when run on the N900 ...
When I try to do a little optimization by running update after drawing all the slanted lines (so that all lines are updated onto the drawing board through a single update() ), the output is ... odd .... That is, immediately after drawing the lines, they lines seem broken (they have vacant patches where the drawing should have happened as well) ... however, if I trigger a redrawing of the form window (say, by changing the size of the form), the broken patches are immediately fixed !! When I run this program on my N900, it gets the initial broken output and stays like that, since I don't know how to enforce a redraw in this case ...
Here is the first 'optimized' code and output (partially correct/incorrect)
void Canvas::drawLineTo(const QPoint &endPoint)
{
QPainter painter(&image);
painter.setPen(QPen(Qt::black,1,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
int fx=0,fy=0,k=0;
qPoints.clear();
connectingPointsCalculator2(qPoints,lastPoint.x(),lastPoint.y(),endPoint.x(),endPoint.y());
int i=0;
int x,y;
for(i=0;i<qPoints.size();i++)
{
x=qPoints.at(i).x();
y=qPoints.at(i).y();
painter.setPen(Qt::black);
painter.drawLine(x-5,y-5,x+5,y+5); **// Drawing slanted lines**
}
**//Updating only once after many draws:**
update (QRect(QPoint(lastPoint.x()-5,lastPoint.y()-5), QPoint(endPoint.x()+5,endPoint.y()+5)).normalized());
modified = true;
lastPoint = endPoint;
}
Image right after scribbling on screen:
http://img823.imageshack.us/img823/8755/59943912.png
After re-adjusting the window size, all the broken links above are fixed like they should be ..
Here is the second un-optimized code (its output is correct right after drawing, just like in the second picture above):
void Canvas::drawLineTo(const QPoint &endPoint)
{
QPainter painter(&image);
painter.setPen(QPen(Qt::black,1,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
int fx=0,fy=0,k=0;
qPoints.clear();
connectingPointsCalculator2(qPoints,lastPoint.x(),lastPoint.y(),endPoint.x(),endPoint.y());
int i=0;
int x,y;
for(i=0;i<qPoints.size();i++)
{
x=qPoints.at(i).x();
y=qPoints.at(i).y();
painter.setPen(Qt::black);
painter.drawLine(x-5,y-5,x+5,y+5); **// Drawing slanted lines**
**//Updating repeatedly during the for loop:**
update(QRect(QPoint(x-5,y-5), QPoint(x+5,y+5)).normalized());//.adjusted(-rad,-rad,rad,rad));
}
modified = true;
int rad = (myPenWidth / 2) + 2;
lastPoint = endPoint;
}
Can anyone see what the issue might be ?
Sorry if I misunderstood, but have you tried to use the "double buffer" approach? Instead of drawing directly on the screen, you "draw" your points and lines to a memory buffer. After that, you just copy the buffer to the screen. This is faster and avoids flickering.
As I understand you should find min and max of x and y processed in your for-loop and use them in update(QRect(QPoint(minX-5, minY-5), QPoint(maxX+5, maxY+5)).normalized());
I'm not sure exactly what your issue is with the broken lines, but I can offer you this advice: keep your pen around. Instead of this:
for(i=0;i<qPoints.size();i++)
{
// ...
painter.setPen(Qt::black);
painter.drawLine(x-5,y-5,x+5,y+5); **// Drawing slanted lines**
// ...
}
do this:
QPen black_pen(Qt::black);
for(i=0;i<qPoints.size();i++)
{
// ...
painter.setPen(black_pen);
painter.drawLine(x-5,y-5,x+5,y+5); **// Drawing slanted lines**
// ...
}
Even more, if you are repeatedly calling your drawLineTo function with the same pen every time, store the pen in your class and keep it around. At my company, we've found that to vastly reduce drawing times where we can take advantage of it. (One instance on a large image cut drawing times in half.)
One other note: I'm not sure what type the image you are painting is, but I'm assuming it is a QImage. When you are done drawing, if you will be using the unmodified image repeatedly, you might convert it once to a QPixmap. The QPixmap class is stored in a way that is supposed to be ready for blitting directly to the screen (but it a lot slower to modify in many cases, because of that).