I am writing an application using Xlib. I set the foreground of the window up like this:
XSetForeground (dpy, gc, WhitePixel (dpy, scr));
But now I need to change the drawing colour to something else, I first wanted to do that like this:
void update_window (Display* d, Window w, GC gc, Colormap cmap)
{
XWindowAttributes winatt;
XColor bcolor;
char bar_color[] = "#4E4E4E";
XGetWindowAttributes (d, w, &winatt);
XParseColor(d, cmap, bar_color, &bcolor);
XAllocColor(d, cmap, &bcolor);
// Draws the menu bar.
XFillRectangle (d, w, gc, 0, 0, winatt.width, 30);
XFreeColormap (d, cmap);
}
But this doesn't work. What does XParseColor and XAllocColor do then? And do I need to use XSetForeground again to change the colour?
You need to use XSetForeground. Try something like this:
XColor xcolour;
// I guess XParseColor will work here
xcolour.red = 32000; xcolour.green = 65000; xcolour.blue = 32000;
xcolour.flags = DoRed | DoGreen | DoBlue;
XAllocColor(d, cmap, &xcolour);
XSetForeground(d, gc, xcolour.pixel);
XFillRectangle(d, w, gc, 0, 0, winatt.width, 30);
XFlush(d);
Also, I don't think you can use that color string. Take a look into this page:
A numerical color specification consists of a color space name and a set of values in the following syntax:
<color_space_name>:<value>/.../<value>
The following are examples of valid color strings.
"CIEXYZ:0.3227/0.28133/0.2493"
"RGBi:1.0/0.0/0.0"
"rgb:00/ff/00"
"CIELuv:50.0/0.0/0.0"
Edit/Update: as #JoL mentions in the comment, you can still use the old syntax, but the usage is discouraged:
For backward compatibility, an older syntax for RGB Device is supported, but its continued use is not encouraged. The syntax is an initial sharp sign character followed by a numeric specification, in one of the following formats:
//I write additional function _RGB(...) where r,g,b is components in range 0...255
unsigned long _RGB(int r,int g, int b)
{
return b + (g<<8) + (r<<16);
}
void some_fun()
{
//sample set color, where r=255 g=0 b=127
XSetForeground(display, gc, _RGB(255,0,127));
//draw anything
XFillRectangle( display, window, gc, x, y, len, hei );
}
All colour changes are done with respect to a certain GC. That GC is then used for drawing. Yes XSetForeground is the most convenient way to do that.
You can have several GCs if you have a handful of colours you use often.
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 use ID3DXFont interface to draw text and that perfectly suits my needs as long as complete string is in single color. Now I'd wish to draw a string but in multiple colors. For instance "abc", with a in red, b in yellow, etc.
I know that I could draw each letter on its own, giving a different Color parameter to DrawText each time. The only issue with this is that I do not know how many pixels should I offset after each letter because every letter has a different width. Hardcoding widths is not really a good solution.
The ID3DXFont interface doesn't allow you to draw multiple colors within a single invocation of DrawText. However, it can give you the bounding rectangles of any text that you wish to draw using the DT_CALCRECT flag, so you do not need to hardcode widths of particular glyphs within your font. This also means you can switch the font and/or size of the font without needing to modify your drawing code, or hardcoding new width. For example:
ID3DXFont* font = ...;
const char* strings[] = { "A", "i", "C" };
D3DCOLOR colors[] = { D3DCOLOR_ARGB(255, 255, 0, 0), D3DCOLOR_ARGB(255, 0, 255, 0), D3DCOLOR_ARGB(255, 0, 0, 255) };
RECT r = { 10,10,0,0}; // starting point
for (int i = 0; i < _countof(strings); ++i)
{
font->DrawText(NULL, strings[i], -1, &r, DT_CALCRECT, 0);
font->DrawText(NULL, strings[i], -1, &r, DT_NOCLIP, colors[i]);
r.left = r.right; // offset for next character.
}
Note: I have used 'i' instead of 'b' from your example, because it makes it apparent that the rectangles are correct, as 'i' is (generally) a very thin glyph. Also note that this assumes a single line of text. The calculated rectangle also includes height, so if you are doing multiple lines, you could also use the height of the calculated rectangle to offset the position.
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);
I'm working on a GUI-Project with d3d9 & d3dx9. I create fonts using the D3DXCreateFont function (C++). Everything is working fine. But I need a function that determines the width in pixel for a specific text.
Something like this:
char text[64] = "Heyho - Blablabla";
GUIFont* hFont = gui->fonts->CreateFont("DefaultFont", "Arial", 18);
int width = hFont->GetTextWidth(text);
[...]
The part with gui->fonts->CreateFont is all working fine. It's my way of creating and storing fonts. Ignore that part, it's all about the GetTextWidth. My CreateFont function initalizes the GUIFont object. The actual D3D-Font is stored in a LPD3DXFONT.
I really hope you can help me with this one, I am pretty sure it's possible - I just don't know how. Thanks for reading, and I hope you have a clue.
You can use the DT_CALCRECT flag on the ID3DXFont function DrawText to return the required size of the rectangle enclosing the text.
So, if you want to get just the width, you might have a function something like this:
int GetTextWidth(const char *szText, LPD3DXFont pFont)
{
RECT rcRect = {0,0,0,0};
if (pFont)
{
// calculate required rect
pFont->DrawText(NULL, szText, strlen(szText), &rcRect, DT_CALCRECT,
D3DCOLOR_XRGB(0, 0, 0));
}
// return width
return rcRect.right - rcRect.left;
}
Obviously, you can also extract the height too if you need it.
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.