I am new to MFC. Now I have the following question:
I have a large-size picture (e.g. size of 2000*2000) display in a small-size window (e.g. size of 640*480). No wonder that the picture cannot show itself fully without zooming out. I know I can save the original picture without losing any pixel if the picture can fit into the window, however, I cannot do this at this time:
CClientDC SHDC(this); //"this" is a CMDIChildWnd derived class
CDC memDC;
CRect rect;
GetClientRect(&rect);
memDC.CreateCompatibleDC(&SHDC);
CBitmap bm;
int uWidth = rect.Width();
int uHeight = rect.Height();
bm.CreateCompatibleBitmap(&SHDC, uWidth, uHeight);
CBitmap *pOld = memDC.SelectObject(&bm);
memDC.BitBlt(0, 0, uWidth, uHeight, &SHDC, 0, 0, SRCCOPY);
......
The saved picture only show the client area, but I would like to get all of the picture saved.
Anyone who can help me? Thanks in advance.
Xi
Instead of BitBlt try StretchBlt. It'll handle the shrinking for you.
The StretchBlt function copies a bitmap from a source rectangle into a
destination rectangle, stretching or compressing the bitmap to fit the
dimensions of the destination rectangle, if necessary. The system
stretches or compresses the bitmap according to the stretching mode
currently set in the destination device context.
If you want higher quality stretching/shrinking look at GDI+. There's a variety of interpolation modes you can use. More info here:
http://msdn.microsoft.com/en-us/library/k0fsyd4e.aspx
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.
I have some old C++/MFC code which uses GDI to create an image in a device context (CDC), using functions like Rectangle. The image is then painted to the screen, by the destructor of CPaintDC. Everything works as expected.
Now I want to store the image in a PNG file. For this, I am using Windows Imaging Component (WIC). I can create an image in WIC by creating a bitmap frame, storing pixels in a buffer, and copying the pixels into it using WritePixels. Then WIC stores the image as a PNG file via Commit. All this also works.
The question is, how can I transfer the image I created using GDI, which is in a CDC, to something WIC recognizes? I'm looking for something more efficient than a CDC::GetPixel loop storing into a buffer followed by WritePixels.
Windows 7, Visual Studio 2015, C++.
Update: I'm trying to get the CImage method suggested by #Andrew Komiagin. My code is
CDC memdc;
memdc.CreateCompatibleDC(nullptr);
myControl.RenderToDC(memdc); // this draws the image onto the dc
CBitmap bmp;
BOOL ok=bmp.CreateCompatibleBitmap(&memdc, w, h); // w=h=292 defined elsewhere
CBitmap *pOldBitmap=memdc.SelectObject(&bmp);
ok=memdc.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
CImage tempImageObj;
tempImageObj.Attach((HBITMAP)bmp.Detach());
CString outputFilename("outputImage.png");
HRESULT hr=tempImageObj.Save(outputFilename);
This produces a PNG image file with an all-black square, w x h pixels, rather than the image I drew. I know the image is being drawn correctly by RenderToDC (see below).
For comparison, this is in MyControl::OnPaint for the control (derived from CStatic):
CPaintDC dc(this); // device context for painting
RenderToDC(dc); // this draws the image onto the dc
// Painted to screen by CPaintDC's destructor
The image appears correctly.
I'd suggest using standard CImage class from ATL/MFC that works just great with GDI device context and natively supports the ability to load and save images in JPEG, GIF, BMP, and Portable Network Graphics (PNG) formats.
Here is an example on how to use it:
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CBitmap Bmp;
Bmp.CreateCompatibleBitmap(&dc,ClientRect.Width(),ClientRect.Height());
MemDC.SelectObject(&Bmp);
MemDC.BitBlt(0,0,ClientRect.Width(),ClientRect.Height(),&dc,0,0,SRCCOPY);
CImage TempImageObj;
TempImageObj.Attach((HBITMAP)Bmp.Detach());
TempImageObj.Save(sFilePath);
i have an issue with a fix i made to allow a flood filled object be printed...
so, the full story is we were using the windows GDI FloodFill function, which we noticed doesnt work on printers, so what i found on the inet, was to create a memory DC, compatible with the printer DC, and make all my drawing operations on the memory DC, and then BitBlt it all at once to the printer DC (i had to change to use a recursive, color replacment flood fill function too, since the memory DC only allows what the main DC did)
the problem is the memory DC seems to be a pixel or two bigger on the x and y, but i dont know what to do, when i get the selected bitmap from the memory DC, it shows it to be the correct size, i would like to use StretchBlt, but the values i have access to use as params for StretchBlt, make it no different than calling BitBlt
let me know if you need more info...
thanks in advance!!!
heres my code:
HDC hMemPrnDC = CreateCompatibleDC (hPrnDC);
HBITMAP hBitmap = CreateCompatibleBitmap (hPrnDC, iWidthLP, iHeightLP);
HBITMAP hOldBitmap = SelectBitmap (hMemPrnDC, hBitmap);
// paint the whole memory DC with the window color
HBRUSH hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
RECT rect;
// add one to right and bottom, FillRect doesnt include the right and bottom edges
SetRect (&rect, 0, 0, iWidthLP + 1, iHeightLP + 1);
// NOTE: im doing this cause it starts out as all black
FillRect (hMemPrnDC, &rect, hBrush);
// delete object
DeleteBrush (hBrush);
//
// do all my MoveToEx, LineTo, Ellipse, Arc, TextOut,
// SetPixel, etc calls on hMemPrnDC here
//
// copy all the memory DC drawing data to the printer DC
BitBlt (hPrnDC, 0, 0, iWidthLP, iHeightLP, hMemPrnDC, 0, 0, SRCCOPY);
// select old bitmap, and clean up objects
SelectBitmap (hMemPrnDC, hOldBitmap);
DeleteBitmap (hBitmap);
DeleteDC (hMemPrnDC);
hMemPrnDC = NULL;
UPDATE (Sept 5):
here is a link to a PDF print where I draw straight to the printer DC:
hPrnDC.pdf
and here is the same but I draw to the memory DC then BitBlt it to the printer DC:
hMemPrnDC.pdf
now, I did enable my recursive flood fill function on the second, to show an example of what we are trying to achieve, it does the same without it, so that is not an issue
as you can see, the bottom and right edge are cut off, I'm also concerned about the differences in font & line weight between the two, but not as much as the sizing mismatch
NOTE: the filename printed at the top doesn't go through the memory DC, that is always drawn straight to the printer DC
I found a solution to my problem, more of a work-around, but it achieved the desired results...
I only used the memory DC as a go between on the items that needed the recursive flood fill (GetPixel and SetPixel), so I draw them first to the memory DC, copy it all over to the printer DC and then draw everything else straight to the printer DC, seems to work just fine
thanks for the help!
I can't seem to get the CBitmap to actually apear on the screen... Here's the code in OnPaint:
CRect frm;
GetClientRect(frm);
CClientDC dc(this);
//dc.FillSolidRect(CRect(0, 0, 1000, 1000), RGB(255, 255, 255));
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap* cache = dcMem.SelectObject(&components.icon.bmp);
dc.BitBlt(0, 0, 55, 55, &dcMem, 0, 0, SRCCOPY);
dc.SelectObject(cache);
The definition of components.icon.bmp is...
components.icon.bmp.LoadBitmap(BMP_BOARDER);
BITMAP icon;
components.icon.bmp.GetBitmap(&icon);
There shouldn't be problem here. The Bitmap is a 32bit alpha bitmap and LoadBitmap returned TRUE.
It doesn't work with 24 bit either.
---- Got it! Problem with my resources
This code seems good to me but there are still many things that can be wrong. Test your code to know where the problem is:
- If you draw anything else (a rectangle for instance) does it appears on the frame window?
- Try to save the bmp to a file and see if it's ok.
- Try to convert the bmp to 24bits using a tool and see if it works.
Once you know where the problem is you'll be able to focus on the problem.
Probably you won't be able to draw a 32bits bmp using GDI but you should get something (a black square at least). Anyway, if you need to draw 32bits alpha-channel bmps using GDI you have to pre-multiply the image and use AlphaBlend method instead of BitBlt.
I'm trying to do some double buffering in an MFC application and trying to draw on the memory DC with GDI+. However, although I called CreateCompatibleDC(), I'm only getting a monochrome image. Here is the code:
CDC bufferDC;
CBitmap bufferBitmap;
bufferDC.CreateCompatibleDC(&dc);
bufferBitmap.CreateCompatibleBitmap(&bufferDC, 300, 300);
bufferDC.SelectObject(bufferBitmap);
Graphics g(bufferDC);
g.Clear(Color::Green);
dc.BitBlt(0, 0, 300, 300, &bufferDC, 0, 0, SRCCOPY);
Instead of a green patch, I see a rectangle of dithered black and white dots. I even tried to save the bitmap to disk after the g.Clear() call. It is indeed a 1-bit depth file.
Any ideas what went wrong? Thanks.
A common mistake. A memory DC takes on the properties of the bitmap selected into it, no matter what compatibility it was created with. The default bitmap selected into a DC is monochrome. If you create a bitmap compatible with that DC, it will be monochrome too.
Create the bitmap to be compatible with the original DC, not the memory DC.
Both the bitnmap and the bufferDC should be compatible with dc (whatever device it refers to), not the bitmap compatible ... with its own DC.
Try to give &dc to CreateCopmpatibleBitmap.
Your code snippet does not show where the dc variable comes from. ThIs guy probably contains a monochrome bitmap, the default. You dont need it anyway. Instead, pass NULL to CreateCompatibleDC and it will be the same format as your display, which is probably color.