Win32 GDI Drawing a circle? - c++

I am trying to draw a circle and I am currently using the Ellipse() function.
I have the starting mouse coordinates - x1 and y1 and the ending coordinates x2 and y2. As you can see, I am forcing the y2(temp_shape.bottom) to be = y1+(x2-x1). This doesn't work as intended. I know the calculation is completely wrong but any ideas on what is right?
Code Below.
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rect;
GetClientRect(hWnd, &rect);
HDC backbuffDC = CreateCompatibleDC(hdc);
HBITMAP backbuffer = CreateCompatibleBitmap( hdc, rect.right, rect.bottom);
int savedDC = SaveDC(backbuffDC);
SelectObject( backbuffDC, backbuffer );
HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
FillRect(backbuffDC,&rect,hBrush);
DeleteObject(hBrush);
//Brush and Pen colours
SelectObject(backbuffDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(backbuffDC, RGB(255,0,0));
SelectObject(backbuffDC, GetStockObject(DC_PEN));
SetDCPenColor(backbuffDC, RGB(0,0,0));
//Shape Coordinates
temp_shape.left=x1;
temp_shape.top=y1;
temp_shape.right=x2;
temp_shape.bottom=y2;
//Draw Old Shapes
//Rectangles
for ( int i = 0; i < current_rect_count; i++ )
{
Rectangle(backbuffDC, rect_list[i].left, rect_list[i].top, rect_list[i].right, rect_list[i].bottom);
}
//Ellipses
for ( int i = 0; i < current_ellipse_count; i++ )
{
Ellipse(backbuffDC, ellipse_list[i].left, ellipse_list[i].top, ellipse_list[i].right, ellipse_list[i].bottom);
}
if(mouse_down)
{
if(drawCircle)
{
temp_shape.right=y1+(x2-x1);
Ellipse(backbuffDC, temp_shape.left, temp_shape.top, temp_shape.right, temp_shape.bottom);
}
if(drawRect)
{
Rectangle(backbuffDC, temp_shape.left, temp_shape.top, temp_shape.right, temp_shape.bottom);
}
if(drawEllipse)
{
Ellipse(backbuffDC, temp_shape.left, temp_shape.top, temp_shape.right, temp_shape.bottom);
}
}
BitBlt(hdc,0,0,rect.right,rect.bottom,backbuffDC,0,0,SRCCOPY);
RestoreDC(backbuffDC,savedDC);
DeleteObject(backbuffer);
DeleteDC(backbuffDC);
EndPaint(hWnd, &ps);
}
break;

If you want Ellipse() to draw a perfectly round circle, you need to give it coordinates for a perfectly square shape, not a rectangular shape.
Assuming x1,y1 are the starting coordinates of the dragging and x2,y2 are the current mouse coordinates, then try this:
//Shape Coordinates
temp_shape.left = min(x1, x2);
temp_shape.top = min(y1, y2);
temp_shape.right = max(x1, x2);
temp_shape.bottom = max(y1, y2);
...
if (drawCircle)
{
int length = min(abs(x2-x1), abs(y2-y1));
if (x2 < x1)
temp_shape.left = temp_shape.right - length;
else
temp_shape.right = temp_shape.left + length;
if (y2 < y1)
temp_shape.top = temp_shape.bottom - length;
else
temp_shape.bottom = temp_shape.top + length;
Ellipse(backbuffDC, temp_shape.left, temp_shape.top, temp_shape.right, temp_shape.bottom);
}

I have worked out a calculation which works better. Pasted below for anyone else wanting the same.
if(drawSquare)
{
int xdiff = abs(x2-x1);
int ydiff=abs(y2-y1);
if(xdiff>ydiff)
{
if(y2>y1)
temp_shape.bottom=y1+xdiff;
else
temp_shape.bottom=y1-xdiff;
}
else
{
if(x2>x1)
temp_shape.right=x1+ydiff;
else
temp_shape.right=x1-ydiff;
}
Rectangle(backbuffDC, temp_shape.left, temp_shape.top, temp_shape.right, temp_shape.bottom);
}

Your code is unnecessarily complex with temp DCs and back buffers and you are recreating GDI brushes in every WM_PAIT? But that's besides the point. Here is the question : why do you do this:
temp_shape.right=y1+(x2-x1); //basing horizontal coordinate on vertical?
What framework stack is this on top of? .NET, then why don't you use built-in double buffering?

Related

in Vsual Studio C++ OffsetRgn not working>

void CBallBounceView::OnDraw(CDC* pDC)
{
CBallBounceDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CString str;
str.Format(_T("Counter = %d"), counter);
CRgn rgnA,copyeed;
VERIFY(rgnA.CreateEllipticRgn(50, 50, 150, 150));
CBrush brA;
VERIFY(brA.CreateSolidBrush(RGB(255, 0, 0)));
VERIFY(pDC->FillRgn(&rgnA, &brA)); // rgnA Red Filled
int nOffsetResult = rgnA.OffsetRgn(500, 100);
ASSERT(nOffsetResult != ERROR && nOffsetResult != NULLREGION);
}
I am trying to offset the ellipse region but it is not offsetting. I am new in visual studio C++;
I am using Visual studio 2019.
The problem is that, once output has been rendered to the given device context (as by your pDC->FillRgn(&rgnA, &brA) call), any subsequent changes to that region will not change the displayed output.
In order to do this, you will need to perform a cycle of operations:
Draw the object in its original position.
Then, when moved, erase that original object; and...
Draw the object in its new position.
Repeat each time you move the object.
You typically perform the 'erase' operation by drawing the object each time using the XOR (exclusive or) 'mix mode' on the target device context. Thus, the first time you draw this, it will paint the object over the background, and the second time, it will restore that background. You can use the CDC::SetROP2(R2_XORPEN) function to set that mode.
Here's a 'scruffy' (but runnable) program demonstrating the use of this XOR drawing technique. It uses 'raw' Windows GDI calls (rather than MFC) and is a Console-Mode program, but it will hopefully show the basic principles of the technique:
#include <Windows.h>
#define BLOBSIZE 20
#define MOVESTEP 5
int main()
{
SetProcessDPIAware(); // Comment this line out if using 'older' versions of Windows.
HWND hWnd = GetConsoleWindow();
HDC hDC = GetDC(hWnd);
RECT rc; GetClientRect(hWnd, &rc);
int x = 50, y = 30;
int dx = MOVESTEP, dy = MOVESTEP;
SetROP2(hDC, R2_XORPEN);
SelectObject(hDC, GetStockObject(WHITE_BRUSH));
SelectObject(hDC, GetStockObject(NULL_PEN));
do {
Ellipse(hDC, x, y, x + BLOBSIZE, y + BLOBSIZE); // First call: Draws the object
Sleep(10); // Here, we just wait, but you can do whatever else you want between moves
Ellipse(hDC, x, y, x + BLOBSIZE, y + BLOBSIZE); // Second call: erase it
x += dx;
if ((dx > 0 && x >= rc.right - BLOBSIZE) || (dx < 0 && x == 0)) {
Beep(1000, 10);
dx *= -1;
}
y += dy;
if ((dy > 0 && y >= rc.bottom - BLOBSIZE) || (dy < 0 && y == 0)) {
Beep(1000, 10);
dy *= -1;
}
} while (!(GetAsyncKeyState(VK_SPACE) & 0x8000)); // Press spacebar to quit!
ReleaseDC(hWnd, hDC);
return 0;
}

WinAPI Polygon Deform on Rotation

Got a bit stuck on problem with my polygon(square), it's deforming on rotation, tried standart function SetWorldTransform, but got disappointed.
Rotation function is OK. Possibly, the main problem is in the error after every rotate.
int xCenter = 105;
int yCenter = 105;
POINT pnts[5];
square()
{
pnts[0].x = 70;
pnts[0].y = 70;
pnts[1].x = 140;
pnts[1].y = 70;
pnts[2].x = 140;
pnts[2].y = 140;
pnts[3].x = 70;
pnts[3].y = 140;
pnts[4].x = 70;
pnts[4].y = 70;
}
void Drawsquare(HWND hWin)
{
HDC hdc;
HBRUSH hBrush;
HPEN hPen;
LOGBRUSH lBrush;
hdc = GetDC(hWin);
lBrush.lbStyle = BS_HOLLOW;
hBrush = CreateBrushIndirect(&lBrush);
hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
SelectObject(hdc, hBrush);
SelectObject(hdc, hPen);
Polygon(hdc, pnts, 5);
ReleaseDC(hWin, hdc);
}
void Rotate(HWND hWin)
{
HDC hdc;
RECT rect;
hdc = GetDC(hWin);
double pi = acos(-1);
double ang = 45 * pi / 180;
for(int i = 0; i < 5; i++)
{
pnts[i].x = (pnts[i].x - xCenter)*cos(ang) - (pnts[i].y - yCenter)*sin(ang) + xCenter;
pnts[i].y = (pnts[i].x - xCenter)*sin(ang) + (pnts[i].y - yCenter)*cos(ang) + yCenter;
}
GetClientRect(hWin, &rect);
ClearScreen(hdc, rect);
Drawsquare(hWin);
ReleaseDC(hWin, hdc);
}
Store your points into custom point structure with doubles instead ints use this type instead of POINT for all the logic operations
struct PrecisePoint
{
double x;
double y;
}
then copy them into POINT array right before Polygon(hdc, pnts, 5);
you can add method like:
precisePointsToPoints( PrecisePoint[] src, POINT[] dst, length);
Your coordinates are being rotated as floating point numbers, then coerced into integers for storing back into POINT structures. The truncation errors are accumulating and causing the distortion.

GDI in C not drawing successive objects

Attempting to create a basic drawing program using C and GDI in the Windows API. The idea is that the user clicks and drags to create a rectangle or any other shape that is selected. The size is specified by where the user clicks down and releases (just like any drawing program). I'm having an issue where the first shape that the user draws will be painted to the screen, however anything successive is not. I initially was not drawing the shapes in WM_PAINT, but I read on another thread that I need to do that, so I created a struct to store information about objects that have been painted and those are drawn each time the WM_PAINT message is received.
Here's my code for WM_PAINT
case WM_PAINT:
{
for (int i = 0; i < drawingcount; i++)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hMyPen = CreatePen(mydrawings[i].style, mydrawings[i].thick, RGB(0,255,0));
HPEN hOldPen = SelectObject(hdc, hMyPen);
if (mydrawings[i].shape == RECTANGLE)
{
Rectangle(hdc, mydrawings[i].start_x, mydrawings[i].start_y, mydrawings[i].end_x, mydrawings[i].end_y);
}
else if (mydrawings[i].shape == ELLIPSE)
{
Ellipse(hdc, mydrawings[i].start_x, mydrawings[i].start_y, mydrawings[i].end_x, mydrawings[i].end_y);
}
else if (mydrawings[i].shape == LINE)
{
MoveToEx(hdc, mydrawings[i].start_x, mydrawings[i].start_y, 0);
LineTo(hdc, mydrawings[i].end_x, mydrawings[i].end_y);
}
SelectObject(hdc, hOldPen);
DeleteObject(hMyPen);
//DeleteObject(hOldPen);
EndPaint(hwnd, &ps);
}
}
break;
And here for WM_LBUTTONUP (once user finishes drawing their shape)
case WM_LBUTTONUP:
{
yendclick = GET_Y_LPARAM(lParam);
xendclick = GET_X_LPARAM(lParam);
mydrawings[drawingcount].shape = selected_shape;
mydrawings[drawingcount].start_x = xclick;
mydrawings[drawingcount].start_y = yclick;
mydrawings[drawingcount].end_x = xendclick;
mydrawings[drawingcount].end_y = yendclick;
mydrawings[drawingcount].thick = thickness;
mydrawings[drawingcount].style = style;
drawingcount++;
SendMessage(hwnd, WM_PAINT, wParam, lParam);
}
Struct and enum declarations:
typedef enum myshape
{
RECTANGLE,
ELLIPSE,
LINE,
MAX_SHAPE
} shape;
typedef struct drawing
{
int start_x;
int start_y;
int end_x;
int end_y;
int thick;
int style;
shape shape;
} drawings;
Would very much appreciate any guidance with this!
Don't use PostMessage WM_PAINT, use InvalidateRect and perhaps UpdateWindow instead. See also here: why not to send WM_PAINT manually

How to extract bitmap from spritesheet in Win32 C++?

I'm trying to load individual cards from a spritesheet of cards based on suit and rank but I'm unsure of how to construct a new Bitmap object from cutting out Rectangle coordinates in the source image. I'm using <windows.h> currently and trying to find a simple way to accomplish this. I'm looking for something like this:
HBITMAP* twoOfHearts = CutOutFromImage(sourceImage, new Rectangle(0, 0, 76, 116));
From source: http://i.stack.imgur.com/WZ9Od.gif
Here's a function I played with the other week for more-or-less this same task. In my case, I wanted to return a HBRUSH that could be used with FillRect. In that instance, we still need to create a bitmap of the area of interest, before then going on to create a brush from it.
In your case, just return the dstBmp instead. spriteSheet is a global that has had a 256x256 spritesheet loaded. I've hardcoded the size of my sprites to 16x16, you'd need to change that to something like 81x117.
Here's some code that grabs a copy of the required area and some more that uses these 'stamps' to draw a level map. That said - there are all kinds of problems with this approach. Speed is one, excessive work is another one that impacts on the first. Finally, scrolling a window drawn like this produces artefacts.
// grabs a 16x16px section from the spriteSheet HBITMAP
HBRUSH getSpriteBrush(int col, int row)
{
HDC memDC, dstDC, curDC;
HBITMAP oldMemBmp, oldDstBmp, dstBmp;
curDC = GetDC(HWND_DESKTOP);
memDC = CreateCompatibleDC(NULL);
dstDC = CreateCompatibleDC(NULL);
dstBmp = CreateCompatibleBitmap(curDC, 16, 16);
int xOfs, yOfs;
xOfs = 16 * col;
yOfs = 16 * row;
oldMemBmp = (HBITMAP)SelectObject(memDC, spriteSheet);
oldDstBmp = (HBITMAP)SelectObject(dstDC, dstBmp);
BitBlt(dstDC,0,0,16,16, memDC, xOfs,yOfs, SRCCOPY);
SelectObject(memDC, oldMemBmp);
SelectObject(dstDC, oldDstBmp);
ReleaseDC(HWND_DESKTOP, curDC);
DeleteDC(memDC);
DeleteDC(dstDC);
HBRUSH result;
result = CreatePatternBrush(dstBmp);
DeleteObject(dstBmp);
return result;
}
void drawCompoundSprite(int x, int y, HDC paintDC, char *tileIndexes, int numCols, int numRows)
{
int mapCol, mapRow;
HBRUSH curSprite;
RECT curDstRect;
for (mapRow=0; mapRow<numRows; mapRow++)
{
for (mapCol=0; mapCol<numCols; mapCol++)
{
int curSpriteIndex = tileIndexes[mapRow*numCols + mapCol];
int spriteX, spriteY;
spriteX = curSpriteIndex % 16;
spriteY = curSpriteIndex / 16;
curDstRect.left = x + 16*mapCol;
curDstRect.top = y + 16 * mapRow;
curDstRect.right = curDstRect.left + 16;
curDstRect.bottom = curDstRect.top + 16;
curSprite = getSpriteBrush(spriteX, spriteY);
FillRect(paintDC, &curDstRect, curSprite);
DeleteObject(curSprite);
}
}
}
The latter function has since been replaced with the following:
void drawCompoundSpriteFast(int x, int y, HDC paintDC, unsigned char *tileIndexes, int numCols, int numRows, pMapInternalData mData)
{
int mapCol, mapRow;
HBRUSH curSprite;
RECT curDstRect;
HDC memDC;
HBITMAP oldBmp;
memDC = CreateCompatibleDC(NULL);
oldBmp = (HBITMAP)SelectObject(memDC, mData->spriteSheet);
for (mapRow=0; mapRow<numRows; mapRow++)
{
for (mapCol=0; mapCol<numCols; mapCol++)
{
int curSpriteIndex = tileIndexes[mapRow*numCols + mapCol];
int spriteX, spriteY;
spriteX = curSpriteIndex % mData->tileWidth;
spriteY = curSpriteIndex / mData->tileHeight
// Draw sprite as-is
// BitBlt(paintDC, x+16*mapCol, y+16*mapRow,
// mData->tileWidth, mData->tileHeight,
// memDC,
// spriteX * 16, spriteY*16,
// SRCCOPY);
// Draw sprite with magenta rgb(255,0,255) areas treated as transparent (empty)
TransparentBlt(
paintDC,
x+mData->tileWidth*mapCol, y+mData->tileHeight*mapRow,
mData->tileWidth, mData->tileHeight,
memDC,
spriteX * mData->tileWidth, spriteY*mData->tileHeight,
mData->tileWidth, mData->tileHeight,
RGB(255,0,255)
);
}
}
SelectObject(memDC, oldBmp);
DeleteObject(memDC);
}

EMF quality diminishes when window is shrinked, but is good when window dimensions are high

I am creating desktop application using C++ and pure WinApi. I need to display image that was given to me as SVG.
Since WinAPI supports only EMF files as vector format, I have used Inkscape to convert the file into EMF. My graphics design skills are at beginner level, but I have managed to convert SVG file into EMF successfully. However, the result is not looking as the original one, it is less "precise" so to say.
If I export the SVG as PNG and display it with GDI+, the result is the same as the original file. Unfortunately I need vector format.
To see exactly what I mean, download SVG, and EMF and PNG that I made here. Just click on Download:test.rar above 5 yellow stars ( see image below ).
Here are the instructions for creating minimal application that reproduces the problem:
1) Create default Win32 project in Visual Studio ( I use VS 2008, but this shouldn't be the problem );
2) Rewrite WM_PAINT like this:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rcClient;
GetClientRect( hWnd, &rcClient );
FillRect( hdc, &rcClient, (HBRUSH)GetStockObject( LTGRAY_BRUSH) );
// put metafile in the same place your app is
HENHMETAFILE hemf = GetEnhMetaFile( L".\\test.emf" );
ENHMETAHEADER emh;
GetEnhMetaFileHeader( hemf, sizeof(emh), &emh );
// rescale metafile, and keep proportion
UINT o_height = emh.rclFrame.bottom - emh.rclFrame.top,
o_width = emh.rclFrame.right - emh.rclFrame.left;
float scale = 0.5;
scale = (float)( rcClient.right - rcClient.left ) / o_width;
if( (float)( rcClient.bottom - rcClient.top ) / o_height < scale )
scale = (float)( rcClient.bottom - rcClient.top ) / o_height;
int marginX = ( rcClient.right - rcClient.left ) - (int)( o_width * scale );
int marginY = ( rcClient.bottom - rcClient.top ) - (int)( o_height * scale );
marginX /= 2;
marginY /= 2;
rcClient.left = rcClient.left + marginX;
rcClient.right = rcClient.right - marginX;
rcClient.top = rcClient.top + marginY;
rcClient.bottom = rcClient.bottom - marginY;
// Draw the picture.
PlayEnhMetaFile( hdc, hemf, &rcClient );
// Release the metafile handle.
DeleteEnhMetaFile(hemf);
EndPaint(hWnd, &ps);
}
break;
3) Add following handlers for WM_SIZE and WM_ERASEBKGND just below WM_PAINT :
case WM_SIZE:
InvalidateRect( hWnd, NULL, FALSE );
return 0L;
case WM_ERASEBKGND:
return 1L;
4) Resize the window to the smallest possible size, and then maximize it.
Notice that the bigger the window gets, the better the image quality is, but the smaller it gets the "less precise" the image gets. I tested this on Windows XP.
I am asking your help to get the same graphic quality of the EMF file as the original SVG.
Thank you for your time and efforts. Best regards.
Solved it!
The solution makes much, if not all of the solution I've submitted redundant. I've therefore decided to replace it with this one.
There's a number of things to take into account and a number of concepts that are employed to get the desired result. These include (in no particular order)
The need to set a maskColour that closely matches the background, while also not being present in the final computed image. Pixels that straddle the border between transparent/opaque areas are blended values of the mask and the EMF's colour at that point.
The need to choose a scaling rate that's appropriate for the source image - in the case of this image and the code I've used, I chose 8. This means that we're drawing this particular EMF at about a megapixel, even though the destination is likely to be in the vicinity of about 85k pixels.
The need to manually set the alpha channel of the generated 32bit HBITMAP, since the GDI stretching/drawing functions disregard this channel, yet the AlphaBlend function requires them to be accurate.
I also note that I've used old code to draw the background manually each time the screen is refreshed. A much better approach would be to create a patternBrush once which is then simply copied using the FillRect function. This is much faster than filling the rect with a solid colour and then drawing the lines over the top. I can't be bothered to re-write that part of the code, though I'll include a snippet for reference that I've used in other projects in the past.
Here's a couple of shots of the result I get from the code below:
Here's the code I used to achieve it:
#define WINVER 0x0500 // for alphablend stuff
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdint.h>
#include "resource.h"
HINSTANCE hInst;
HBITMAP mCreateDibSection(HDC hdc, int width, int height, int bitCount)
{
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bitCount;
bi.bmiHeader.biCompression = BI_RGB;
return CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, 0,0,0);
}
void makePixelsTransparent(HBITMAP bmp, byte r, byte g, byte b)
{
BITMAP bm;
GetObject(bmp, sizeof(bm), &bm);
int x, y;
for (y=0; y<bm.bmHeight; y++)
{
uint8_t *curRow = (uint8_t *)bm.bmBits;
curRow += y * bm.bmWidthBytes;
for (x=0; x<bm.bmWidth; x++)
{
if ((curRow[x*4 + 0] == b) && (curRow[x*4 + 1] == g) && (curRow[x*4 + 2] == r))
{
curRow[x*4 + 0] = 0; // blue
curRow[x*4 + 1] = 0; // green
curRow[x*4 + 2] = 0; // red
curRow[x*4 + 3] = 0; // alpha
}
else
curRow[x*4 + 3] = 255; // alpha
}
}
}
// Note: maskCol should be as close to the colour of the background as is practical
// this is because the pixels that border transparent/opaque areas will have
// their colours derived from a blending of the image colour and the maskColour
//
// I.e - if drawing to a white background (255,255,255), you should NOT use a mask of magenta (255,0,255)
// this would result in a magenta-ish border
HBITMAP HbitmapFromEmf(HENHMETAFILE hEmf, int width, int height, COLORREF maskCol)
{
ENHMETAHEADER emh;
GetEnhMetaFileHeader(hEmf, sizeof(emh), &emh);
int emfWidth, emfHeight;
emfWidth = emh.rclFrame.right - emh.rclFrame.left;
emfHeight = emh.rclFrame.bottom - emh.rclFrame.top;
// these are arbitrary and selected to give a good mix of speed and accuracy
// it may be worth considering passing this value in as a parameter to allow
// fine-tuning
emfWidth /= 8;
emfHeight /= 8;
// draw at 'native' size
HBITMAP emfSrcBmp = mCreateDibSection(NULL, emfWidth, emfHeight, 32);
HDC srcDC = CreateCompatibleDC(NULL);
HBITMAP oldSrcBmp = (HBITMAP)SelectObject(srcDC, emfSrcBmp);
RECT tmpEmfRect, emfRect;
SetRect(&tmpEmfRect, 0,0,emfWidth,emfHeight);
// fill background with mask colour
HBRUSH bkgBrush = CreateSolidBrush(maskCol);
FillRect(srcDC, &tmpEmfRect, bkgBrush);
DeleteObject(bkgBrush);
// draw emf
PlayEnhMetaFile(srcDC, hEmf, &tmpEmfRect);
HDC dstDC = CreateCompatibleDC(NULL);
HBITMAP oldDstBmp;
HBITMAP result;
result = mCreateDibSection(NULL, width, height, 32);
oldDstBmp = (HBITMAP)SelectObject(dstDC, result);
SetStretchBltMode(dstDC, HALFTONE);
StretchBlt(dstDC, 0,0,width,height, srcDC, 0,0, emfWidth,emfHeight, SRCCOPY);
SelectObject(srcDC, oldSrcBmp);
DeleteDC(srcDC);
DeleteObject(emfSrcBmp);
SelectObject(dstDC, oldDstBmp);
DeleteDC(dstDC);
makePixelsTransparent(result, GetRValue(maskCol),GetGValue(maskCol),GetBValue(maskCol));
return result;
}
int rectWidth(RECT &r)
{
return r.right - r.left;
}
int rectHeight(RECT &r)
{
return r.bottom - r.top;
}
void onPaintEmf(HWND hwnd, HENHMETAFILE srcEmf)
{
PAINTSTRUCT ps;
RECT mRect, drawRect;
HDC hdc;
double scaleWidth, scaleHeight, scale;
int spareWidth, spareHeight;
int emfWidth, emfHeight;
ENHMETAHEADER emh;
GetClientRect( hwnd, &mRect );
hdc = BeginPaint(hwnd, &ps);
// calculate the draw-size - retain aspect-ratio.
GetEnhMetaFileHeader(srcEmf, sizeof(emh), &emh );
emfWidth = emh.rclFrame.right - emh.rclFrame.left;
emfHeight = emh.rclFrame.bottom - emh.rclFrame.top;
scaleWidth = (double)rectWidth(mRect) / emfWidth;
scaleHeight = (double)rectHeight(mRect) / emfHeight;
scale = min(scaleWidth, scaleHeight);
int drawWidth, drawHeight;
drawWidth = emfWidth * scale;
drawHeight = emfHeight * scale;
spareWidth = rectWidth(mRect) - drawWidth;
spareHeight = rectHeight(mRect) - drawHeight;
drawRect = mRect;
InflateRect(&drawRect, -spareWidth/2, -spareHeight/2);
// create a HBITMAP from the emf and draw it
// **** note that the maskCol matches the background drawn by the below function ****
HBITMAP srcImg = HbitmapFromEmf(srcEmf, drawWidth, drawHeight, RGB(230,230,230) );
HDC memDC;
HBITMAP old;
memDC = CreateCompatibleDC(hdc);
old = (HBITMAP)SelectObject(memDC, srcImg);
byte alpha = 255;
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
AlphaBlend(hdc, drawRect.left,drawRect.top, drawWidth,drawHeight,
memDC, 0,0,drawWidth,drawHeight, bf);
SelectObject(memDC, old);
DeleteDC(memDC);
DeleteObject(srcImg);
EndPaint(hwnd, &ps);
}
void drawHeader(HDC dst, RECT headerRect)
{
HBRUSH b1;
int i,j;//,headerHeight = (headerRect.bottom - headerRect.top)+1;
b1 = CreateSolidBrush(RGB(230,230,230));
FillRect(dst, &headerRect,b1);
DeleteObject(b1);
HPEN oldPen, curPen;
curPen = CreatePen(PS_SOLID, 1, RGB(216,216,216));
oldPen = (HPEN)SelectObject(dst, curPen);
for (j=headerRect.top;j<headerRect.bottom;j+=10)
{
MoveToEx(dst, headerRect.left, j, NULL);
LineTo(dst, headerRect.right, j);
}
for (i=headerRect.left;i<headerRect.right;i+=10)
{
MoveToEx(dst, i, headerRect.top, NULL);
LineTo(dst, i, headerRect.bottom);
}
SelectObject(dst, oldPen);
DeleteObject(curPen);
MoveToEx(dst, headerRect.left,headerRect.bottom,NULL);
LineTo(dst, headerRect.right,headerRect.bottom);
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HENHMETAFILE hemf;
switch(uMsg)
{
case WM_INITDIALOG:
{
hemf = GetEnhMetaFile( "test.emf" );
}
return TRUE;
case WM_PAINT:
onPaintEmf(hwndDlg, hemf);
return 0;
case WM_ERASEBKGND:
{
RECT mRect;
GetClientRect(hwndDlg, &mRect);
drawHeader( (HDC)wParam, mRect);
}
return true;
case WM_SIZE:
InvalidateRect( hwndDlg, NULL, true );
return 0L;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst=hInstance;
InitCommonControls();
return DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
}
Finally, here's an example of creating a patternBrush for filling the background using the FillRect function. This approach is suitable for any tileable background.
HBRUSH makeCheckerBrush(int squareSize, COLORREF col1, COLORREF col2)
{
HDC memDC, tmpDC = GetDC(NULL);
HBRUSH result, br1, br2;
HBITMAP old, bmp;
RECT rc, r1, r2;
br1 = CreateSolidBrush(col1);
br2 = CreateSolidBrush(col2);
memDC = CreateCompatibleDC(tmpDC);
bmp = CreateCompatibleBitmap(tmpDC, 2*squareSize, 2*squareSize);
old = (HBITMAP)SelectObject(memDC, bmp);
SetRect(&rc, 0,0, squareSize*2, squareSize*2);
FillRect(memDC, &rc, br1);
// top right
SetRect(&r1, squareSize, 0, 2*squareSize, squareSize);
FillRect(memDC, &r1, br2);
// bot left
SetRect(&r2, 0, squareSize, squareSize, 2*squareSize);
FillRect(memDC, &r2, br2);
SelectObject(memDC, old);
DeleteObject(br1);
DeleteObject(br2);
ReleaseDC(0, tmpDC);
DeleteDC(memDC);
result = CreatePatternBrush(bmp);
DeleteObject(bmp);
return result;
}
Example of result, created with:
HBRUSH bkBrush = makeCheckerBrush(8, RGB(153,153,153), RGB(102,102,102));