How to get monitor resolution independent of scaling? - screen-resolution

I need to obtain the horizontal and vertical monitor resolution, independent of the display scaling in use by Windows. At scaling of 100%, there is no problem; GetSystemMetrics(SM_CXSCREEN) returns the actual horizontal resolution of the monitor in pixels. On a 1920 x 1080 monitor, that function returns 1920. However, if the scaling is set to 125% on the same computer, GetSystemMetrics(SM_CXSCREEN) returns 1536. The problem with that is, if I use that number in a call to BitBlt() to copy the screen, it copies only a partial screen, the left three quarters or so. My program does not know that it has to use 1920 to capture the entire screen, because Windows is telling it the horizontal monitor resolution is 1536. I tried using GetDeviceCaps() and using GetMonitorInfo() to get the monitor resolution, but they return the same values as GetSystemMetrics(). Windows is reporting a monitor resolution it is not actually using for BitBlt() and also for SetPixel(). The latter function draws to a location as though the screen resolution were 1920. For example, at scaling of 125%, SetPixel() draws a pixel at the x coordinate of 1500 not near the right edge of the screen, but at about three quarters from the left. SetPixel() will draw near the right edge of the screen if I tell it to draw at the x coordinate 1900, so evidently it is not interpreting screen coordinates as reported by GetSystemMetrics().
How can I get the actual monitor resolution in pixels, regardless of the scaling?

I found a solution:
RECT rBox;
HDC hdcScreen = GetDC(NULL);
GetClipBox(hdcScreen, &rBox);
When GetClipBox() returns, rBox contains dimensions in pixels of the entire screen, regardless of scaling factor. They are the same dimensions for all scaling factors. With screen resolution set to 1920 x 1080, the function sets rBox.left to 0 and rBox.right to 1920.

Related

Is there a way to define a view's frame in terms of inches instead of points?

Given any screen resolution, is there a way that I can figure out the amount of points in an inch? For instance, if I wanted to create an NSView that was 8.5 inches by 11 inches (like a sheet of a paper), is there an algorithm that will allow me to obtain the correct point values for the frame across many different types of Macs and screen resolutions?
It's not straightforward. I'm not sure there's a good way. I can provide an approach, but I haven't confirmed that this works reliably:
First, you can use CGDisplayScreenSize() to get the screen's physical size in millimeters. You can obtain the CGDirectDisplayID for a screen from NSScreen, which you can, in turn, get from the window. Obtain the screen's deviceDescription and get the value for the "NSScreenNumber" key. That may need to be cast to CGDirectDisplayID.
The problem from there is that the display mode may not fill the screen. It could be letterboxed or pillarboxed. Or, it might be stretched. This should be fairly uncommon these days, but still possible. You can obtain the display mode using CGDisplayCopyDisplayMode(). To determine if it's stretched, you can examine its ioFlags to see if they contain the bitmask kDisplayModeStretchedFlag (declared in IOKit).
If it's stretched, the screen's frame will have to be mapped to its size in millimeters separately for the X and Y axes. You assume the screen's frame.width (in points) maps to the full physical width, and similarly for the height.
If the mode is not stretched, you'll have to check the aspect ratio of the frame and the screen physical size to see if it's letter- or pillarboxed. If the aspect ratios are very close, then it's presumably not. That case is similar to the stretched case, but the width and height mappings should be equivalent.
If the aspect ratios differ significantly, then you compare them. If the screen's physical aspect ratio is larger than the frame's, then the screen is physically wider than the mode is using (pillarboxed). So, you compute the mapping from points to millimeters from the two heights. If the physical aspect ratio is smaller than the logical one, then the mode is letterboxed and you use the widths to compute the mapping.

How can I control which monitor plots are displayed on?

I have a 3 monitor Gentoo Linux system running MATLAB. MATLAB runs on the center monitor. I need MATLAB to generate plots on the left monitor but it always plots on the right monitor.
I believe this is at least partially caused by the non-standard way I have my monitors arranged physically - essentially 2,3,1:
>> get(0,'MonitorPositions')
ans =
1 1 1920 1080
-3839 1 1920 1080
-1919 1 1920 1080
Is there a way I can control this as a default within MATLAB?
You can set the default figure position on the root object like so:
set(0, 'DefaultFigurePosition', [-3839 1 1920 1080]);
This will create windows that fill the left monitor by default. However, this default will likely reset each time you restart MATLAB, so you will have to put it in your startup file if you want it to persist from session to session.
Note: The documentation for the 'MonitorPositions' property of the root object says this:
The first two elements in each row indicate the display location with respect to the origin point. The last two elements in each row indicate the display size. The origin point is the lower-left corner of the primary display.
If you change which monitor is used as the primary display, the relative coordinates in the left two columns will change, meaning you will have to change the position value in the above line of code. If you think the display setup may change often, or you will be running code on different monitor setups, then you can ensure plots will always appear on the left-most monitor by looking for the monitor position with the lowest value in the left column. Here's how you could do it (also incorporating the previous default window size and position within a monitor):
monitorPos = get(0, 'MonitorPositions');
figurePos = get(0, 'DefaultFigurePosition');
[~, leftIndex] = min(monitorPos(:, 1));
set(0, 'DefaultFigurePosition', figurePos + [monitorPos(leftIndex, 1:2)-1 0 0]);

How can you repeat a background in cocos2d when HxW lengths are not a power of 2?

While trying to create a repeated tile overlay, I've found many questions (like this one)
mentioning that repeated images in Cocos2d must have height and width dimensions that are powers of two.
This raises two questions. First, why is this a limitation? Second, and more importantly, how can I create a repeating, scrolling image that has dimensions that are not a power of two? What if I have a really wide background (say 4000 pixels) and I want it to repeat across the X axis. What should I do in that context? I can't believe the "correct" answer is to add an additional 96 pixels to the width, and increase the height of the image to 4096, as well. That's wasted bytes!
This answer has excellent info on why the need for power of 2 textures.
Why do images for textures on the iPhone need to have power-of-two dimensions?
As for your second question, the texture does not have to be square, just both the width and height have to be a power of 2. So you could have an image that is 4096x128 repeating as your background. Keep in mind also that textures, no matter what the size, are always stored in memory in an uncompressed power of two size. So an image with width of 4000 and and an image with width of 4096 are actually using the same amount of memory.

Get screen size (in pixels) for secondary monitor

Is there any way to get the screen size in pixels for a secondary monitor using Matlab? get(0,'ScreenSize') seems to only recognize the screen on the main monitor.
Try using get(0,'MonitorPositions') instead. It returns the width and height of primary and secondary monitors, in pixels.

StretchableImageWithLeftCapWidth stretching wrong portions

I am trying to use a UIImage with stretchableImageWithLeftCapWidth to set the image in my UIImageView but am encountering a strange scaling bug. Basically picture my image as an oval that is 31 pixels wide. The left and right 15 pixels are the caps and the middle single pixel is the scaled portion.
This works fine if I set the left cap to 15. However, if I set it to, say, 4. I would expect to get a 'center' portion that is a bit curved as it spans the center while the ends are a little pinched.
What I get is the left cap seemingly correct, followed by a long middle portion that is as if I scaled the single pixel at pixel 5, then a portion at the right of the image where it expands and closes over a width about twice the width of the original image. The resulting image is like a thermometer bulb.
Has anyone seen odd behavior like this and might know what's going on?
Your observation is correct, Joey. StretchableImageWithLeftCapWidth does NOT expand the whole center of the image as you would expect. It only expands the pixel column just right of the left cap and the pixel row just below the top cap!
Use UIView's contentStretch property instead, and your problem will be solved. Another advantage to this is that contentStretch can also shrink a graphic properly, whereas stretchableImageWithLeftCapWidth only works when making the graphic larger.
Not sure if I got you right, but LeftCapWidth etc is made for rounded corners, with everything in the rectangle within the rounding radius is stretched to fit the space between the 'caps' on the destination button or such.
So if your oval is taller or wider than 4 x 2 = 8, whatever is in the middle rectangle will be stretched. And yours is, so it would at least look at bit ugly! But if it's not even symmetrical, something has affected the stretch. Maybe something to do with origin or frame, or when it's set, or maybe it's set twice, or you have two different stretched images on top of each other giving the thermometer look.
I once created two identical buttons in the same place, using the same retained object - of course throwing away the previous button. Then I wondered why the heck the button didn't disappear when I set alpha to 0... But it did, it's just that there was a 'dead' identical button beneath it :)