Twips, please give information how to handle this [duplicate] - windows

I would like to get the actual screen dpi/ppi, not the dpi setting used for font in C++.
I tried with the following codes:
Version 1, reports 72 dpi, which is wrong.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hSize = GetDeviceCaps(screen, HORZSIZE);
double vSize = GetDeviceCaps(screen, VERTSIZE);
double hRes = GetDeviceCaps(screen, HORZRES);
double vRes = GetDeviceCaps(screen, VERTRES);
double hPixelsPerInch = hRes / hSize * 25.4;
double vPixelsPerInch = vRes / vSize * 25.4;
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
Version 2, reports 96 dpi, which is the Windows dpi setting for font, but not the actual screen dpi.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSX);
double vPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSY);
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;

I'm honestly confused by the answers here.
Microsoft has a GetDpiForMonitor method:
https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
And monitors DO expose their physical dimensions to tools. You can read your monitors width and height, in centimeters, using the HWiNFO64 tool. So if they're getting it (DDI?), it stands to reason that you can access that information yourself.
Even a different Stack Overflow post mentions using WmiMonitorBasicDisplayParams to get the data.
How to get monitor size
So the top post is flat-out, 100%, wrong.

What you're asking for is, unfortunately, not possible in the general case.
Windows doesn't know the physical screen size. Windows might know that your screen has 1024x768 pixels, but it doesn't know how big the screen actually is. You might pull the cable out of your old 13" screen and connect it to a 19" monitor without changing the resolution. The DPI would be different, but Windows won't notice that you changed monitors.
You can get the true physical dimensions and DPI for a printer (assuming the driver isn't lying), but not for a screen. At least not reliably.
UPDATED
As others have pointed out, there are standards for two-way communication between newer monitors and the OS (EDID), that might make this information available for some devices. But I haven't yet found a monitor that provides this information.
Even if EDID were universally available, it's still not solvable in the general case, as the display could be a video projector, where the DPI would depend on the zoom, the focus, the lens type, and the throw distance. A projector is extremely unlikely to know the throw distance, so there's no way for it to report the actual DPI.

Getting DPI information is found to produce exact value using the below method.
ID2D1Factory* m_pDirect2dFactory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
FLOAT dpiX, dpiY;
m_pDirect2dFactory->GetDesktopDpi( &dpiX, &dpiY );

I think what you're after is:
GetDeviceCaps(hdcScreen, LOGPIXELSX);
GetDeviceCaps(hdcScreen, LOGPIXELSY);

Related

Differences between GetDC() and BeginPaint()?

I am having trouble with some of my owner drawn listboxes on High DPI monitors on Windows 10 in a dialog box. The text is chopped off at the bottom. We saw the problem on Windows 7 and were able to fix it. It is not necessarily High DPI, but when the user sets a different text scaling. I solved the problem, so I thought (!), by using a CClientDC (wrapper around GetDC()) and calling GetTextMetrics() to determine the text height. Previously, our icons had always been taller than our text so it was not a problem. With larger DPI monitors we saw some customers reporting problems when they scaled the text.
Now we are getting new reports under Windows 10. The former problem is fine under Windows 7--but Windows 7 only scales to 100, 125, and 150 percent. Windows 10 (and maybe 8? -- but no customer reports) allows user defined scaling.
So, I tracked down the problem somewhat... I knew what the font height was when I called GetTextMetrics() during WM_MEASUREITEM. I went and put some code in to debug what GetTextMetrics() was during my WM_DRAWITEM. Well, they were different--20 pixels high during WM_MEASUREITEM, and 25 pixels high during WM_DRAWITEM. Obviously, that is a problem. I want the GetTextMetrics() to have the same results in both places.
My thought was that the only real difference I could think of was that during WM_MEASUREITEM I am calling GetDC() via CClientDC constructor, and that during WM_DRAWITEM I am using an already constructed HDC (which probably was from a return of GetPaint() inside GDI32.dll or another system DLL).
I thought maybe the BeginPaint() does something like select the windows HFONT into the HDC...
So, inside my WM_MEASUREITEM after getting the DC, I select the font of the listbox into the HDC, and then I call GetTextMetrics(). Lo and behold, the numbers match now in WM_MEASUREITEM and WM_DRAWITEM.
However, I don't know if I just got lucky. It's all just guesswork at this point.
Does BeginPaint() select the window font into the DC whereas GetDC() does not? Does the default handler of WM_PAINT for an owner drawn LISTBOX or COMBOBOX do something like select the window font into the paint DC?
BOOL DpiAwareMeasureGraphItem(LPMEASUREITEMSTRUCT lpM, CWnd* pWnd)
{
int iItemHeight = INTERG_BITMAP_HEIGHT + 4;
if (pWnd)
{
CClientDC dc(pWnd);
if (dc.GetSafeHdc())
{
CFont* pOldFont = dc.SelectObject(pWnd->GetFont()); // seems to fix it on Windows 10, but is it luck?
TEXTMETRIC tm;
memset(&tm, 0, sizeof(tm));
dc.GetTextMetrics(&tm);
LONG tmHeight = tm.tmHeight + 4; //pad
iItemHeight = max(iItemHeight, tmHeight);
dc.SelectObject(pOldFont);
}
}
lpM->itemHeight = iItemHeight;
return (TRUE);
}
Neither GetDC() or BeginPaint() initialise the DC they return with anything other than the default system font. But WM_DRAWITEM is different - it gives you an already-initialised DC to draw into.
The method you stumbled across is the right one. WM_MEASUREITEM doesn't supply a DC at all, so if you need one for size calculations you're responsible for obtaining it and setting it up with the appropriate font.

Capture screenshot: native API vs opengl

In my program I am using Qt's function: qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(),x_offset,y_offset,w,h);
But its a little bit slowly for a "main task". So I've collide with a question above. The program able to work under Windows and Mac OS X. I heard about opengl as nice screengrabber since it is closer to GPU than native API plus its a cross platform solution. This is the first knowledge I want to get: Opengl as desktop screengrabber, is it real? I mean like a button "print screen".
If it is, how?If its not:
Windows: can you please give advice how to? BitBlt, GetDC, smthing like this?
Mac OS X: AVFoundation? Please, can you describe this or give some link about how to capture screenshot using this class? (Its a hard way since I know about Objective-C(++) almost nothing)
UPDATE: I read a lot about ways to capture screenshot. There are some knowledge:1. Opengl (maybe) is real as screengrabber, but use it will be irrcorrect for this software. Btw, I don't care, if there is some solution I will accept it.
2. DirectX it is not a way to solve my problem since this software does not work under Mac OS X.
Just to expand on #Zhenyi Luo's answer, here is a code snippet I have used in the past.
It also uses FreeImage for exporting the screenshot.
void Display::SaveScreenShot (std::string FilePath, SCREENSHOT_FORMAT Format){
// Create Pixel Array
GLubyte* pixels = new GLubyte [3 * Window::width * Window::height];
// Read Pixels From Screen And Buffer Into Array
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glReadPixels (0, 0, Window::width, Window::height, GL_BGR, GL_UNSIGNED_BYTE, pixels);
// Convert To FreeImage And Save
FIBITMAP* image = FreeImage_ConvertFromRawBits (pixels, Window::width,
Window::height, 3 * Window::width, 24,
0x0000FF, 0xFF0000, 0x00FF00, false);
FreeImage_Save ((FREE_IMAGE_FORMAT) Format, image, FilePath.c_str (), 0);
// Free Resources
FreeImage_Unload (image);
delete [] (pixels);
}
glReadPixels( GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLvoid * data), would read a block of pixels into client memory starting at location data.

QT5 font rendering different on various platforms

I want to make reproductible tests of some custom widgets renderings. In order to do that, I paint them onto a QImage and save the result as PNG. The output is really different on Windows compared to MacOSX.
I took care of :
Selecting the same font on all platform (I provide the "TTF" font file and point the code to it)
Drawing onto a QImage and not a QPixmap, as the documentation says QImage painter is supposed to be platform independant
I also selected Antialisating and TextAntialiasing hints
Requesting the font via QFontDatabase::font() so that pointSize is specified and not pixelSize
How can I make sure the rendering is exactly the same on all platforms so that my test runs are reproductible ? In other words, is it maybe possible to force QT5 to use the same font engine on all platforms (for instance freetype) ?
**
I nailed down the issue to a simple rendering test program.
So the code is looks like :
QFontDatabase fontDb;
fontDb.addApplicationFont(".../fonts/Vera.ttf");
QImage result(width, height, QImage::Format_RGB32);
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
QBrush background(QColor(205, 205, 205));
painter.fillRect(0, 0, 800, 600, background);
QFont font = fontDb.font("Bitstream Vera Sans", "Normal", 10);
painter.setFont(font);
painter.setPen(QColor(0, 0, 0));
painter.drawText(10, 10, "ABCD abcd 01234567");
The Bitstream Vera font can be downloaded on fontsquirel.com for instance.
See the result on MacOSX (left) and on Win32 (right), which are very different:
Following answer and comments by N1ghtLight below, and after reading the links he suggested, I changed the code to get the font to :
QFont font = fontDb_->font(("Bitstream Vera Sans", "Normal", -1);
qreal screenDPI = QApplication::primaryScreen()->physicalDotsPerInch();
qreal RENDER_DPI = 72;
int pixelSize = (int)((qreal)10 * screenDPI / RENDER_DPI);
font.setPixelSize(pixelSize);
This seems to mostly solve the problem of the font of very different size. At least on MacOSX, the font is exactly 10 pixels high now. On Windows though the font renders much thinner and a bit smaller too. I'm still lost and confused...
Here is the new result (left MacOSX, right Windows). The white scale indicates true 10 pixels size.
Following answer by G_G below I adapted the code (what about Linux ? Mobile platforms ? This gets very complicated...). Now the fonts are 10 pixels in the output on both Windows and MacOSX, still the rending remains very different (still MacOSX on the left, Windows on the right).
Thanks.
your render DPI variable should be 96 for Windows and 72 for OSX
according to:
http://www.rfwilmut.clara.net/about/fonts.html
On a Macintosh monitor, the notional resolution is 72 dots-per -inch
(dpi), so that a graphic 72 pixels wide would notionally be 1 inch
wide - though obviously the actual size would depend on the individual
monitor. However it will always print one inch wide.
But on a Windows monitor the resolution is (usually) 96 dpi. This
means that though the picture is still 72 pixels wide, it will print
at 0.75 inches.
QFont font = fontDb_->font(("Bitstream Vera Sans", "Normal", -1);
qreal screenDPI = QApplication::primaryScreen()->physicalDotsPerInch();
#ifdef WINDOWS
qreal RENDER_DPI = 96;
#else
qreal RENDER_DPI = 72;
#endif
int pixelSize = (int)((qreal)10 * screenDPI / RENDER_DPI);
font.setPixelSize(pixelSize);
It happens because you set size in pixels. You need to use setPointSize() instead.
From the Qt 5 docs:
void QFont::setPixelSize(int pixelSize)
Sets the font size to pixelSize pixels.
Using this function makes the font device dependent. Use setPointSize() or setPointSizeF() to set the size of the font in a device independent manner.
Also, for more info, you can check this post. This difference happens because of different display density on different OSs. I have experienced such problem myself on one OS X, Windows cross platform project.
[Updated] After my additional research, I've found, that current behavior of Qt fonts is just a bug. That's why solution above doesn't work (it worked for Qt 4)
Here is described different workarounds to solve this problem. Good luck!
You can try this. this will generate an image with the text which you will write. I using this code.
QPixmap photo;
QFont qfont;
QPainter painter;
QString txt;
QImage img(x,y,QImage::Format_RGB32);
painter.begin(&img);
img.fill(0xffffffff);
painter.drawPixmap(0,0,x,y,photo);
qfont.setFamily("Sampige");
qfont.setPixelSize(28);
painter.setFont(qfont);
txt = QString::fromUtf8("ನಮಸ್ಕಾರ");
painter.drawText(1,26,txt);
painter.end();
img.save("abcd.jpg");

How to scale NSTextView to show rich text in actual size?

The rich text in an NSTextView displays on my screen (27" Mac) a lot smaller than the font size would imply, although it prints correctly, and is the correct size if pasted in to another app (e.g. OpenOffice). TextEdit shows the same behaviour.
The following lines in awakeFromNib fixes this more or less exactly.
[myTextView scaleUnitSquareToSize:NSMakeSize(96.0/72, 96.0/72)];
myTextView.layoutManager.usesScreenFonts = NO;
So it looks as if the screen is using 96 points per inch. If I don't have the 2nd line, the text is slightly squashed up, and monotext mangled. Obviously I shouldn't hard code the scale factor, but where can I find the factor to put there? From [NSScreen mainScreen].deviceDescription ( a dictionary) I get NSDeviceResolution = {72, 72}", so it seems that's not what's being used.
I'm not sure this is wise, but you can get the "real" DPI from the display mode:
CGDirectDisplayID displayID = [window.screen.deviceDescription[#"NSScreenNumber"] unsignedIntValue];
CGSize size = CGDisplayScreenSize(displayID);
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID);
NSSize dpi;
dpi.width = CGDisplayModeGetWidth(mode) * 25.4 / size.width;
dpi.height = CGDisplayModeGetHeight(mode) * 25.4 / size.height;
CGDisplayModeRelease(mode);
[myTextView scaleUnitSquareToSize:NSMakeSize(dpi.width/72, dpi.height/72)];
myTextView.layoutManager.usesScreenFonts = NO;
Some display modes are letterboxed. These will presumably never be used as the general mode for the Mac GUI; they'd only be used by full-screen games. However, if you want your app to handle such a mode, it should account for the fact that the mode size does not correspond to the full screen size in one dimension and the DPI calculation above will be off. I don't think there's a direct way to figure that out. You have to examine all available display modes to see if there's one that's the same in relevant properties (size, pixel encoding, etc.) as the current mode but whose IOKit flags indicate that it's stretched (CGDisplayModeGetIOFlags(mode) & kDisplayModeStretchedFlag != 0) while the current mode is not. In that case, you probably want to assume the pixels are square and pick the smaller of dpi.width and dpi.height to use for both.

Proper way to convert an Enhanced Metafile to a Windows Metafile without pixel rounding

When I convert an Enhanced Metafile (constructed via GDI+ in C#) into an old-style Windows Metafile, the results are very rough, apparently because coordinates are being rounded to the nearest screen pixel. I see this if I convert either via
GetWinMetaFileBits(emfh, bits_l, bits, MM_ANISOTROPIC, GetDC(0));
or using GDI+'s Metafile::EmfToWmfBits. The culprit is presumably the screen DC being used. This posting suggests using a printer DC, which works for me, but obviously will not work if the user has no printers installed. Is there a better way? I have considered creating a high-resolution in-memory DC for the purpose, but I can find no proper documentation for doing so, and I also worry about the RAM used.
The normal way is to create a much higher resolution memory DC, render to that and then save it however you want. Note that font sizes can get screwed up by this.
HDC memDC = CreateCompatibleDC ( hDC );
HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );
SelectObject ( memDC, memBM );
With nWidth , nHeight much larger.
The CreateCompatible bit should set all the dpi etc, but I have had problems where the fonts were drawn fixed pixel size rather than rescaled - so they ended up being only 10pixels high on a 10,000 pixel image
Ask Google for ENMETA.EXE
Description by Microsoft
Example in download included.

Resources