CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 26
  1. #1
    Join Date
    May 2007
    Posts
    680

    Exclamation Inverting regions -

    Sup guys,

    How would you let a user select a region(create it) and then invert the contents?

    Thanks.

  2. #2
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    Sup guys,

    How would you let a user select a region(create it) and then invert the contents?

    Thanks.
    use the rubber banding method to draw a selection box on the screen

    on the WM_LBUTTONUP you can copy the last rubber band region to create your selected region.

    then if you want to invert the pixels in that region (assuming you are selecting a region of a bitmap that you are displaying on the screen) then loop through the pixels of the region and invert them. or there are loads of options for pixel manipulation in BitBlt if you want to do it then..

    if you are are a bit more specific about your application i can probably find some relevant code?

  3. #3
    Join Date
    May 2007
    Posts
    680

    Re: Inverting regions -

    Thanks for your post man, but I've never heard of the method you mentioned and still do not know how to work with anything related to bitmaps(I'm close though). But can't I just handle a WM_MOUSEMOVE message to make it record the current x and y of the mouse then use the function RedrawWindow to keep on sending my self a WM_PAINT message which then uses the coordinates given from WM_LBUTTONDOWN as the starting point and the coordinates from WM_MOUSEMOVE as the ending zone(with the function CreateRectRgn) then when I receive the WM_LBUTTONUP message I could use InvertRgn with the handle to the region that was last created in the WM_PAINT message?

  4. #4
    Join Date
    Nov 2004
    Location
    Pakistan
    Posts
    466

    Re: Inverting regions -

    1. Select a region? using mouse, if so then it includes drawing, do capture mouse events and draw in WM_PAINT.
    2. "invert the contents", I could not follow this.

    regards
    » Please 'Rate This Post' if it helped (encourage us to help you more)
    » Build GUI in minute using rad c++
    » Free IDE + GUI code generator - screenshot
    » Free WINAPI sourcecode and tutorials

  5. #5
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    Thanks for your post man, but I've never heard of the method you mentioned and still do not know how to work with anything related to bitmaps(I'm close though). But can't I just handle a WM_MOUSEMOVE message to make it record the current x and y of the mouse then use the function RedrawWindow to keep on sending my self a WM_PAINT message which then uses the coordinates given from WM_LBUTTONDOWN as the starting point and the coordinates from WM_MOUSEMOVE as the ending zone(with the function CreateRectRgn) then when I receive the WM_LBUTTONUP message I could use InvertRgn with the handle to the region that was last created in the WM_PAINT message?
    that sounds basically like i have done it.

    what exactly do you want to invert though? i assume you are just doing it to see how it works, but if you are inverting pixels then it should really be in an area of the screen that you are responsible for (i.e. within the client area of your window).

    the approach i have been using to display bitmaps is not too hard - i'm sure there are better tutorials around, but to give you an idea:
    i create an 'image area' window that displays bitmaps
    Code:
    HWND hDisplayArea = CreateWindowEx(0,
    				"STATIC", "Image Area", 
    				WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_BITMAP, 
    				0, 0, WIDTH, HEIGHT, hDispWnd, (HMENU)IDC_DISPLAY_AREA,
    				g_hInstance, NULL);
    in other parts of the code i edit the pixels of a 'display bitmap' and select it into a 'display DC'
    Code:
    LPBITMAPINFO lpbi = CreateBitmapStructure();
    BYTE *bArray = FillTempPixelArray(Array, index);
    SetDIBits(hdc, m_hbmDisp, 0, HEIGHT, bArray, lpbi, DIB_RGB_COLORS);
    delete[] lpbi;
    delete[] bArray;
    those functions are just ones i wrote (it only deals with 24-bit and 8-bit bitmaps, and then they are only grayscale)
    if you want colour then replace the for(int k=0; k<3; k++) loop in FillTempPixelArray() to write separate RBG values. but my input array is greyscale so there was no point for me..
    Code:
    // ============================================================
    LPBITMAPINFO CreateBitmapStructure()
    {
    	LPBITMAPINFO lpbi;
    
    	if (BITS == 8)
    	{
    		lpbi = (LPBITMAPINFO)new BYTE[sizeof(BITMAPINFOHEADER)
    									+ (256*sizeof(RGBQUAD))];
    		lpbi->bmiHeader.biBitCount = 8;
    	}
    	else
    	{
    		lpbi = (LPBITMAPINFO)new BYTE[sizeof(BITMAPINFOHEADER)];
    		lpbi->bmiHeader.biBitCount = 24;
    	}
    	lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    	lpbi->bmiHeader.biWidth = WIDTH;
    	lpbi->bmiHeader.biHeight = HEIGHT;
    	lpbi->bmiHeader.biPlanes = 1;
    	lpbi->bmiHeader.biCompression = BI_RGB;
    	lpbi->bmiHeader.biSizeImage = 0;
    	lpbi->bmiHeader.biXPelsPerMeter = 0;
    	lpbi->bmiHeader.biYPelsPerMeter = 0;
    	lpbi->bmiHeader.biClrUsed = 0;
    	lpbi->bmiHeader.biClrImportant = 0;
    
    	if (BITS == 8)
    	{
    		for (int i=0;i<256;i++)
    		{
    			lpbi->bmiColors[i].rgbRed		= (BYTE)i;
    			lpbi->bmiColors[i].rgbBlue		= (BYTE)i;
    			lpbi->bmiColors[i].rgbGreen		= (BYTE)i;
    			lpbi->bmiColors[i].rgbReserved	= (BYTE)0;
    		}
    	}
    	return lpbi;
    }
    // ======================================================
    int CalculateEndbytes(int bitcount, int width)
    {
    	int endbytes = 0;
    	if (bitcount == 8)
    	{
    		endbytes = 4 - (width % 4);
    		if (endbytes == 4){endbytes = 0;}
    		if (endbytes < 0 || endbytes > 3)
    			MessageBox(g_hWnd,
    			"ERROR calculating endbytes",
    			"Message",MB_OK);
    	}
    	if (bitcount == 24)
    	{
    		endbytes = 4 - (3*width % 4);
    		if (endbytes == 4){endbytes = 0;}
    		assert (endbytes >= 0 && endbytes <= 3);
    	}
    	return endbytes;
    }
    // ======================================================
    BYTE* FillTempPixelArray(BYTE *myArray, int myindex)
    {
    	int blocksize, endbytes;
    
    	endbytes = CalculateEndbytes(BITS, WIDTH);
    	if (BITS == 8)
    		blocksize = HEIGHT * (WIDTH+endbytes);
    	else
    		blocksize = HEIGHT * ((3*WIDTH)+endbytes);
    
    	BYTE *bArray = new BYTE[blocksize];
    	for(int i=0; i<HEIGHT; i++)
    	{
    		for(int j=0; j<WIDTH; j++)
    		{
    			if (BITS == 8)
    			{
    				bArray[((HEIGHT-1-i)*(WIDTH+endbytes))+j] = 
    					myArray[(myindex*AREA) + i*WIDTH + j];
    			}
    			else
    			{
    				for(int k=0; k<3; k++)
    				{
    					bArray[((HEIGHT-1-i)*((3*WIDTH)+endbytes))+(3*j)+k] = 
    						myArray[(myindex*AREA) + i*WIDTH + j];
    				}
    			}
    		}
    		if (endbytes != 0)
    		{
    			int n = 0;
    			for(int b=0; b<endbytes; b++)
    			{
    				if (BITS == 8)
    					bArray[((HEIGHT-1-i)*(WIDTH+endbytes))+j+n++] = 0;
    				else
    					bArray[((HEIGHT-1-i)*((3*WIDTH)+endbytes))+(3*j)+n++] = 0;
    			}
    		}
    	}	
    	return bArray;
    }
    // ======================================================
    then you have the DC all ready to draw with the bitmap selected so on the WM_PAINT message for the window just do
    Code:
    PAINTSTRUCT ps;
    HWND hwnd = GetDlgItem(g_hDispWnd,IDC_DISPLAY_AREA);
    
    BeginPaint(hwnd, &ps);
    BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top,
    		ps.rcPaint.right - ps.rcPaint.left,
    		ps.rcPaint.bottom - ps.rcPaint.top,
    		hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
    EndPaint(hwnd, &ps);
    so back to the context of selecting regions: what i then do is draw a rubber band selection box (look up SetROP2() ) in a temporary DC as i drag the mouse over the image and then once the mouse button is released i draw the selected region onto the display DC in green. this will then be updated when WM_paint is called.
    Code:
    void DrawRegion(RECT myRect)
    {
    	COLORREF oldColRef = GetDCPenColor(hdc);
    	SetDCPenColor(hdc,RGB(0x66,0xff,0x00));
    	SelectObject(hdc,GetStockObject(HOLLOW_BRUSH));
    
    	HRGN hrgn = CreateRectRgn(myRect.left,
    		myRect.top,myRect.right,myRect.bottom);
    	FrameRgn(hdc,hrgn,(HBRUSH)GetStockObject(DC_PEN),1,1);
    	DeleteObject(hrgn);
    	SetDCPenColor(hdc,oldColRef);
    }
    there is actually a 'refresh' function that updates all the pixels in the display DC bitmap and then checks to see if there is a selected region and draws it over the top. the refresh function then requests a WM_PAINT using InvalidateRgn() for the image area.

    so what i am trying to say, is that this way you can invert the pixels in your actual stored pixel array, then you can display your altered pixels on the screen.

    hope that was not too dull for you - but it sounds like you are just out to learn as much as you can! good luck.

  6. #6
    Join Date
    May 2007
    Posts
    680

    Re: Inverting regions -

    Thanks again for another post!

    Just to get rid of the confusion(caused by all the extra unknown functions used), could you please give me a example(full/compilable) that simply draws a line and then puts the lines used into a Memory Device Context then blits them to the screen.

    Thanks.

  7. #7
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    Thanks again for another post!

    Just to get rid of the confusion(caused by all the extra unknown functions used), could you please give me a example(full/compilable) that simply draws a line and then puts the lines used into a Memory Device Context then blits them to the screen.

    Thanks.
    here you go:

    the left mouse button draws with a brush on a 'permanent' memory DC, and the right mouse button draws on a temporary one that will be replaced as soon as the window is painted.

    Code:
    #define _WIN32_WINNT 0x0502
    #include <windows.h>
    
    #define IDC_DISPLAY_AREA	101
    
    #define WIDTH	400
    #define HEIGHT	300
    #define AREA	(WIDTH*HEIGHT)
    
    // Global Variables:
    HINSTANCE g_hInst;			// current instance
    
    HDC g_hdcTemp = NULL;		// Temp HDC - used when required
    
    HDC g_hdcMem = NULL;		// Memory HDC - intialised on window creation
    HBITMAP	g_hbm = NULL;		// HBM will be selected into Memory HDC
    HANDLE	g_hbmOld = NULL;	// Old HBM returned from Memory HDC
    
    // Foward declarations of functions included in this code module:
    ATOM				MyRegisterClass(HINSTANCE hInstance);
    BOOL				InitInstance(HINSTANCE, int);
    LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
    
    // ======================================================
    // ======================================================
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
     	// TODO: Place code here.
    	MSG msg;
    
    	// Initialize global strings
    	MyRegisterClass(hInstance);
    
    	// Perform application initialization:
    	if (!InitInstance (hInstance, nCmdShow)) 
    	{
    		return FALSE;
    	}
    
    	// Main message loop:
    	while (GetMessage(&msg, NULL, 0, 0)) 
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    // ======================================================
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
    	WNDCLASSEX wcex;
    
    	wcex.cbSize = sizeof(WNDCLASSEX); 
    
    	wcex.style			= CS_HREDRAW | CS_VREDRAW;
    	wcex.lpfnWndProc	= (WNDPROC)WndProc;
    	wcex.cbClsExtra		= 0;
    	wcex.cbWndExtra		= 0;
    	wcex.hInstance		= hInstance;
    	wcex.hIcon			= NULL;
    	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
    	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    	wcex.lpszMenuName	= NULL;
    	wcex.lpszClassName	= "MyWindowClass";
    	wcex.hIconSm		= NULL;
    
    	return RegisterClassEx(&wcex);
    }
    // ======================================================
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       HWND hWnd;
    
       g_hInst = hInstance; // Store instance handle in our global variable
    
       hWnd = CreateWindow("MyWindowClass", "This is a Window", WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT, 0, WIDTH+8, HEIGHT+34, NULL, NULL, hInstance, NULL);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    // ======================================================
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	PAINTSTRUCT ps;
    
    	switch (message) 
    	{
    		case WM_CREATE:
    			{
    			// Create Window that will be the only place you can draw on
    			HWND hDisplayArea = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
    				"STATIC", "Image Area", 
    				WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_BITMAP, 
    				0, 0, WIDTH, HEIGHT, hWnd, (HMENU)IDC_DISPLAY_AREA,
    				g_hInst, NULL);
    
    			// Initialise Memory DC
    			HDC MainDC = GetWindowDC(hWnd);
    			g_hdcMem = CreateCompatibleDC(MainDC);
    			// Select Bitmap into Memory DC
    			g_hbm = CreateCompatibleBitmap(GetWindowDC(hWnd), WIDTH, HEIGHT);
    			g_hbmOld = SelectObject(g_hdcMem, g_hbm);
    
    			// Create Bitmap Structure to fill the background of that window
    			LPBITMAPINFO lpbi = (LPBITMAPINFO)new BYTE[sizeof(BITMAPINFOHEADER)];
    			lpbi->bmiHeader.biBitCount = 32;
    			lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    			lpbi->bmiHeader.biWidth = WIDTH;
    			lpbi->bmiHeader.biHeight = HEIGHT;
    			lpbi->bmiHeader.biPlanes = 1;
    			lpbi->bmiHeader.biCompression = BI_RGB;
    			lpbi->bmiHeader.biSizeImage = 0;
    			lpbi->bmiHeader.biXPelsPerMeter = 0;
    			lpbi->bmiHeader.biYPelsPerMeter = 0;
    			lpbi->bmiHeader.biClrUsed = 0;
    			lpbi->bmiHeader.biClrImportant = 0;
    
    			// Create Bit Array for the Bitmap
    			BYTE *bArray =  new BYTE[4*AREA];	
    			for(int i=0; i<HEIGHT; i++)
    			{
    				for(int j=0; j<WIDTH; j++)
    				{
    					bArray[((HEIGHT-1-i)*4*WIDTH)+(4*j)+0] = 0xdd; //blue
    					bArray[((HEIGHT-1-i)*4*WIDTH)+(4*j)+1] = 0xdd; //green
    					bArray[((HEIGHT-1-i)*4*WIDTH)+(4*j)+2] = 0xdd; //red
    					bArray[((HEIGHT-1-i)*4*WIDTH)+(4*j)+3] = 0x00; //junk
    				}
    			}
    
    			// Set Bitmap Bits in Memory DC
    			SetDIBits(g_hdcMem, g_hbm, 0, HEIGHT, bArray, lpbi, DIB_RGB_COLORS);
    
    			delete[] lpbi;
    			delete[] bArray;
    			DeleteObject(hDisplayArea);
    			DeleteObject(MainDC);
    			}
    			break;
    		case WM_PAINT:
    			// Paint the Main Window
    			DefWindowProc(hWnd, message, wParam, lParam);
    			
    			// Paint the image area
    			{
    			HWND hwnd = GetDlgItem(hWnd, IDC_DISPLAY_AREA);
    				
    			BeginPaint(hwnd, &ps);
    			BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top,
    					ps.rcPaint.right - ps.rcPaint.left,
    					ps.rcPaint.bottom - ps.rcPaint.top,
    					g_hdcMem, // Global Mem DC
    					ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
    
    			EndPaint(hwnd, &ps);
    			DeleteObject(hwnd);
    			}
    			break;
    		
    		// DRAW GREEN BRUSH ON MEMORY DC WITH LEFT MOUSE BUTTON
    		case WM_LBUTTONDOWN:
    			{
    				int xp = LOWORD(lParam);
    				int yp = HIWORD(lParam);
    
    				if (   xp<=WIDTH  && xp>=0
    					&& yp<=HEIGHT && yp>=0 )
    				{
    					COLORREF colour = RGB(0x00,0xff,0x00); // GREEN
    					SelectObject(g_hdcMem,GetStockObject(DC_PEN));
    					SetDCPenColor(g_hdcMem,colour);
    					SelectObject(g_hdcMem,GetStockObject(DC_BRUSH));
    					SetDCBrushColor(g_hdcMem,colour);
    
    					int size = 10;
    					Ellipse(g_hdcMem, xp-size, yp+size, xp+size, yp-size);
    
    					HRGN DispRgn = CreateRectRgn(0,0,WIDTH,HEIGHT);
    					InvalidateRgn(hWnd,DispRgn,FALSE);
    					DeleteObject(DispRgn);
    				}
    			}
    			break;
    		case WM_LBUTTONUP:
    			{	
    			//  could manipulate pixels here or whatever
    
    				HRGN DispRgn = CreateRectRgn(0,0,WIDTH,HEIGHT);
    				InvalidateRgn(hWnd,DispRgn,FALSE);
    				DeleteObject(DispRgn);
    			}
    			break;
    
    		// DRAW RED BRUSH ON TEMP DC WITH RIGHT MOUSE BUTTON
    		case WM_RBUTTONDOWN:
    			{
    				int xp = LOWORD(lParam);
    				int yp = HIWORD(lParam);
    
    				if (   xp<=WIDTH  && xp>=0
    					&& yp<=HEIGHT && yp>=0 )
    				{
    					g_hdcTemp = GetWindowDC(GetDlgItem(hWnd,IDC_DISPLAY_AREA));
    
    					COLORREF colour = RGB(0xff,0x00,0x00); // RED
    					SelectObject(g_hdcTemp,GetStockObject(DC_PEN));
    					SetDCPenColor(g_hdcTemp,colour);
    					SelectObject(g_hdcTemp,GetStockObject(DC_BRUSH));
    					SetDCBrushColor(g_hdcTemp,colour);
    
    					int size = 10;
    					Ellipse(g_hdcTemp, xp-size, yp+size, xp+size, yp-size);
    				}
    			}
    			break;
    		case WM_RBUTTONUP:
    			{
    				// release the temp DC
    				if(g_hdcTemp != NULL)
    					ReleaseDC(hWnd, g_hdcTemp);
    				g_hdcTemp = NULL;
    
    				// Invalidate window here to clear the red line
    				// could also manipulate pixels here or whatever
    			}
    			break;
    
    		// DRAW BRUSHES AS THE MOUSE MOVES FOR EACH BUTTON DOWN
    		case WM_MOUSEMOVE:
    			{
    			int xp = LOWORD(lParam);
    			int yp = HIWORD(lParam);
    			if (GetKeyState(VK_LBUTTON)<0)
    			{
    				if (   xp<=WIDTH  && xp>=0
    					&& yp<=HEIGHT && yp>=0 )
    				{
    					int size = 10;
    					Ellipse(g_hdcMem, xp-size, yp+size, xp+size, yp-size);
    				}
    				HRGN DispRgn = CreateRectRgn(0,0,WIDTH,HEIGHT);
    				InvalidateRgn(hWnd,DispRgn,FALSE);
    				DeleteObject(DispRgn);
    			}
    			if (GetKeyState(VK_RBUTTON)<0)
    			{
    				if (   xp<=WIDTH  && xp>=0
    					&& yp<=HEIGHT && yp>=0 )
    				{
    					int size = 10;
    					Ellipse(g_hdcTemp, xp-size, yp+size, xp+size, yp-size);
    				}
    			}
    			}
    			break;
    
    		case WM_DESTROY:
    			{ // Clean up GDI things
    			DeleteObject(g_hdcTemp);
    
    			HANDLE DeadHBM = SelectObject(g_hdcMem, (HBITMAP)g_hbmOld);
    
    			//assert(DeadHBM == g_hbm);
    			DeleteObject(g_hdcMem);
    			DeleteObject(g_hbm);
    			DeleteObject(g_hbmOld);
    			
    			DestroyWindow(GetDlgItem(hWnd, IDC_DISPLAY_AREA));
    			}
    
    			PostQuitMessage(0);
    			break;
    //		case WM_COMMAND:
    //			switch (LOWORD(wParam))
    //			{
    //				case IDM_EXIT:
    //				   DestroyWindow(hWnd);
    //				   break;
    //				default:
    //				   return DefWindowProc(hWnd, message, wParam, lParam);
    //			}
    //			break;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
       }
       return 0;
    }
    // ======================================================
    //EOF

  8. #8
    Join Date
    May 2007
    Posts
    680

    Re: Inverting regions -

    Thanks again for another helpful post loki!

    But you know me right? The delinquent! I'm sorry I did not understand it but I think I have an idea. Is it possible for me to simply just treat the WM_PAINT message as temporary and when a WM_LBUTTONUP message occurs get the positions from WM_LBUTTONUP and WM_MOUSEMOVE and use them along with a Memory Device Context to draw?

    Thanks.

  9. #9
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    Thanks again for another helpful post loki!

    But you know me right? The delinquent! I'm sorry I did not understand it but I think I have an idea. Is it possible for me to simply just treat the WM_PAINT message as temporary and when a WM_LBUTTONUP message occurs get the positions from WM_LBUTTONUP and WM_MOUSEMOVE and use them along with a Memory Device Context to draw?

    Thanks.

    i'm not sure what you mean? the WM_PAINT message should redraw whatever you want to be on the screen. it is sent whenever there is an invalid region of the window or when you explicitly UpdateWindow() or RedrawWindow().

    so i don't see how the WM_PAINT message can be 'temporary' as it is handled all the time and if you do not handle it properly then the window will not paint itself at all (menu, title bar, frame, etc...)

    maybe what i refered to as temporary in the example was misleading - i just mean that it will be overwritten whenever the WM_PAINT message is handled (try drawing in red and green and then moving the window half off of the screen and back again). the Memory DC is what you draw to the window each time WM_PAINT is called, but what you draw on it depends on your application.

    Whenever you change the memory DC you must invalidate at least the area you have changed or the window will not be redrawn. try commenting out some of the InvalidateRgn() commands and see what difference it makes.

  10. #10
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    if you want to read up on this kind of thing from scratch, try:
    http://msdn2.microsoft.com/en-us/library/ms969905.aspx

    then for the region selection:
    http://support.microsoft.com/kb/816170
    http://www.codeguru.com/cpp/g-m/gdi/article.php/c139/

  11. #11
    Join Date
    May 2007
    Posts
    680

    Re: Inverting regions -

    I mean is... Uh... Actually have a look at this code -

    Code:
    #include <windows.h>
    
    const char *ClsName = "BasicApp";
    const char *WndName = "A Simple Window";
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
    WPARAM wParam, LPARAM lParam);
    
    HINSTANCE hInstance;
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
    {
    MSG Msg;
    HWND hWnd;
    WNDCLASSEX WndClsEx;
    
    // Create the application window
    WndClsEx.cbSize = sizeof(WNDCLASSEX);
    WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
    WndClsEx.lpfnWndProc = WndProc;
    WndClsEx.cbClsExtra = 0;
    WndClsEx.cbWndExtra = 0;
    WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClsEx.lpszMenuName = NULL;
    WndClsEx.lpszClassName = ClsName;
    WndClsEx.hInstance = hInstance;
    WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    
    // Register the application
    RegisterClassEx(&WndClsEx);
    
    // Create the window object
    hWnd = CreateWindow(ClsName,
    WndName,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    NULL,
    hInstance,
    NULL);
    
    // Find out if the window was created
    if( !hWnd ) // If the window was not created,
    return 0; // stop the application
    
    // Display the window to the user
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);
    
    // Decode and treat the messages
    // as long as the application is running
    while( GetMessage(&Msg, NULL, 0, 0) )
    {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
    }
    
    return Msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
    
     static bool Draw = false;
     static POINT StartingCP, CurrentCP, EndingCP;
    
    
     switch (Msg) 
     {
    
      case WM_DESTROY :
    
       PostQuitMessage(WM_QUIT);
      
      break;
    
      case WM_CLOSE :
    
       DestroyWindow(hWnd);
    
      break;
    
      case WM_LBUTTONDOWN :
    
       Draw = true;
       GetCursorPos(&StartingCP);
       ScreenToClient(hWnd, &StartingCP);
    
      break;
    
      case WM_MOUSEMOVE :
    
       if (Draw == true)
       {
    
        GetCursorPos(&CurrentCP);
        ScreenToClient(hWnd, &CurrentCP);
        RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
    
       }
    
      break;
    
      case WM_LBUTTONUP :
    
       Draw = false;
    
       GetCursorPos(&EndingCP);
       ScreenToClient(hWnd, &EndingCP);
    
       // Create a compatible Memory DC right here and use the starting and ending cursor points to get the new line into the M-DC
    
      break;
    
      case WM_PAINT :
    
       PAINTSTRUCT PS;
       HDC DC = BeginPaint(hWnd, &PS);
       // "Show" the contents of the Memory DC here
    
       if (Draw == true)
       {
    
        MoveToEx(DC, StartingCP.x, StartingCP.y, NULL);
        LineTo(DC, CurrentCP.x, CurrentCP.y);
    
       }
    
       EndPaint(hWnd, &PS);
    
      break;
    
     }
    
     return DefWindowProc(hWnd, Msg, wParam, lParam);
    
    }
    Thanks.

  12. #12
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    Is it possible for me to simply just treat the WM_PAINT message as temporary and when a WM_LBUTTONUP message occurs get the positions from WM_LBUTTONUP and WM_MOUSEMOVE and use them along with a Memory Device Context to draw?
    okay, i compiled your program and drew some pretty paterns! however, i think i see what you meant by your original question

    the way you do it there is to draw directly onto the window DC, due to:
    Code:
    HDC DC = BeginPaint(hWnd, &PS);
    there is nothing wrong with that - and it is the same result as drawing on what i called g_hdcTemp. the only real difference is that i got the DC a different way, and it is the DC of the child window not the main window..
    Code:
    g_hdcTemp = GetWindowDC(GetDlgItem(hWnd,IDC_DISPLAY_AREA));
    it could also have been the following line, but then i would have to use the ScreenToClient() function that you have used to get rid of the offset.
    Code:
    g_hdcTemp = GetWindowDC(hWnd);
    anyway - back to the question - the problem in both cases is that you are drawing to the actual DC that is being displayed on the window. when you cover part of the window with another window, or move it off of the screen it is invalidated and then redrawn - except it is not redrawn because there is no record of it.

    it 'would' be possible to copy the pixels/bitmap/DC from the display so that you could display it later, but what if the window is partially redrawn before you perform the copy? why not just draw to a DC that you are responsible for and display that one whenever the window needs redrawing.

    another completely different way to achieve the same result would be to remember ALL of your points that you have drawn in an array, and draw lines for each point pair from the whole array to the screen at WM_PAINT. for a simple picture like that it would actually take up less memory, since you do not need to know all the pixels, just the points at the ends of the line.

    if you look at the Microsoft Scribble tutorial (forget the MDI part) - it shows a method of building up an array of strokes (where each stroke is an array of points). as far as i can remember, it is the same idea of drawing points while the mouse id down and then saving the stroke when the mouse button is released. the mouse up event is a good time to either save the acquired data or draw it to a memory DC.

    there are times when you actually do want to draw things temporarily - such as the rubber banding method used when dragging out a box, and in this case you don't need to keep a record of anything you have drawn apart from the coordinates of the mouse down and the mouse up event (drawing the rectangles in between is just a visual guide to the user).

  13. #13
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    oh, i forgot to say ()

    in you code where you suggested to make a MemDC on the WM_LBUTTONUP and draw on it at the same time is fine, but you end up creating and destroying DCs for every mouse click (or worse, creating them without destroying them!).

    it is better to make the DC once at the start (since you know you will just need one for the window) and then simply draw to it on WM_LBUTTONUP or whenever you have something that needs displaying.

  14. #14
    Join Date
    May 2007
    Posts
    680

    Re: Inverting regions -

    Thanks man I seem to have made slight progress

    I replaced my BeginPaint function with GetWindowDC(hWnd) and took away the Endpaint, but since I do not know how to use ScreenToClient(because it only accepts points) to convert the offset.

    Questions -
    1) Do you have to destroy the DC of the window?
    2) How do I think about the DC? By this I mean that you completely confused me when you talked about the DC, should I think of it as a sheet? The Memory DC could be a sheet that you put to the side for later use, a window DC could be a sheet the is associated to a window and finally a temp. DC could be sheet to test something and then throw it.
    3) Is it alright if you would edit my code and show me in simple terms how to work with it?
    4) Is it a common problem for people to understand how these things work?

    Thanks a lot.

  15. #15
    Join Date
    Oct 2007
    Posts
    178

    Re: Inverting regions -

    Quote Originally Posted by .pcbrainbuster
    1) Do you have to destroy the DC of the window?
    yes - windows goes mental when it runs out of GDI resources bits of windows appearing in other windows and general crazyness. there are a limited number available (not sure how many) but think how often a window redraws its self - you can soon clock them up.

    every way of creating DCs has a corresponding way of destroying them. check out MSDN.
    GetDC() goes with ReleaseDC()
    BeginPaint() goes with EndPaint()
    CreateDC() goes with DeleteDC()
    the GDI limit also includes other less obvious things like regions, so for every HRGN hrgn; you must also have a DeleteObject(hrgn); when you are done.

    Quote Originally Posted by .pcbrainbuster
    2) How do I think about the DC? By this I mean that you completely confused me when you talked about the DC, should I think of it as a sheet? The Memory DC could be a sheet that you put to the side for later use, a window DC could be a sheet the is associated to a window and finally a temp. DC could be sheet to test something and then throw it.
    it is everything windows needs to draw on a window, so it includes a bitmap, a pen, a brush, a font, and loads of other stuff. in this context you are drawing onto a bitmap that exists within the DC. when you create a DC yourself (not the one that comes with the window) it has a 1x1 pixel bitmap in it which you must keep track of when you select in your new full size bitmap so that you can delete it later (see the g_hbmOld in my code).
    the rule is - if you change any part of the DC you should put it all back how it was at the start before you delete it. and then delete any other non-system things that you have created separately.

    Quote Originally Posted by .pcbrainbuster
    3) Is it alright if you would edit my code and show me in simple terms how to work with it?
    i would have done that before, but i'm not sure what you are trying to do. there is no definite best way - it depends what you want to draw and how you also want to use the data.

    Quote Originally Posted by .pcbrainbuster
    4) Is it a common problem for people to understand how these things work?
    lol, yeah for sure. i don't really understand it - i just know that if you want to draw a bitmap to the screen it must first be selected into a DC.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured