how do I do print preview in win32 c++? - c++

I have a drawing function that just takes an HDC.
But I need to show an EXACT scaled version of what will print.
So currently, I use
CreateCompatibleDC() with a printer HDC and
CreateCompatibleBitmap() with the printer's HDC.
I figure this way the DC will have the printer's exact width and height.
And when I select fonts into this HDC, the text will be scaled exactly as the printer would.
Unfortunately, I can't to a StretchBlt() to copy this HDC's pixels to the control's HDC since they're of different HDC types I guess.
If I create the "memory canvas" from a window HDC with same w,h as the printer's page,
the fonts come out WAY teeny since they're scaled for the screen, not page...
Should I CreateCompatibleDC() from the window's DC and
CreateCompatibleBitmap() from the printer's DC or something??
If somebody could explain the RIGHT way to do this.
(And still have something that looks EXACTLY as it would on printer)...
Well, I'd appreciate it !!
...Steve

Depending on how accurate you want to be, this can get difficult.
There are many approaches. It sounds like you're trying to draw to a printer-sized bitmap and then shrink it down. The steps to do that are:
Create a DC (or better yet, an IC--Information Context) for the printer.
Query the printer DC to find out the resolution, page size, physical offsets, etc.
Create a DC for the window/screen.
Create a compatible DC (the memory DC).
Create a compatible bitmap for the window/screen, but the size should be the pixel size of the printer page. (The problem with this approach is that this is a HUGE bitmap and it can fail.)
Select the compatible bitmap into the memory DC.
Draw to the memory DC, using the same coordinates you would use if drawing to the actual printer. (When you select fonts, make sure you scale them to the printer's logical inch, not the screen's logical inch.)
StretchBlt the memory DC to the window, which will scale down the entire image. You might want to experiment with the stretch mode to see what works best for the kind of image you're going to display.
Release all the resources.
But before you head in that direction, consider the alternatives. This approach involves allocating a HUGE off-screen bitmap. This can fail on resource-poor computers. Even if it doesn't, you might be starving other apps.
The metafile approach given in another answer is a good choice for many applications. I'd start with this.
Another approach is to figure out all the sizes in some fictional high-resolution unit. For example, assume everything is in 1000ths of an inch. Then your drawing routines would scale this imaginary unit to the actual dpi used by the target device.
The problem with this last approach (and possibly the metafile one) is that GDI fonts don't scale perfectly linearly. The widths of individual characters are tweaked depending on the target resolution. On a high-resolution device (like a 300+ dpi laser printer), this tweaking is minimal. But on a 96-dpi screen, the tweaks can add up to a significant error over the length of a line. So text in your preview window might appear out-of-proportion (typically wider) than it does on the printed page.
Thus the hardcore approach is to measure text in the printer context, and measure again in the screen context, and adjust for the discrepancy. For example (using made-up numbers), you might measure the width of some text in the printer context, and it comes out to 900 printer pixels. Suppose the ratio of printer pixels to screen pixels is 3:1. You'd expect the same text on the screen to be 300 screen pixels wide. But you measure in the screen context and you get a value like 325 screen pixels. When you draw to the screen, you'll have to somehow make the text 25 pixels narrower. You can ram the characters closer together, or choose a slightly smaller font and then stretch them out.
The hardcore approach involves more complexity. You might, for example, try to detect font substitutions made by the printer driver and match them as closely as you can with the available screen fonts.
I've had good luck with a hybrid of the big-bitmap and the hardcore approaches. Instead of making a giant bitmap for the whole page, I make one large enough for a line of text. Then I draw at printer size to the offscreen bitmap and StretchBlt it down to screen size. This eliminates dealing with the size discrepancy at a slight degradation of font quality. It's suitable for actual print preview, but you wouldn't want to build a WYSIWYG editor like that. The one-line bitmap is small enough to make this practical.
The good news is only text is hard. All other drawing is a simple scaling of coordinates and sizes.
I've not used GDI+ much, but I think it did away with non-linear font scaling. So if you're using GDI+, you should just have to scale your coordinates. The drawback is that I don't think the font quality on GDI+ is as good.
And finally, if you're a native app on Vista or later, make sure you've marked your process as "DPI-aware" . Otherwise, if the user is on a high-DPI screen, Windows will lie to you and claim that the resolution is only 96 dpi and then do a fuzzy up-scaling of whatever you draw. This degrades the visual quality and can make debugging your print preview even more complicated. Since so many programs don't adapt well to higher DPI screens, Microsoft added "high DPI scaling" by default starting in Vista.
Edited to Add
Another caveat: If you select an HFONT into the memory DC with the printer-sized bitmap, it's possible that you get a different font than what would get when selecting that same HFONT into the actual printer DC. That's because some printer drivers will substitute common fonts with in memory ones. For example, some PostScript printers will substitute an internal PostScript font for certain common TrueType fonts.
You can first select the HFONT into the printer IC, then use GDI functions like GetTextFace, GetTextMetrics, and maybe GetOutlineTextMetrics to find out about the actual font selected. Then you can create a new LOGFONT to try to more closely match what the printer would use, turn that into an HFONT, and select that into your memory DC. This is the mark of a really good implementation.
Another Edit
I've recently written new code that uses enhanced meta files, and that works really well, at least for TrueType and OpenType fonts when there's no font substitution. This eliminates all the work I described above trying to create a screen font that is a scaled match for the printer font. You can just run through your normal printing code and print to an enhanced meta file DC as though it's the printer DC.

One thing that might be worth trying is to create an enhanced metafile DC, draw to it as normal and then scale this metafile using printer metrics. This is the approach used by the WTL BmpView sample - I don't know how accurate this will be but it might be worth looking at (it should be easy to port the relevant classes to Win32 but WTL is a great replacement for Win32 programming so might be worth utilizing.)

Well it won't look the same because you have a higher resolution in the printer DC, so you'll have to write a conversion function of sorts. I'd go with the method that you got to work but the text was too small and just multiply every position/font size by the printer window width and divide by the source window width.

Related

Taking a screenshot, analyzing it, then deleting it

I've been trying to code an auto clicker for a simple game online (a php coded one), but I've had trouble analyzing the colors on-screen. (English isn't my first language, sorry!) I've already done a bit of C++ in university, but only for science-oriented simple console programs. (Edit: I'm working on windows!! forgot to mention)
I've already tried the getpixel function, but since my chrome window is zoomed out at 80% to get the full game in frame, it seems I'm having some DPI related issues, but looking into this made my head dizzy.
After watching a Codebullet video, I thought a better approach to this would be to take a screenshot of the problematic area, analyze it to see if the condition is filled, then delete the screenshot. The problem is, I have no idea how I could achieve this and Google didn't help much this time :\
My code is extremely messy so I can't show it right now, but it's basically just a:
-click there
-click there after 5 seconds
-click there if this pixel is this color
-repeat
Is there an easy answer to this? I'd be really thankful if there is. Have a nice day! :)
You don't need to save the screen shot if you don't want to:
Pass the target window handle to GetDC(), t will return the the device context of the window.
Pass the device context to CreateCompatibleDC() to create a compatible DC.
Use CreateCompatibleBitmap(), passing in the DC and the size of the window. This returns a handle to a bitmap
Use SelectObject() to select the bitmap
Use BitBlt() to do a bitblock transfer of the selected pixels from the regular DC into the compatible DC using the SRCCOPY raster operation code to do a normal copy.
Create a BITMAP object. Use GetObject() and pass the handle to the bitmap you created.
Create a BITMAPINFOHEADER and define the member vars. Create an array of unsigned chars big enough to fit all the pixels from your bitmap.
Use GetDIBits() passing in the handle to the compatible bitmap, the bitmap header and a pointer to the pixel array. This loads the pixels from the bitmap into the pixel array.
Now parse all that juicy pixel data, search for the colors you're looking for and test the results against your conditionals to decide what to do next.
Don't forget to delete objects and release memory & device contexts.
I believe this is the tutorial I followed where I learned this, courtesy of MSDN: https://learn.microsoft.com/en-us/windows/desktop/gdi/capturing-an-image

What size of ImageList icons do I need to make & load for my app (considering higher DPI)?

I have a CListCtrl control (or a ListView in Win32) that is created with LVS_REPORT style.
I am intending to display icons in its items as such:
But the question is what size of icons do I need to make and load?
Let me explain. From the old Win32 samples, I can see that everyone creates image lists with 15x15 pixel icons. But the issue with those is that it looks horribly pixelated on any modern PC with higher DPI settings. Thus I was looking for a dynamic way to determine the appropriate size of image lists for the CListCtrl.
And also the first part of the question, what icon size should I make originally?
EDIT
PS: Since DPI scaling came up, how do you find it out? I'm currently using the following approach:
//No error handling for brevity
HDC hDC = ::GetDC(hAppsMainWindowHandle);
int nCx = ::GetDeviceCaps(hDC, LOGPIXELSX);
int nCy = ::GetDeviceCaps(hDC, LOGPIXELSY);
::ReleaseDC(hAppsMainWindowHandle, hDC);
//I technically get horizontal & vertical scaling --
//can those be different?
double scalingCx = (double)nCx / 96.0; //1.0 = 100%
double scalingCy = (double)nCy / 96.0;
Is font scaling something different?
A list view uses a "small" or "large" image list depending on its mode. In report mode, it uses the "small" image list. You can use GetSystemMetrics() to get the dimensions of "small" images using the SM_CXSMICON and SM_CYSMICON metrics (use SM_CXICON and SM_CYICON for "large" images).
Note that the returned values will be virtual/scaled if your app is not DPI-aware, so to get accurate values, make sure it is DPI-aware via SetProcessDPIAware(), SetProcessDpiAwareness(), or a DPI manifest.
Update: I just ran across this function, might be useful for you when writing a DPI-aware app:
LoadIconWithScaleDown()
Make larger images and let the API scale them down to smaller sizes.
The report style list view wants small icons, that is icons with SM_CXSMICON by SM_CYSMICON metrics. Assuming your app is high DPI aware, then the actual value of these metrics depends on the user's chosen font scaling or DPI setting. So up front you cannot know what size icons should be used. You have to query the system metrics at runtime, and use appropriately sized icons.
Now, what size icons you include in your executable depend on what DPI settings you wish to support. Back in XP days you could reasonably expect to encounter 100% and 125% font scaling. These days, high density display panels are common and you really need to support larger ratios. At least 150% and 200%, and quite probably 175%.
The closest I can find to guidelines is in this MSDN article: http://msdn.microsoft.com/en-US/library/windows/desktop/dn742485.aspx
This article was written around the Vista time frame and already shows its age. I think you have to use these articles as a guide and adapt to the hardware of the day.
You will also find that people run their machines at font scaling values in between the round numbers listed above, and in the article I link to. One of my colleagues runs at 120%. Interestingly this has highlighted various bugs in our code so it's always useful to have someone dog-fooding your program.
When you build your image lists at runtime, size them according to system metrics. And try to avoid scaling icons. For instance, if system metrics suggest an 18px icons, and you only have 16px and 20px icons, then make a new 18px icons. Fill the image with transparent pixels, and blit the 16px icon into the middle of this 18px image. Make the icon out of that. This approach will avoid aliasing problems.

Draw GDI+ graphics objects using multi-threading

I have an application in which i am drawing thousands of rectangles of different size. Now here i am giving on user selection of those rectangle i am just drawing rotating border on that particular rectangle...(marching ant animation on rectangle selection).
Now if user selects few rectangles than it won't create such trouble but once user selects all or many at a time then redrawing showing flickering effect which doesn't look good and not even acceptable.
i want make it parallelization of it so i can gain the performance out of it.
I suggest you to use double buffering: create a memory DC, draw on it and then perform BitBlt on a real DC. You can find a lot of examples about this technique in the Internet.
Also you may refer to this msdn article: Flicker-Free Displays Using an Off-Screen DC

Supporting different monitor resolutions

I have a MFC application with some bitmaps, dialog boxes and menus. Currently it supports only one monitor resolution (1280x1024). I am planning to add different monitor resolution support to it. To do that I guess I have to load different resolution bitmaps, change the font size etc. My question is, how these are handled in a typical windows application? Do they use a single bitmap and stretch/shrink it according to monitor resolution or actually have different set of bitmaps in the resource and load them dynamically depending on the resolution? Is there any standard solution to this?
In the past I have used one large image and scaled accordingly.
Making sure that the menus and dialogs resize is tricky but there are helper codes on CodeProject that could help.
I would say to use multiple resources with different resolutions. While you can scale the icons dynamically, they'll probably look better if you resize them in a proper image-editing program.
Menu and toolbar icons are normally displayed with the same number of pixels regardless of screen resolution. Thus menus and toolbars take up a smaller proportion of the screen as the resolution increases.
I don't think there's any standard way of handling different resolutions where bitmaps are concerned.
I would also make sure your application works with Windows DPI scaling. That might be a better alternative when running on higher resolution displays rather than having to redesign the application to meet a specific resolution.
Scaling bitmaps will look bad (making them bigger will always look bad, making them smaller kind of depends on the source). If possible, see if you can compose your big bitmap of several smaller bitmaps that can scale nicely. Lots of times it is fairly easy to make a bitmap that can scale in one direction nicely. For example, if you want to make a frame around something, instead of using one bitmap, cut it up like a tic-tac-toe board into 9 pieces. the four corner pieces stay their original size, the top and bottom pieces stretch horizontally, the left and right vertically, and the center goes both directions (if it is used at all).

Printed CDC appears tiny on paper

When I print the CDC for a report control that I've created it appears tiny (less than 1 square inch on paper). How can I get the report to be printed to occupy the entire page ?
Or in other words, how can I make the entire report to appear in one printed page.
CPrintDialog printDialog(FALSE);
printDialog.DoModal();
CDC dcPrint;
if(dcPrint.Attach(printDialog.GetPrinterDC()))
{
int iHorzRes = dcPrint.GetDeviceCaps(HORZRES);
int iVertRes = dcPrint.GetDeviceCaps(VERTRES);
int iHorzResCDC = m_CDC.GetDeviceCaps(HORZRES);
int iVertResCDC = m_CDC.GetDeviceCaps(VERTRES);
dcPrint.m_bPrinting = TRUE;
dcPrint.BitBlt(0,0, iHorzRes, iVertRes, &m_CDC, iHorzResCDC, iVertResCDC, SRCCOPY);
CFont* pOldFont = dcPrint.SelectObject(&m_HeaderFont);
dcPrint.TextOut(0,0,"HelloWorld") ;
dcPrint.SelectObject(pOldFont);
CPrintInfo printInfo;
printInfo.m_rectDraw.SetRect(0,0, iHorzRes, iVertRes);
dcPrint.StartDoc("Report Print");
dcPrint.StartPage();
if(dcPrint.EndPage())
dcPrint.EndDoc();
else
dcPrint.AbortDoc();
}
dcPrint.DeleteDC();
m_CDC is the memory DC that I use to buffer and display the entire report on screen.
As others have said, this is because, in general, the display resolution of printers is a lot higher than displays. Displays are usually 96 to 120DPI: at 96DPI this means that an image of 96 pixels (dots) by 96 pixels occupies approximately 1 square inch on the display. However, if you just take that image and print it out on a 600DPI printer, the size of the image will be about 1/6" by 1/6" - much smaller. This is a bane of the publishing world - images that look fine on displays often look either tiny or terrible when printed.
You could, as has been suggested, use StretchBlt rather than BitBlt to scale up your image. Depending on the difference between your display and printer, this will either look a bit blocky, or utterly hideously blocky.
A much better option is to rewrite your code that does the drawing of the control so that you've got a method that takes a device context (and some co-ordinates) and draws into it. Your normal window painting code can pass the memory DC to this routine and then BitBlt the result to the window, and your painting code can call this method with the printer DC and some suitable co-ordinates.
When writing this routine you'll have to worry about scaling: for example, you'll need to create fonts for the given device context, and with a scaling-indepdendant size (that is, specify the font size in points, not pixels), rather than relying on a pre-created font.
I suppose that you're not scaling your report to the printer's resolution. Typical screen resolution is 72 DPI (sometimes 96 DPI). Printer resolution can be 300DPI, 600DPI or higher.
You should repaint the report to the printer DC with all coordinates and sizes scaled to the printer's resolution.
Your printer has a lot more dots per inch than your screen. You will need to scale things to fit the printed page a bit better.
Try using StretchBlt() instead of BitBlt().