Allegro, sprites leaving trail - allegro

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

Related

GTK++/Cairo: load png, rotate, and copy rectangle to surface

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;
}

changing alpha value in SDL_SetRenderDrawColor doesn't effect anything. SDL2

This code shows just a simple window with a color:
#include<SDL.h>
SDL_Window* g_pWindow = 0;
SDL_Renderer* g_pRenderer = 0;
int main(int argc, char* args[])
{
if (SDL_Init(SDL_INIT_EVERYTHING) >= 0)
{
g_pWindow = SDL_CreateWindow("Chapter 1: Setting up SDL",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_SHOWN);
if (g_pWindow != 0)
{
g_pRenderer = SDL_CreateRenderer(g_pWindow, -1, 0);
}
}
else
{
return 1; // sdl could not initialize
}
SDL_SetRenderDrawColor(g_pRenderer, 80, 80, 253, 0);
// clear the window to black
SDL_RenderClear(g_pRenderer);
// show the window
SDL_RenderPresent(g_pRenderer);
// set a delay before quitting
SDL_Delay(2000);
// clean up SDL
SDL_Quit();
return 0;
}
I'm testing to see what happens when I change the alpha factor in SDL_SetRenderDrawColor(g_pRenderer, 80, 80, 253, 0). When I change the alpha value from 0 to 255 it doesn't effect anything.
What's the problem here?
First of all, you haven't enabled blending (e.g. SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);).
But anyway, it makes no sense for clear operation to use blending, and I bet SDL_RenderClear ignores it.
If you want fullscreen blend, you should draw fullscreen rectangle with SDL_RenderFillRect.
Writing this line will make alpha values have effect on transparency:
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
No need to rewrite every time & for every effert, just write once before using transparency
see documentation on more blend modes:
https://wiki.libsdl.org/SDL2/SDL_BlendMode

accumulatedweight throws cv:Exception error

I am new to OpenCV and trying to find contours and draw rectangle on them, here's my code but its throwing cv::Exception when it comes to accumulatedweighted().
i tried to make both src(Original Image) and dst(background) by converting to CV_32FC3 and then finding avg using accumulatedweighted.
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis is a Example to implement CAMSHIFT to detect multiple motion objects.\n";
}
Rect rect;
VideoCapture capture;
Mat currentFrame, currentFrame_grey, differenceImg, oldFrame_grey,background;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
bool first = true;
int main(int argc, char* argv[])
{
//Create a new movie capture object.
capture.open(0);
if(!capture.isOpened())
{
//error in opening the video input
cerr << "Unable to open video file: " /*<< videoFilename*/ << endl;
exit(EXIT_FAILURE);
}
//capture current frame from webcam
capture >> currentFrame;
//Size of the image.
CvSize imgSize;
imgSize.width = currentFrame.size().width; //img.size().width
imgSize.height = currentFrame.size().height; ////img.size().height
//Images to use in the program.
currentFrame_grey.create( imgSize, IPL_DEPTH_8U);//image.create().
while(1)
{
capture >> currentFrame;//VideoCapture& VideoCapture::operator>>(Mat& image)
//Convert the image to grayscale.
cvtColor(currentFrame,currentFrame_grey,CV_RGB2GRAY);//cvtColor()
currentFrame.convertTo(currentFrame,CV_32FC3);
background = Mat::zeros(currentFrame.size(), CV_32FC3);
accumulateWeighted(currentFrame,background,1.0,NULL);
imshow("Background",background);
if(first) //Capturing Background for the first time
{
differenceImg = currentFrame_grey.clone();//img1 = img.clone()
oldFrame_grey = currentFrame_grey.clone();//img2 = img.clone()
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);//convertscaleabs()
first = false;
continue;
}
//Minus the current frame from the moving average.
absdiff(oldFrame_grey,currentFrame_grey,differenceImg);//absDiff()
//bluring the differnece image
blur(differenceImg, differenceImg, imgSize);//blur()
//apply threshold to discard small unwanted movements
threshold(differenceImg, differenceImg, 25, 255, CV_THRESH_BINARY);//threshold()
//find contours
findContours(differenceImg,contours,hierarchy,CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //findcontours()
//draw bounding box around each contour
//for(; contours! = 0; contours = contours->h_next)
for(int i = 0; i < contours.size(); i++)
{
rect = boundingRect(contours[i]); //extract bounding box for current contour
//drawing rectangle
rectangle(currentFrame, cvPoint(rect.x, rect.y), cvPoint(rect.x+rect.width, rect.y+rect.height), cvScalar(0, 0, 255, 0), 2, 8, 0);
}
//New Background
convertScaleAbs(currentFrame_grey, oldFrame_grey, 1.0, 0.0);
//display colour image with bounding box
imshow("Output Image", currentFrame);//imshow()
//display threshold image
imshow("Difference image", differenceImg);//imshow()
//clear memory and contours
//cvClearMemStorage( storage );
//contours = 0;
contours.clear();
//background = currentFrame;
//press Esc to exit
char c = cvWaitKey(33);
if( c == 27 ) break;
}
// Destroy All Windows.
destroyAllWindows();
return 0;
}
Please Help to solve this.
you might want to RTFM before asking here.
so, you missed the alpha param as well as the dst Mat in your call to addWeighted
Mat dst;
accumulateWeighted(currentFrame, 0.5 background, 0.5, 0, dst);
also, no idea, what the whole thing should achieve. adding up the current frame before diffing it does not make any sense to me.
if you planned to do background separation, throw it all away, and use one of the builtin backgroundsubtractors instead

Flicker does not reduce after double buffering in MFC CPaintDC

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.

UpdateLayeredWindow and DrawText

I'm using UpdateLayeredWindow to display an application window. I have created my own custom buttons and i would like to create my own static text. The problem is that when i try to draw the text on the hdc, the DrawText or TextOut functions overwrite the alpha channel of my picture and the text will become transparent. I tried to find a solution to this but i could not find any. My custom controls are designed in such way that they will do all the drawing in a member function called Draw(HDC hDc), so they can only access the hdc. I would like to keep this design. Can anyone help me? I am using MFC and i would want to achieve the desired result without the use of GDI+.
I know this is an old post ... but I just had this very same problem ... and it was driving me CRAZY.
Eventually, I stumbled upon this post by Mike Sutton to the microsoft.public.win32.programmer.gdi newsgroup ... from almost 7 years ago!
Basically, the DrawText (and TextOut) do not play nicely with the alpha channel and UpdateLayeredWindow ... and you need to premultiply the R, G, and B channels with the alpha channel.
In Mike's post, he shows how he creates another DIB (device independent bitmap) upon which he draws the text ... and alpha blends that into the other bitmap.
After doing this, my text looked perfect!
Just in case, the link to the newsgroup post dies ... I am going to include the code here. All credit goes to Mike Sutton (#mikedsutton).
Here is the code that creates the alpha blended bitmap with the text on it:
HBITMAP CreateAlphaTextBitmap(LPCSTR inText, HFONT inFont, COLORREF inColour)
{
int TextLength = (int)strlen(inText);
if (TextLength <= 0) return NULL;
// Create DC and select font into it
HDC hTextDC = CreateCompatibleDC(NULL);
HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont);
HBITMAP hMyDIB = NULL;
// Get text area
RECT TextArea = {0, 0, 0, 0};
DrawText(hTextDC, inText, TextLength, &TextArea, DT_CALCRECT);
if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top))
{
BITMAPINFOHEADER BMIH;
memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER));
void *pvBits = NULL;
// Specify DIB setup
BMIH.biSize = sizeof(BMIH);
BMIH.biWidth = TextArea.right - TextArea.left;
BMIH.biHeight = TextArea.bottom - TextArea.top;
BMIH.biPlanes = 1;
BMIH.biBitCount = 32;
BMIH.biCompression = BI_RGB;
// Create and select DIB into DC
hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB);
if (hOldBMP != NULL)
{
// Set up DC properties
SetTextColor(hTextDC, 0x00FFFFFF);
SetBkColor(hTextDC, 0x00000000);
SetBkMode(hTextDC, OPAQUE);
// Draw text to buffer
DrawText(hTextDC, inText, TextLength, &TextArea, DT_NOCLIP);
BYTE* DataPtr = (BYTE*)pvBits;
BYTE FillR = GetRValue(inColour);
BYTE FillG = GetGValue(inColour);
BYTE FillB = GetBValue(inColour);
BYTE ThisA;
for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) {
for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) {
ThisA = *DataPtr; // Move alpha and pre-multiply with RGB
*DataPtr++ = (FillB * ThisA) >> 8;
*DataPtr++ = (FillG * ThisA) >> 8;
*DataPtr++ = (FillR * ThisA) >> 8;
*DataPtr++ = ThisA; // Set Alpha
}
}
// De-select bitmap
SelectObject(hTextDC, hOldBMP);
}
}
// De-select font and destroy temp DC
SelectObject(hTextDC, hOldFont);
DeleteDC(hTextDC);
// Return DIBSection
return hMyDIB;
}
Here is the code that drives the CreateAlphaTextBitmap method:
void TestAlphaText(HDC inDC, int inX, int inY)
{
const char *DemoText = "Hello World!\0";
RECT TextArea = {0, 0, 0, 0};
HFONT TempFont = CreateFont(50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial\0");
HBITMAP MyBMP = CreateAlphaTextBitmap(DemoText, TempFont, 0xFF);
DeleteObject(TempFont);
if (MyBMP)
{
// Create temporary DC and select new Bitmap into it
HDC hTempDC = CreateCompatibleDC(inDC);
HBITMAP hOldBMP = (HBITMAP)SelectObject(hTempDC, MyBMP);
if (hOldBMP)
{
// Get Bitmap image size
BITMAP BMInf;
GetObject(MyBMP, sizeof(BITMAP), &BMInf);
// Fill blend function and blend new text to window
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0x80;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(inDC, inX, inY, BMInf.bmWidth, BMInf.bmHeight, hTempDC, 0, 0, BMInf.bmWidth, BMInf.bmHeight, bf);
// Clean up
SelectObject(hTempDC, hOldBMP);
DeleteObject(MyBMP);
DeleteDC(hTempDC);
}
}
}

Resources