I want to redraw only part of a GtkTextView widget. In the draw signal handler I tried many things for example:
GdkRectangle rect;
gdk_cairo_get_clip_rectangle(cr, &rect);
cairo_surface_t* surface = cairo_get_target(cr);
cairo_surface_t* newsurf = cairo_surface_create_similar(surface, cairo_surface_get_content(surface), rect.width - 50, rect.height);
cairo_push_group(cr);
cairo_set_source_surface(cr, newsurf, rect.x + 10, rect.y + 40);
cairo_fill_preserve(cr);
cairo_stroke(cr);
cairo_pop_group_to_source(cr);
cairo_paint(cr);
except that nothing really happens. How do I re-draw only a rect of the dirty clip region in cr or how (as I am trying) to modify cr so that the surface area has different dimensions?
To clarify further - I don't draw my own things. I want to set my own surface in the cairo_t passed in the draw callback of the GtkTextView for greater control of how the widget is rendered naturally. This new surface may be the same as the source surface in cr, but cropped or an earlier "snapshot" of the widget obtained with gtk_widget_draw()
Related
I have opened a window which shows two rects on the screen then using SDL_TTF to show the mouse position on the screen.
The bit I am having hard time understanding is why after rendering text the the two rects before it do not show up.
I am using SDL_RenderFillRect to draw two rects on screen
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, rect1);
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderFillRect(renderer, rect2);
Code for rendering the text is
// define string with mouse x, y coords
sprintf(someString, "x: %d, y: %d", mouse.x, mouse.y);
SDL_Point textPos = {10, 10};
WriteText(renderer, font, someString, textPos, (SDL_Color){255, 255, 255, 255});
SDL_Surface *fontSurface = TTF_RenderText_Blended(font, someString, COLOR_BLACK); // create font surface
SDL_Texture *fontTexture = SDL_CreateTextureFromSurface(renderer, fontSurface); // create the texture
// get clip width and height from fontsurface clip rect
SDL_Rect *fontRect = &fontSurface->clip_rect;
fontRect->x = pos.x;
fontRect->y = pos.y;
SDL_RenderCopy(renderer, fontTexture, NULL, fontRect); // copy text to the renderer
// delete surface and texture
SDL_FreeSurface(fontSurface);
SDL_DestroyTexture(fontTexture);
It to shows the mouse positon top left corner of the window. However this makes the rest of the window blank.
To prevent this my work around is having to draw something on the screen after calling SDL_RendererCopy (and weirdly before calling SDL_DestroyTexture too) For example drawing single point
...
SDL_RenderCopy(renderer, fontTexture, NULL, fontRect); // copy text to the renderer
// why is this needed??
SDL_RenderDrawPoint(renderer, 0, 0);
// delete surface and texture
SDL_FreeSurface(fontSurface);
SDL_DestroyTexture(fontTexture); // have to draw a point before this
...
This then shows the two rects rendered before the text
If I set dstRect to NULL when calling SDL_RenderCopy then the text spans the whole window but I can see what was rendered before underneath the text.
Why am I having to draw a point after calling SDL_RenderCopy to stop what was rendered before from not showing up?
NOTE: Link to full source code https://pastebin.com/tRSFT0PV
This is a bug in SDL 2.0.10. It's fixed by https://hg.libsdl.org/SDL/rev/6ee12b88beed and this fix will ship with 2.0.11. Sorry about that!
I searched in INTERNET related to the subject but I found only people that want to save png file with the background transparent.
I report belove the source code that at the moment I use and produces png with background transparent:
cairo_surface_t *surface; // Declarations
cairo_t *cr;
// Creations of the surface
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,3508,2480);
// Contest
cr = cairo_create(surface);
// Moving the plot to center in the page
cairo_translate(cr, 200,200);
// Scaling the plot
cairo_scale(cr,1.8,1.8);
// Make all the necessary actions to produce the plot
do_drawing(cr);
// write the plot on the file png
cairo_surface_write_to_png(surface, "image.png");
// End
cairo_surface_destroy(surface);
cairo_destroy(cr);
I used also the surface CAIRO_FORMAT_RGB24 but I obtain a black rectangle like plot.
Please could you help me?
Thank You
cairo_surface_t *surface; // Declarations
cairo_t *cr;
// Creations of the surface
// XXX: Changed to format RGB24; if you don't need transparency, well.
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,3508,2480);
// Contest
cr = cairo_create(surface);
// Draw background XXXXXXXXXXXXXXXXXX
cairo_save(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_restore(cr);
// Moving the plot to center in the page
cairo_translate(cr, 200,200);
// Scaling the plot
cairo_scale(cr,1.8,1.8);
// Make all the necessary actions to produce the plot
do_drawing(cr);
// write the plot on the file png
cairo_surface_write_to_png(surface, "image.png");
// End
cairo_surface_destroy(surface);
cairo_destroy(cr);
You need to draw with an opaque background, take a look at the cairo_set_source_rgb function.
(code snippet. I know it's ugly but i wanted to make it work before making it better so please don't pay too much attention to the structure)
I modified slightly the glfw example present in the documentation to have a triangle that rotates when pressing the right arrow key and draws a circle described by the position of one of his vertices (the blue one in this case).
I clear the GL_COLOR_BUFFER_BIT only when initializing the window to avoid having to store all the coordinates that will be needed to draw the line (they would be hundreds of thousands in the final program), that means that on the screen every time i press the right arrow a "copy" of the triangle is draws rotated by 12 degrees and a line is drawn that connects the old blue angle position to the new one.
The problem now is that i would want to be able to press the escape key GLFW_KEY_ESCAPE and "delete" the triangles while keeping the lines drawn.
I tried using a z-buffer to hide the triangles behind a black rectangle but only the last line drawn is visualized (i think this is because opengl doesn't know the z of the previous lines since i don't store them).
Is there a way to do what i want without having to store all the point coordinates and then clearing the whole screen and redrawing only the lines? If this is the case, what would be the best way to store them?
Here is part of the code i have so far.
bool check = 0;
Vertex blue = {0.f, 0.6f, 0.5f};
Vertex green = {0.6f,-0.4f, 0.5f};
Vertex red = {-0.6f, -0.4f, 0.5f};
Vertex line = {0.f, 0.6f, 0.f};
Vertex line2 = {0.f, 0.6f, 0.f};
static void
key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
check = !check;
if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS) {
line.x = line2.x;
line.y = line2.y;
rotation -= 12;
rad = DegToRad(-12);
double x = line.x*cos(rad) - line.y * sin(rad);
double y = line.y * cos(rad) + line.x * sin(rad);
line2.x = x;
line2.y = y;
}
int main(void) {
GLFWwindow *window;
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
window = glfwCreateWindow(1280, 720, "Example", NULL, NULL);
if (!window) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glClear(GL_COLOR_BUFFER_BIT);
while (!glfwWindowShouldClose(window)) {
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
float ratio;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(rotation, 0.f, 0.f, 1.f);
glBegin(GL_TRIANGLES);
glColor3f(1.f, 0.f, 0.f);
glVertex3f(red.x, red.y, red.z);
glColor3f(0.f, 1.f, 0.f);
glVertex3f(green.x, green.y, green.z);
glColor3f(0.f, 0.f, 1.f);
glVertex3f(blue.x, blue.y, blue.z);
glEnd();
glLoadIdentity();
glLineWidth(1.0);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINES);
glVertex3f(line.x, line.y, line.z);
glVertex3f(line2.x, line2.y, line2.z);
glEnd();
if (check){
//hide the triangles but not the lines
}
glEnd();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
I clear the GL_COLOR_BUFFER_BIT only when initializing the window
That's your problem right there. It's idiomatic in OpenGL to always start with a clear operation of the main framebuffer color bits. That is, because you don't know the state of your window main framebuffer when the operating system is asking for a redraw. For all you know it could have been all replaced with cat pictures in the background without your program knowing it. Seriously: If you have a cat video running and the OS felt the need to rearrange your window's main framebuffer memory this is what you might end up with.
Is there a way to do what i want without having to store all the point coordinates and then clearing the whole screen and redrawing only the lines?
For all intents and purposes: No. In theory one could come up with a contraption made out of a convoluted series of stencil buffer operations to implement that, but this would be barking up a very wrong tree.
Here's something for you to try out: Draw a bunch of triangles like you do, then resize your window down so there nothing remains, then resize it back to its original sizeā¦ you see where the problem? There's a way to address this particular problem, but that's not what you should do here.
The correct thing is to redraw everything. If you feel that that's to slow you have to optimize your drawing process. On current generation hardware it's possible to churn out on the order of 100 million triangles per second.
I have a SDL2 game project written in C that has a fullscreen feature, using
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP)
Drawing was then allowed on all the fullscreen'd window surface.
However, I since switched from directly rendering into the window to rendering into a texture that gets then rendered on-screen.
void initGFX(void) {
// (SNIP)
framebuffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_TARGET, WINDOW_WIDTH, WINDOW_HEIGHT); // WINDOW_WIDTH and _HEIGHT are constants, the non-fullscreen window having these sizes.
// (SNIP)
}
void renderPresent(void) {
SDL_RenderPresent(renderer);
SDL_SetRenderTarget(renderer, NULL); // renderer has "SDL_RENDERER_TARGETTEXTURE"
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, framebuffer, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_SetRenderTarget(renderer, framebuffer);
}
This doesn't cause any graphical errors when in windowed mode, but in fullscreen the texture is rendered 1:1 on the top-left corner.
SDL_WINDOW_FULLSCREEN doesn't allow drawing on the whole screen even with SDL_SetWindowDisplayMode to have .w and .h match the desktop resolution.
I have a Gtk app written in C running on Ubuntu Linux.
I'm confused about some behavior I'm seeing with the pango_cairo_show_layout function: I get the exact "ink" (not "logical") pixel size of a pango layout and draw the layout using pango_cairo_show_layout on a GtkDrawingArea widget. Right before drawing the layout, I draw a rectangle that should perfectly encompass the text that I'm about to draw, but the text always shows up a little below the bottom edge of the rectangle.
Here is my full code:
// The drawing area widget's "expose-event" callback handler
gboolean OnTestWindowExposeEvent(GtkWidget *pWidget, GdkEventExpose *pEvent, gpointer data)
{
// Note that this window is 365 x 449 pixels
double dEntireWindowWidth = pEvent->area.width; // This is 365.0
double dEntireWindowHeight = pEvent->area.height; // This is 449.0
// Create a cairo context with which to draw
cairo_t *cr = gdk_cairo_create(pWidget->window);
// Draw a red background
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
cairo_rectangle(cr, 0.0, 0.0, dEntireWindowWidth, dEntireWindowHeight);
cairo_fill(cr);
// Calculate the padding inside the window which defines the text rectangle
double dPadding = 0.05 * ((dEntireWindowWidth < dEntireWindowHeight) ? dEntireWindowWidth : dEntireWindowHeight);
dPadding = round(dPadding); // This is 18.0
// The size of the text box in which to draw text
double dTextBoxSizeW = dEntireWindowWidth - (2.0 * dPadding);
double dTextBoxSizeH = dEntireWindowHeight - (2.0 * dPadding);
dTextBoxSizeW = round(dTextBoxSizeW); // This is 329.0
dTextBoxSizeH = round(dTextBoxSizeH); // This is 413.0
// Draw a black rectangle that defines the area in which text may be drawn
cairo_set_line_width(cr, 1.0);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_rectangle(cr, dPadding, dPadding, dTextBoxSizeW, dTextBoxSizeH);
cairo_stroke(cr);
// The text to draw
std::string szText("Erik");
// The font name to use
std::string szFontName("FreeSans");
// The font size to use
double dFontSize = 153.0;
// The font description string
char szFontDescription[64];
memset(&(szFontDescription[0]), 0, sizeof(szFontDescription));
snprintf(szFontDescription, sizeof(szFontDescription) - 1, "%s %.02f", szFontName.c_str(), dFontSize);
// Create a font description
PangoFontDescription *pFontDescription = pango_font_description_from_string(szFontDescription);
// Set up the font description
pango_font_description_set_weight(pFontDescription, PANGO_WEIGHT_NORMAL);
pango_font_description_set_style(pFontDescription, PANGO_STYLE_NORMAL);
pango_font_description_set_variant(pFontDescription, PANGO_VARIANT_NORMAL);
pango_font_description_set_stretch(pFontDescription, PANGO_STRETCH_NORMAL);
// Create a pango layout
PangoLayout *pLayout = gtk_widget_create_pango_layout(pWidget, szText.c_str());
// Set up the pango layout
pango_layout_set_alignment(pLayout, PANGO_ALIGN_LEFT);
pango_layout_set_width(pLayout, -1);
pango_layout_set_font_description(pLayout, pFontDescription);
pango_layout_set_auto_dir(pLayout, TRUE);
// Get the "ink" pixel size of the layout
PangoRectangle tRectangle;
pango_layout_get_pixel_extents(pLayout, &tRectangle, NULL);
double dRealTextSizeW = static_cast<double>(tRectangle.width);
double dRealTextSizeH = static_cast<double>(tRectangle.height);
// Calculate the top left corner coordinate at which to draw the text
double dTextLocX = dPadding + ((dTextBoxSizeW - dRealTextSizeW) / 2.0);
double dTextLocY = dPadding + ((dTextBoxSizeH - dRealTextSizeH) / 2.0);
// Draw a blue rectangle which should perfectly encompass the text we're about to draw
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
cairo_rectangle(cr, dTextLocX, dTextLocY, dRealTextSizeW, dRealTextSizeH);
cairo_stroke(cr);
// Set up the cairo context for drawing the text
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
// Move to the top left coordinate before drawing the text
cairo_move_to(cr, dTextLocX, dTextLocY);
// Draw the layout text
pango_cairo_show_layout(cr, pLayout);
// Clean up
cairo_destroy(cr);
g_object_unref(pLayout);
pango_font_description_free(pFontDescription);
return TRUE;
}
So, why is the text not being drawn exactly where I tell it to be drawn?
Thanks in advance for any help!
Look at the documentation for pango_layout_get_extents() (this is not mentioned in the docs for pango_layout_get_pixel_extents():
Note that both extents may have non-zero x and y. You may want to use
those to offset where you render the layout.
https://developer.gnome.org/pango/stable/pango-Layout-Objects.html#pango-layout-get-extents
This is because the position that you render the layout at is (as far as I remember) the position of the base line (so something logically related to the text) instead of the top-left corner of the layout (which would be some "arbitrary thing" not related to the actual text).
In the case of your code, I would suggest to add tRectangle.x to dTextLocX (or subtract? I'm not completely sure about the sign). The same should be done with the y coordinate.
TL;DR: Your PangoRectangle has a non-zero x/y position that you need to handle.
Edit: I am not completely sure, but I think Pango handles this just like cairo. For cairo, there is a nice description at http://cairographics.org/tutorial/#L1understandingtext. The reference point is the point you give to cairo. You want to look at the description of bearing.