I am just starting with MFC so please be tolerant ;).
I have wrote (it was mostly generated to be honest) a simple application which should do the Paint chores: drawing lines, rectangulars, ellipses, changing a color of object to be drawn etc.
I need to save what has been drawn on the screen into a bmp file. Any ideas how can I achieve this ?
I do not know if that's relevant but I am drawing objects on the screen without the use of any CBitmaps or things like that. Here is a part of code responsible for drawing :
CPaintDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
Anchor.x=point.x;
Anchor.y=point.y;
OldPoint.x=Anchor.x;
OldPoint.y=Anchor.y;
if(pDoc->shapeCount>=MAX_SHAPES) return;
pDoc->shapeCount++;
if(bFreehand)
{
pDoc->m_shape[pDoc->shapeCount-1] = new Shape;
pDoc->m_shape[pDoc->shapeCount-1]->shape = ePoint;
}
if(bLine)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CLine;
pDoc->m_shape[pDoc->shapeCount-1]->shape = eLine;
}
if(bRectangle)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CRectangle;
pDoc->m_shape[pDoc->shapeCount-1]->shape = eRectangle;
}
if(bEllipse)
{
pDoc->m_shape[pDoc->shapeCount-1] = new CEllipse;
pDoc->m_shape[pDoc->shapeCount-1]->shape=eEllipse;
}
pDoc->m_shape[pDoc->shapeCount-1]->x=point.x;
pDoc->m_shape[pDoc->shapeCount-1]->y=point.y;
pDoc->m_shape[pDoc->shapeCount-1]->x2=point.x;
pDoc->m_shape[pDoc->shapeCount-1]->y2=point.y;
pDoc->m_shape[pDoc->shapeCount-1]->Pen=CurrentPen;
pDoc->m_shape[pDoc->shapeCount-1]->Brush=CurrentBrush;
bButtonDown=true;
SetCapture();
I have found this way to do it but I don't know how to obtain screen width and height to fill it in the CreateBitmap parameter's list
CBitmap *bitmap;
bitmap.CreateBitmap(desktopW, desktopH, 1, 32, rgbData);
CImage image;
image.Attach(bitmap);
image.Save(_T("C:\\test.bmp"), Gdiplus::ImageFormatBMP);
The CreateBitmap call only requires the desktop width and height if the image you wish to save is actually the entire size of the screen. If that's indeed your intent, you can use CWnd::GetDesktopWindow() to get a CWnd object that you can query for its width and height:
http://msdn.microsoft.com/en-us/library/bkxb36k8(v=VS.80).aspx
That gets dodgy in general...if for no other reason than multi-monitor scenarios...so I'd recommend against it unless you really feel like writing a screen capture app.
What you probably want to do isn't to take a full screen shot, but just save the contents of your program's window. Typically you'd do this by breaking out the drawing logic of your program so that in the paint method you call a helper function that is written to take a CDC device context. Then you can either call that function on the window-based DC you get in the paint call or on a DC you create from the bitmap to do your save. Note that you can use a CBitmap in CDC::SelectObject:
http://msdn.microsoft.com/en-us/library/432f18e2(v=VS.71).aspx
(Though let me pitch you on not using MFC. Try Qt instead. Way better.)
Related
Hello I'm studying MFC and I wanna know how to save a specific part of image which is in Picture Control when I click.
Here is the sequence what I want to do
I already make a function that when I click grab button, it shows the image in picture control.
After that, I want to save image when I click specific area.
I attach the image to help understand.
"I want to know how to save the surrounding area as an image when I click a specific part of the picture control as shown above."
It would be easy problem, but I'd appreciate it if you understand because it's my first time doing this.
I tryed to use ScreenToClient function, but I failed.
There are quite a few ways to do this, but the general idea is usually pretty much the same:
Get a DC to the screen or the window's client area (aka DC1).
Create a DC compatible with DC1 (aka DC2).
create a CBitmap compatible with DC1, of the size you want to copy/save.
select the bitmap into DC2.
bitblt the area you care about from DC1 to DC2.
Create a CImage object.
Detach the bitmap from the CBitmap, and attach it to the CImage.
Use CImage::Save to save the bitmap to a file.
There are variations (e.g., writing the data to a file without using a CImage), but they tend to be tedious, and unless you're willing to put quite a bit of effort into things, fairly limited as well. e.g., writing a .BMP file on your own isn't terribly difficult, but is a bit tedious.If you want to support writing JPEG, TIFF, PNG, etc., you either need to integrate a number of libraries, or else write quite a bit of code for the file formats. CImage is somewhat limited as well, but at least supports a half dozen or so of the most common formats (and most people find just JPEG, PNG and possibly GIF sufficient).
Here's a bit of tested sample code for saving a 200x200 chunk of whatever's displayed in the current view window into a file named capture.jpg:
void CPicCapView::OnLButtonDown(UINT nFlags, CPoint point) {
// width and height of area to capture
static const uint32_t width = 200;
static const uint32_t height = 200;
CClientDC dc(this);
CDC temp;
temp.CreateCompatibleDC(&dc);
CBitmap bmp;
bmp.CreateCompatibleBitmap(&dc, width, height);
temp.SelectObject(&bmp);
// x-width/2, y-width/2 to get area centered at click point
temp.BitBlt(0, 0, width, height, &dc, point.x- width/2, point.y-height/2, SRCCOPY);
CImage dest;
dest.Attach((HBITMAP)bmp.Detach());
// for the moment, just saving to a fixed file name
dest.Save(L".\\capture.jpg");
CView::OnLButtonDown(nFlags, point);
}
It might initially seem like you should be able to create the CImage object, use its Create to create a bitmap, and then blit directly from the screen bitmap to the CImage bitmap. Unfortunately, CImage's BitBlt uses the CImage as a source, not a destination, so although there may be some way to get this to work, the obvious method doesn't.
My windows c++ program creates EMFs (Enhanced Metafile Format) to export to clipboard, word and excel.
The following example code generates an EMF rectangle (width=height=25) that is only 12x12 while the canvas is 25x25 (note: screen resolution of my laptop is3600x1800).
At other screen resolutions, similar scaling anomalies occur (too big/too small).
It would appear the scaling of the graphics-drawing needs to be set as a function of the resolution.
I clearly have a gap in my knowledge here...any help is appreciated.
HDC ref_dc = GetDC(NULL);
Rect r(0, 0, 25, 25);
Metafile* emf = new Metafile(ref_dc, r, MetafileFrameUnitPixel, EmfTypeEmfPlusDual, L"Drawing");//to HDC
Graphics* g = new Graphics(emf);
//draw a simple box
Gdiplus::Pen* pen = new Pen(Color(0, 255, 0), 1.0f);
pen->SetDashStyle(DashStyleSolid);
pen->SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g->DrawRectangle(pen, r); // DrawMyObject(g);
// code here to put on clipboard
Although unrelated to your actual problem, I feel very compelled to point out that your programming style of creating all the objects on the heap is quite odd. There is no reason to use new like this. Just create temporary objects on the stack. This keeps you from leaking memory and other resources like a sieve. By way of illustration, all you need is this:
// Create Graphics object
Graphics g(emf);
// Draw a simple box
Gdiplus::Pen pen(Color(0, 255, 0), 1.0f);
pen.SetDashStyle(DashStyleSolid);
pen.SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g.DrawRectangle(pen, r);
// g and pen automatically go out of scope here, implicitly calling the destructor
// and freeing their resources. No need to call delete.
As for your actual question, metafiles don't have a fixed size. They basically just encapsulate a series of GDI drawing instructions that can be recapitulated at will.
The normal way to put an enhanced metafile on the clipboard would be to call SetClipboardData function, using the CF_ENHMETAFILE format. The handle type would obviously then be HENHMETAFILE. You'll need to get GDI+ to give you one of those, probably by using the Metafile::GetHENHMETAFILE method after you've finished loading/creating your metafile object.
The scaling/sizing is the responsibility of the client code, the one that receives your metafile from the clipboard and tries to display it. The metafile header contains an entry that specifies its horizontal and vertical resolution. That can then be scaled in terms of the display DPI. In GDI+, something like:
Graphics g(...);
Metafile mf(L"MyFile.emf");
MetafileHeader mfh;
mf->GetMetafileHeader(&mfh);
REAL xScale = mfh->GetDpiX() / g.GetDpiX();
REAL yScale = mfh->GetDpiY() / g.GetDpiY();
g.ScaleTransform(xScale, yScale);
Self Answer:
The Graphics is created with a 'new' because it must be destroyed
before the EMF is 'written'. Else the emf does not 'record' the steps performed in by the Graphics object. Perhaps we should put an extra set of braces, but that would be equally awkward. According to the documentation: "The recording ends when the Graphics object is deleted or goes out of scope." Hence the explicit new(s) and delete(s). The EMF get pushed to the clipboard post destruction of Graphics and prior to destruction of EMF.
*The mistake/problem appears to be caused by using the constructor for the EMF that includes Rect struct as an argument. The Rect dimensions appears to have no discernible relationship with graphcs object coordinates used for drawing, and thus it crops the resultant EMF in unpredictable ways. Using a constructor with only an HDC or an HDC and a filename solves this problem, at least in my hands.
Last, adding the code lines
REAL xScale = mfh->GetDpiX() / g.GetDpiX();
REAL yScale = mfh->GetDpiY() / g.GetDpiY();
g.ScaleTransform(xScale, yScale);
provides a base scaling such that the emfs are more or less similar size regardless of screen resolutions. This is quite useful to give a consistent/reasonable default size for exports on the user side.
I'm updating an MFC dialog with a number of buttons on it.
At present, the dialog has a Picture control covering the whole dialog providing a patterned background. On top of that, each button is a CBitmapButton using (opaque) images carefully generated to match the area of background they cover.
It would obviously be much easier if the images could be created as mostly transparent, so the background shows through automatically. However, I can't work out how to get MFC to render transparent images correctly in this case.
I understand that I might want a different class to CBitmapButton, or need to write a custom subclass; that's fine, but I don't know where to start. It would be nice to support 32-bit BMP or PNG with alpha channel, but I'd settle for the "specified colour should be transparent" type.
It may not be the best way to do it, but what I'd do is create a custom CButton derived class (assuming that you're actually using the rest of the CButton functionality), then override the DrawItem function to put your custom draw code in.
For the image itself I'd use a Bitmap GDI+ object (which will allow you to load either BMPs or PNGs with alpha channels) then use the regular DrawImage function to draw the bitmap.
If you're going to put PNGs into your resource file then you need to put them in as a "PNG" type. Make sure when you look in the resource file code that the entry looks like
IDB_PNG1 PNG "C:\temp\test.png"
and doesn't try to treat it as a BITMAP resource otherwise you'll have problems loading them.
Edit
Putting my response here so I can post code. Yes, I meant to derive a custom class from CButton, then add a Gdiplus::Bitmap member variable. Here is roughly what you'll need to do to get it to work, though I haven't checked that the code actually compiles and works, but hopefully you'll get the idea. It's not the most efficient way to do it, but if you've not done much custom drawing before then it does have the advantage of being simple!
void CMyButton::LoadImage(const int resourceID)
{
m_pBitmap = Gdiplus::Bitmap::FromResource(NULL, MAKEINTRESOURCE(resourceID));
ASSERT(m_pBitmap);
}
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
CRect rcClient;
GetClientRect(&rcClient);
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
// If you want to do anything special when the button is pressed, do it here
// Maybe offset the rect to give the impression of the button being pressed?
rcClient.OffsetRect(1,1);
}
Graphics gr(lpDrawItemStruct->hDC);
gr.DrawImage(m_pBitmap, rcClient.left, rcClient.top);
}
I have a visual C++ application that uses a CView derived class to render its display, which is primarily 3d vector data and true type text. The mapping mode used is either MM_ANISOTROPIC or MM_LOMETRIC. I can't use MM_TEXT as I use the same code for printing and plotting of the data and also have to overcome non-square screen pixel issues.
The drawing code currently draws directly onto the screen using the CViews OnDraw method and the CDC object provided. I am trying to replace this with drawing to a bitmap and blitting the bitmap to screen, using a CreateCompatibleDC / CreateCompatibleBitmap combination, as described in the MS documentation and elsewhere.
The problem is that the DCs are not compatible for mapping modes other than MM_TEXT, such that my view is rendered upside down and at the wrong scale. Investigation shows the following;
void CMyView::OnDraw(CDC *pDC)
{
CDC MyDC = CreateCompatibleDC(pDC); // Create a new memory DC;
int a = pDC->GetMapMode(),b = MyDC.GetMapMode();
'
'
'
}
a = 2
b = 1
Calling a SetMapMode on MyDC causes the display to be drawn entirely in black. Do I have to rewrite my code to suit MM_TEXT for drawing to a bitmap, or is there another way to overcome this problem.
You probably need to also call SetWindowExt and SetViewportExt. I have definitely used MM_ISOTROPIC with bitmap DCs before and it worked OK (don't have the code to hand as it was since ported to GDI+)
How to Convert a Bitmap-like struct into an HDC?
I am now writting image processing program in c++, gdi.
If I got a HDC.
I can draw whatever I like on the HDC in gdi by the following code.
// HDC is handy.
HDC dc;
dc.DrawXXX // I can draw using gdi method.
Graphics gr(dc); // now I can also draw on the dc using gdi+ method.
My Application is based on FreeImage.
I make of fipImage. ( use data struct like Bitmap )
However if I want to draw on fipWinImage, now I have to copy fipWinImageto Bitmap, then draw on the Bitmap, and finally convert the bitmap into fipImage again, which is time comsuming and memory comsuming.
Convert fipImage to Bitmap -> Draw on the bitmap -> convert bitmap to fipWinImage
fipWinImage imagefip;
Bitmap* tempImg = new Bitmap(imagefip->GetWidth(), imagefip.GetHeigt(), PixelFormat24bppRGB); // memory comsuming is image is large
Graphics *pGr = Graphics::FromImage(tempImg);
HDC dc = pGr->GetHDC();
RECT rec;
rec.left = 0;
rec.top = 0;
rec.right = imagefip.GetWidth();
rec.bottom = imagefip.GetHeight();
fipImage.draw(dc, rec); // using stretchdibits()
pGr->ReleaseHDC(dc);
Graphics gr(tempImg);
HDC dc = gr.GetHDC(); // Get an Hdc, draw using gdi method
gr.ReleaseHDC(tempDC); //
gr.drawXXX // Draw using gdi+ method.
fipWinImage fipImg; // final result fipWinImage.
HBITMAP temp;
Color color;
tempImg->GetHBITMAP(color, &temp);
fipImg->copyFromBitmap(temp);
I want to construct a HDC directly from fipImage. and draw directly on fipWinImage
How can I do this?
First a few clarifications:
A Device Context is basically a structure that remembers things like foreground and background colors, brushes, font info, and the physical drawing surface (bitmap).
This is a handy thing, so that you don't have to keep specifying all of these things when you're doing one graphics operation after another. You can also pass all of these settings around more easily this way. That's all that a DC really is - just a collection of drawing parameters, including the surface to draw upon.
An "HDC" is just a handle (reference) to one of these structs. Being a "Handle" lets window move the struct around in memory to manage free space without your pointers to it getting messed up.
If you have access to the source code for the library you're using, examine the fipWinImage::draw(...) method. If they're using StretchDIBits, then they must get their raw bitmap data into a compatible format at some point. It's also possible that the fipWinImage class is wrapping an underlying BITMAP or DIB, etc.
The final step to getting your own HDC...
A bitmap is "SELECTED" into a device context, and can only be selected into a single DC at one time. If you can get the internal HBITMAP from fipWinImage, you can select it into another DC (assuming that it isn't still selected into another HC).
When you create a DC, windows automatically creates a 1x1 bitmap for it (since a DC must have a selected bitmap at all times). When you select in a new bitmap, you get the handle to the previously selected bitmap returned to you. Hang on to that, because you're going to need to put it back when you're done.
Hope that helps.
I don't know FreeImage, but if you can get a pointer to the actual pixel data (DIB section) out of it, you could just create a HBITMAP that shares it without having to copy the data every time.