I am in the process of developing an artificial horizon, as used in planes. This horizon has a background containing ground (brown) and sky (blue). Depending on the roll-angle/pitch of the plane, this background is rotated.
In order to keep it simple and keep CPU usage low, instead of drawing everything every pass, I wish to use an oversized static background image, which I will rotate as needed, and from which I will then copy/paste a square section to the screen.
The problem I'm having, is that I can't get cairo to rotate the surface FIRST and THEN copy/paste a section. It does copy/paste correctly, only rotates AFTER this has been done.
The code I have so far:
#define WINDOW_WIDTH 320
#define WINDOW_HEIGHT 240
double deg2rad( double degrees )
{
return((double)((double)degrees * ( (double)M_PI/(double)180.0 )));
}
static gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{
cairo_surface_t *source;
cairo_t *bck;
cairo_pattern_t *source_pattern;
gint s_width, s_height, d_width, d_height,source_x, source_y;
// load the image from disk
source = cairo_image_surface_create_from_png ("/home/henri/dev/art_horiz_bck.png");
bck = cairo_create(source);
cairo_set_source_surface (bck, source,0,0);
s_width = cairo_image_surface_get_width(source);
s_height = cairo_image_surface_get_height(source);
// rotate around center of image
cairo_translate(bck, s_width/2, s_height/2);
cairo_rotate(cr, deg2rad(30));
cairo_paint(bck);
// after rotation, the image size should have been changed (increased)
s_width = cairo_image_surface_get_width(cairo_get_target (bck));
s_height = cairo_image_surface_get_height(cairo_get_target (bck));
d_width = gtk_widget_get_allocated_width (widget);
d_height = gtk_widget_get_allocated_height (widget);
// get the center 'viewport'
source_x = (s_width/2)-(d_width/2);
source_y = (s_height/2)-(d_height/2);
// copy this rectangle to the destination surface
cairo_set_source_surface (cr, source, -source_x, -source_y);
cairo_rectangle (cr, 0, 0, 320, 200);
cairo_fill (cr);
return FALSE;
}
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *grid;
GtkWidget *topbar;
GtkWidget *bottombar;
GtkWidget *da;
gtk_init (&argc, &argv);
topbar = gtk_image_new_from_file ("/home/henri/dev/topbar.png");
bottombar = gtk_image_new_from_file ("/home/henri/dev/bottombar.png");
da = gtk_drawing_area_new();
gtk_widget_set_size_request (da, WINDOW_WIDTH, WINDOW_HEIGHT);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_decorated(GTK_WINDOW (window), 0);
gtk_window_set_default_size (GTK_WINDOW (window), WINDOW_WIDTH, WINDOW_HEIGHT);
g_signal_connect (da, "draw", G_CALLBACK(draw_cb), NULL);
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
grid = gtk_grid_new ();
gtk_container_add (GTK_CONTAINER (window), grid);
gtk_grid_attach (GTK_GRID (grid), topbar, 0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid), da, 0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), bottombar, 0, 2, 1, 1);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
As stated, this almost does what I want, besides the rotation not getting applied at cairo_paint(bck). So i FIRST want cairo to rotate the image and THEN get a square horizontal rectangle from it. Now it first gets the rectangle, and then rotates this.
//edit
to make this more clear. The app will run on an embedded device with a 320x240 tft screen. It will be fullscreen. Above and below the horizon windows there wil be small static bars. This question only handles the drawing area in between.
I have this background image:
http://s10.postimg.org/3xuvr2dyh/art_horiz_bck.png
This is sufficiently oversized to cover all possible roll and pitch angles of the airplane in question. Now suppose the plane is flying in a 10 degree nose up attitude (so it's climbing) and is rolling with 5 degrees roll-angle.
Now what I want to do is to rotate the above background by 5 degrees, and then take a rectangular section out of it, above the horizon, so that the 10 degree nose up attitude is also displayed. So from the above image, I want to distill this image:
http://s9.postimg.org/a4u8m4oan/Naamloos.png
Note that this second image is cropped to 320x240, the size of the drawing area it will be drawn on.
//edit 2
the below posted code by Uli Schlachter does seem to do what I want, however, goes wrong at this point:
cairo_matrix_translate (&matrix, -(s_width-d_width)/2.0, -(s_height - d_height)/2.0)
This is because it uses the dimensions of the original, unrotated image. I need it to use the dimensions of the already rotated image there.
No idea if this works the way you want, but hopefully it helps you to figure out how to do what you want:
static gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{
cairo_surface_t *source;
cairo_t *bck;
cairo_pattern_t *source_pattern;
gint s_width, s_height, d_width, d_height,source_x, source_y;
cairo_matrix_t matrix;
// load the image from disk
source = cairo_image_surface_create_from_png ("/home/henri/dev/art_horiz_bck.png");
source_pattern = cairo_pattern_create_for_surface (source);
s_width = cairo_image_surface_get_width(source);
s_height = cairo_image_surface_get_height(source);
d_width = gtk_widget_get_allocated_width (widget);
d_height = gtk_widget_get_allocated_height (widget);
cairo_surface_destroy (source);
// rotate around center of image
cairo_matrix_init_identity (&matrix)
cairo_matrix_translate (&matrix, s_width/2.0, s_height/2.0)
cairo_matrix_rotate (&matrix, deg2rad(30));
cairo_matrix_translate (&matrix, -(s_width-d_width)/2.0, -(s_height-d_height)/2.0)
cairo_pattern_set_matrix (source_pattern, &matrix);
// copy this rectangle to the destination surface
cairo_set_source (cr, source_pattern);
cairo_pattern_destroy (source_pattern);
cairo_rectangle (cr, 0, 0, 320, 200);
cairo_fill (cr);
return FALSE;
}
Related
I modified the "Simple example" example in GLFW3.0.4 in Mac OSX 10.8 as an XCode 4.6 project (runs fine when unchanged). I am having a (2D) rectangle drawn with an external library (which draws via shaders). I can see the rectangle but If I draw the sample triangle (immediate drawn) before it, the triangle is seen in the first splash (frame) and then it is lost. If I try to draw it after, the triangle is never seen. I can only see the rectangle and I don't know what settings/states the library is changing!
I tried to inspect the application with OpenGL Profiler. Stopped before CGLFlushDrawable and could not find the triangle in any of the buffers (front, back, depth, stencil).
Am I doing something (prominently) wrong? The profiler allows only gl-function breakpoints. How can I debug this (more efficiently) and find the problem.
Here is (much of the changed parts of) the code:
void glfw2DViewport(GLFWwindow * window) {
float ratio;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ratio = width / (float) height;
glViewport(0, 0, width, height);
glClearColor(0.8, 0.8, 0.8, 1.0); // Lets see if something black is drawn!!
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// eye is at 0,0,0 looking to positive Z, -1(behind) to 1 are clipping planes:
// https://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml
glOrtho(ratio, -ratio, -1.f, 1.f, 1.0f, -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// ----- 2D settings -----
glfwSwapInterval(1);
glEnable(GL_SMOOTH);
glDisable(GL_DEPTH_TEST);
//glDisable(GL_STENCIL_TEST); // Disabling changed nothing!!
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(5.0f);
glEnable(GL_LINE_SMOOTH);
glPointSize(5.0f);
glEnable(GL_POINT_SMOOTH);
}
int main(void) {
GLFWwindow* window;
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
if (!window) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
glfw2DViewport(window);
//...
while (!glfwWindowShouldClose(window)) {
glMatrixMode(GL_MODELVIEW_MATRIX);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
//drawUnitTriangle(); // can be seen just in the first frame!
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); // A vain attempt?
glPushAttrib(GL_ALL_ATTRIB_BITS); // Another vain attempt??
external_library_identity_matrix();
external_library_rectangle(POS,RED);
external_library_flush();
glPopAttrib();
glPopClientAttrib();
// Other vain attempts:
glfwMakeContextCurrent(window);
glMatrixMode(GL_MODELVIEW_MATRIX);
glLoadIdentity();
drawUnitTriangle(); // Nothing is Drawn!!
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
Are you sure the posted code is exactly what you are building with? If that's true, please check the argument of glMatrixMode() it should be GL_MODELVIEW, not GL_MODELVIEW_MATRIX. There are two places where you set it like this.
Since you already have glfw2DViewport(), why don't you put it in the while loop and delete other model view setting codes?
I need to make an interactive graph using MFC
It will be sort of like an equalizer control, where the user should be able to click on a point on the equalizer drag it to change it's y axis value
I am also just starting to learn MFC
Upto this point I have used CPaintDC in the OnPaint() function to draw the graph in a dialog box. For now the graph is very simple, with rectangle boarder, filled with white colour, and 4 points on the graph. I use OnMouseMove() function to know if the cursor is inside the graph area and OnLButtonDown() function to know where the user has clicked. If the user has clicked at a position which implies I want to change the y axis value of the graph point at that location, I repaint the figure using Invalidate() and calling OnPaint() inside OnLButtonDown(). However, every time the graph has to update, I can see a flicker. it is not a problem now, but I will need to extend this graph so that it has at least 64 changeable points, with the ability to change the y axis value for a point by dragging instead of just clicking where I want it to go. Will the flickering problem increase as I increase the number of points and the complexity of the appearance of the graph? The graph will need to have axes, gridlines, labels, etc later on. Is the flickering something I should be concerned about? Is there any way I can prevent it?
----UPDATE ----
This is how I updated my OnPaint() function according to how I understood CodeDreamer's suggestion
void Cgraph_on_dlgboxDlg::OnPaint()
{
CPaintDC dc_blt(this);
CDC dc;
CBitmap bmpDC;
CRect rcClient;
GetClientRect(rcClient);
if (IsIconic())
{
// CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
dc.CreateCompatibleDC(&dc);
bmpDC.CreateCompatibleBitmap(&dc, theGraph.width,theGraph.height );
dc.SelectObject(&bmpDC);
CPen pen;
COLORREF pencolour = RGB(0, 0, 0);
COLORREF brushcolour = RGB(0, 0, 255);
COLORREF graphColour = RGB(0, 0, 150);
// Draw boarder
pen.CreatePen(PS_SOLID, 3, pencolour);
// CBrush brush(HS_CROSS, brushcolour);
dc.SetBkMode(TRANSPARENT);
dc.SetMapMode(MM_TEXT);
dc.SetViewportOrg(theGraph.x1, theGraph.y1);
dc.SelectObject(&pen);
// Draw graph boundary
CPoint point1(0,0);
point1.x = 0;
point1.y = 0;
CPoint point2(0,0);
point2.x = point1.x + theGraph.width;
point2.y = point1.y + theGraph.height;
dc.Rectangle(CRect(point1, point2));
pen.DeleteObject();
// Draw Horizontal at 0
pen.CreatePen(PS_SOLID, 1, pencolour);
dc.SelectObject(&pen);
dc.MoveTo(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
dc.LineTo(theGraph.width, theGraph.height - ORG_DIST_FROM_BOTTOM);
pen.DeleteObject();
dc.SetViewportOrg(theGraph.x1, theGraph.y1 + theGraph.height - ORG_DIST_FROM_BOTTOM); // dc.SetViewportOrg() always works relative to the clinet origin
// Draw graph line
pen.CreatePen(PS_SOLID, 2, graphColour);
dc.SelectObject(&pen);
for(int i = 0; i<NUM_OF_SECTIONS_IN_GRAPH; i++){
dc.MoveTo(graphSamplePoints[i].x, graphSamplePoints[i].y);
dc.LineTo(graphSamplePoints[i+1].x, graphSamplePoints[i+1].y);
}
// draw circles at graph sample points
for(int i = 0; i<NUM_OF_POINTS_IN_GRAPH; i++){
CIRCLE(dc, graphSamplePoints[i].x, graphSamplePoints[i].y, GRP_SMP_RAD);
}
// dc_blt.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dc, 0, 0, SRCCOPY);
dc_blt.BitBlt(theGraph.x1,theGraph.y1,theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);
}
I will need to change the origins of the viewport a number of times and my guess is that this could be one of the reasons for the error. Any suggestions will be welcome.
This is what my output look like without double buffering
This is what it looks like with my attempt at double buffering
In this case, a general solution is 'double buffering'.
The principle is that it creates a compatible memory dc for drawing in advance, and when drawing is ended, it outputs on screen dc.
A code sample is below.
//in OnPaint() function
CPaintDC dc(this);
CDC dcMem;
CBitmap bmpDC;
CRect rcClient;
GetClientRect(&rcClient);
dcMem.CreateCompatibleDC(pDC);
bmpDC.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height());
dcMem.SelectObject(&bmpDC);
CRect rect(0, 0, 100, 200);
dcMem.Rectangle(rect);
dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);
A couple of references are below.
introduction
another reference
I hope this will help you a little.
Try the CMemDC MFC class.
In your OnPaintFunction you would have this:
CPaintDC DC(this);
CMemDC mDC(&DC, this);
// now use mDC instead of DC
Also look here fore more samples and explanations.
I am trying to make an interactive graph using MFC where the y axis of a sample point in the graph can be changed using mouse click. I implemented double buffering using this tutorial
enter link description here. I should also point out that I need to change the origins of the viewport from time to time for my program. However, when I click on the graph for the sample point to be updated, I can still see it flicker. It's not an inconvenience, but I need to extend this graph to include lots of sample points and other features such as gridlines, axes, labels, boundary areas, etc and I am worried that the flickering might become a problem for me in the future as the size of this project grows. Implementing double buffering did not seem to make any changes to the output. Moreover, now that I have implemented double buffering, the program seems to stop in the middle of execution (when I am running it in Debug mode in Visual Studio) with this error:
Unhandled exception at 0xffffff3a in graph_on_dlgbox.exe: 0xC0000005: Access violation reading location 0xffffff3a.
I am still not sure what causes it to appear, but seems to happen if I start randomly clicking around the graph area rapidly. Since I have not seen this error (yet) in my code that does not use double buffering, I am assuming it has something to do with the double buffering code, but I am not sure.
Anyway I would like to tackle this one problem at a time, and the first problem is the flicker. Here is my code without double buffering:
void Cgraph_on_dlgboxDlg::OnPaint()
{
CPaintDC dc(this);
if (IsIconic())
{
// CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
CPen pen;
COLORREF pencolour = RGB(0, 0, 0);
COLORREF brushcolour = RGB(0, 0, 255);
COLORREF graphColour = RGB(0, 0, 150);
// Draw boarder
pen.CreatePen(PS_SOLID, 3, pencolour);
// CBrush brush(HS_CROSS, brushcolour);
dc.SetBkMode(TRANSPARENT);
dc.SetMapMode(MM_TEXT);
dc.SetViewportOrg(theGraph.x1, theGraph.y1);
// Dc.SetViewportExt(theGraph.width, theGraph.height);
dc.SelectObject(&pen);
// dc.SelectObject(&brush);
// Draw graph boundary
CPoint point1(0,0);
point1.x = 0;
point1.y = 0;
CPoint point2(0,0);
point2.x = point1.x + theGraph.width;
point2.y = point1.y + theGraph.height;
dc.Rectangle(CRect(point1, point2));
pen.DeleteObject();
// Draw Horizontal at 0
pen.CreatePen(PS_SOLID, 1, pencolour);
dc.SelectObject(&pen);
dc.MoveTo(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
dc.LineTo(theGraph.width, theGraph.height - ORG_DIST_FROM_BOTTOM);
pen.DeleteObject();
// Shift overall graph origin from top left corner to beginning of this horizontal line
dc.SetViewportOrg(theGraph.x1, theGraph.y1 + theGraph.height - ORG_DIST_FROM_BOTTOM); // dc.SetViewportOrg() always works relative to the clinet origin
// Draw graph line
pen.CreatePen(PS_SOLID, 2, graphColour);
dc.SelectObject(&pen);
for(int i = 0; i<NUM_OF_SECTIONS_IN_GRAPH; i++){
dc.MoveTo(graphSamplePoints[i].x, graphSamplePoints[i].y);
dc.LineTo(graphSamplePoints[i+1].x, graphSamplePoints[i+1].y);
}
// draw circles at graph sample points
for(int i = 0; i<NUM_OF_POINTS_IN_GRAPH; i++){
CIRCLE(dc, graphSamplePoints[i].x, graphSamplePoints[i].y, GRP_SMP_RAD);
}
}
and here is the modified version with double buffering:
void Cgraph_on_dlgboxDlg::OnPaint()
{
// /*****
CPaintDC dc_blt(this);
CDC dc;
CBitmap bmpDC;
// CRect rcClient;
// GetClientRect(rcClient);
if (IsIconic())
{
// CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
dc.CreateCompatibleDC(&dc_blt);
// bmpDC.CreateCompatibleBitmap(&dc_blt, rcClient.Width(),rcClient.Height());
bmpDC.CreateCompatibleBitmap(&dc_blt, theGraph.width,theGraph.height );
dc.SelectObject(&bmpDC);
// ----------- After this point, do all operations considering (0,0) to be the origin of the bitmap
// consider bitmap coordinates a device coordinates for Viewport operations
CPen pen;
COLORREF pencolour = RGB(0, 0, 0);
COLORREF brushcolour = RGB(0, 0, 255);
COLORREF graphColour = RGB(0, 0, 150);
// Draw boarder
pen.CreatePen(PS_SOLID, 3, pencolour);
// CBrush brush(HS_CROSS, brushcolour);
dc.SetBkMode(TRANSPARENT);
dc.SetMapMode(MM_TEXT);
// dc.SetViewportOrg(theGraph.x1, theGraph.y1);
// dc.SetViewportExt(theGraph.width, theGraph.height);
dc.SelectObject(&pen);
// dc.SelectObject(&brush);
// Draw graph boundary
CPoint point1(0,0);
point1.x = 0;
point1.y = 0;
CPoint point2(0,0);
point2.x = point1.x + theGraph.width;
point2.y = point1.y + theGraph.height;
dc.Rectangle(CRect(point1, point2));
pen.DeleteObject();
// Draw Horizontal at 0
pen.CreatePen(PS_SOLID, 1, pencolour);
dc.SelectObject(&pen);
dc.MoveTo(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
dc.LineTo(theGraph.width, theGraph.height - ORG_DIST_FROM_BOTTOM);
pen.DeleteObject();
// Shift overall graph origin from top left corner to beginning of this horizontal line
// dc.SetViewportOrg(theGraph.x1, theGraph.y1 + theGraph.height - ORG_DIST_FROM_BOTTOM); // dc.SetViewportOrg() always works relative to the client area origin
// New origin defined in terms of the Bitmap's origin
dc.SetViewportOrg(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
// Draw graph line
pen.CreatePen(PS_SOLID, 2, graphColour);
dc.SelectObject(&pen);
for(int i = 0; i<NUM_OF_SECTIONS_IN_GRAPH; i++){
dc.MoveTo(graphSamplePoints[i].x, graphSamplePoints[i].y);
dc.LineTo(graphSamplePoints[i+1].x, graphSamplePoints[i+1].y);
}
// draw circles at graph sample points
for(int i = 0; i<NUM_OF_POINTS_IN_GRAPH; i++){
CIRCLE(dc, graphSamplePoints[i].x, graphSamplePoints[i].y, GRP_SMP_RAD);
}
dc.SetViewportOrg(0, 0);
// dc_blt.BitBlt(rcClient.left+100,rcClient.top+50,rcClient.Width(), rcClient.Height(), &dc, 0, 0, SRCCOPY);
// dc_blt.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dc, theGraph.x1, theGraph.y1, SRCCOPY);
dc_blt.BitBlt(theGraph.x1,theGraph.y1, theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);
// --- Bring the bitmap to this particular location on screen specified by (theGraph.x1,theGraph.y1, theGraph.width, theGraph.height)
// dc_blt.BitBlt(0,0,theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);
// dc_blt.BitBlt(theGraph.x1,theGraph.y1,theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);
// *****/
m_bMyDraw = FALSE;
}
Here is a sample screenshot of the output:
The y axis values of the sample points on the graph can be changed by clicking, and the program redraws the graph after every click by calling InvalidateRect() with the area of the graph as the rectangle to be repainted.
The coordinates of the sample points are stored in a array of CPoint objects, and it's members are updated every time the graph is clicked at the appropriate area. The graph then repaints, due to the call to InvalidateRect(), but with a flicker; unless of course, the program crashes in debug mode with this error:
How do I remove the flickering?
---- UPDATE ----
BOOL Cgraph_on_dlgboxDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
if ( m_bMyDraw )
return TRUE;
else
return CDialogEx::OnEraseBkgnd(pDC);
}
This function has been made this way since it was done like this in the tutorial I mentioned earlier
------ UPDATE 2 ----------
If I just put return TRUE; in the body of the above function, the flicker seems to vanish, but now the output looks like this
The dialog box background seems to have taken the contents of my Visual Studio window. How do I prevent this?
You're close! The idea of double buffering is to paint every pixel in your window exactly once. If it is painted zero times, artifacts like Visual Studio remain. And if it is painted a first time, and then painted again with a different color, you will see flicker. So, to make sure every pixel is painted, create your compatible dc the full width and height of the window so that when it is copied to the CPaintDC, it covers the entire area and not just theGraph. Keep returning TRUE in OnEraseBkgnd, so that the pixels are not first painted in OnEraseBkgnd, and then again in OnPaint.
Two things :
Have you made sure OnEraseBkgnd() just returns TRUE and doesn't call the base class to erase the view?
You don't need to do all that drawing for the double buffering in OnPaint(). All you need to do in the OnPaint() is the BitBlt. You can do the drawing to the memory bitmap in a UpdateRect() function which gets called whenever you need to update the screen, which then calls InvalidateRect() to update the screen. I've posted some code about a flicker-free double buffering method I've used many times here which might help.
The way that flicker prevention work is that first you return TRUE from OnEraseBkgnd to suppress the default erase. But then your OnPaint code must include a full erase of the window. You don't do that so you get the background image of your source code or whatever was there before. So add a FillSolidRect call to your OnPaint to clear the window.
Your creation of a CPaintDC before calling CDialogEx::OnPaint destroys the dialog's ability to properly paint itself, since that function also creates a CPaintDC. But only one call to CPaintDC is permitted for each paint message. To avoid this problem you need a completely different approach. The dialog should have a picture control on it (a CStatic), and you should paint your graph in a class you derive from CStatic.
I'm getting the problem my sprites leave a trail behind when i move them.
Tried drawning a BG with every refresh but then it start flickering.
This is what i do
// ...
int main(int argc, char *argv[])
{
BITMAP *buffer = NULL;
BITMAP *graphics = NULL;
buffer = create_bitmap(SCREEN_W, SCREEN_H);
graphics = load_bitmap("my_graphics.bmp", NULL);
clear_to_color(screen, makecol(0, 0, 0));
clear_to_color(buffer, makecol(0, 0, 0));
while(!key[KEY_ESC])
{
// ...
render_map(100,100);
// ...
}
}
void render_map(int w, int h)
{
// ...
for(int i=0;i < w * h;i++)
{
masked_blit(graphics, buffer, 0, 0, pos_x, pos_y, 32, 32);
}
// ...
blit(buffer, screen, camera_x,camera_y,0,0,SCREEN_W, SCREEN_H);
clear_to_color(buffer, makecol(0, 0, 0));
}
Thanks in advance for any help
Your code is a little hard to read, and you've left out big pieces of it. So it's hard to say for sure, but this line looks suspicious:
blit(buffer, screen, camera_x,camera_y,0,0,SCREEN_W, SCREEN_H);
When using a buffer, you typically will always be calling it like:
blit(buffer, screen, 0,0, 0,0, SCREEN_W,SCREEN_H);
and that is the only time you ever draw to the screen. So the steps are:
clear the buffer (by drawing a background image, tileset, color, etc)
draw everything to the buffer
copy the buffer to the screen
repeat
I have a piece of code here.
This is a camera capture application using OpenCV and Qt(for GUI).
void MainWindow::on_pushButton_clicked()
{
cv::VideoCapture cap(0);
if(!cap.isOpened()) return;
//namedWindow("edges",1);
QVector<QRgb> colorTable;
for (int i = 0; i < 256; i++) colorTable.push_back(qRgb(i, i, i));
QImage img;
img.setColorTable(colorTable);
for(;;)
{
cap >> image;
cvtColor(image, edges, CV_BGR2GRAY);
GaussianBlur(edges, edges, cv::Size(7,7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
//imshow("edges", edges);
if(cv::waitKey(30) >= 0) break;
// change color channel ordering
//cv::cvtColor(image,image,CV_BGR2RGB);
img = QImage((const unsigned char*)(edges.data),
image.cols,image.rows,QImage::Format_Indexed8);
// display on label
ui->label->setPixmap(QPixmap::fromImage(img,Qt::AutoColor));
// resize the label to fit the image
ui->label->resize(ui->label->pixmap()->size());
}
}
Initially "edges" is displayed in red with green background.Then it switches to blue background. This switching is happening randomly.
How can I display white edges in a black background in a stable manner.
In short, add the img.setColorTable(colorTable); just before the // display on labelcomment.
For more details, you create your image and affect the color table at the begining of your code:
QImage img;
img.setColorTable(colorTable);
Then in the infinite loop, you are doing the following:
img = QImage((const unsigned char*)(edges.data), image.cols, image.rows, QImage::Format_Indexed8);
What happens is that you destroy the image created at the begining of your code, the color map for this new image is not set and thus uses the default resulting in a colored output.