What is the relationship between ScaleTransform and Pixel - c#

I am new to the concepts of pixels, dpi, resolution and stuffs. So let me put out this simple question for which I am finding hard to get a simple answer
What is the default measuring unit in wpf
When I apply a scale transform to a button, how is it getting bigger (whats really happening)
What is the unit of the zoom factor in wpf
How is the zoom factor related to pixel (or location of a control that is scaled)

That subjet is quite complex actually.
Unit
In WPF, all sizes are expressed in Device Independent Unit (DIU).
1 DIU = 1/96th of an inch.
1 DIU = 1 pixel on a 96 DPI display.
1 DIU = 2.083333 pixels on a 200 DPI display.
Scale and Zoom
You look confused here. A scale/zoom of 1.0 means 100%, 2.0 means 200%, 0.5 means 50%. There is not unit, it's a factor.

Related

Eliminating a border effect

This is a work project. I inherited some code using SharpDX (a DirectX layer). One of my tasks is to fix a piece of code where certain image effects are applying to a geometric shape containing a fill. If the filter is applied to the fill itself, it doesn't conform to the edges. I've figured out the code to pull out an excerpt using the Geometry of the object. For various reasons, they want to keep the fill that exists outside of the shape (namely, we have some distortion effects that pull in pixels outside of the shape), so I need to overlay it over the background. The problem I'm running into is that I'm getting this single-pixel border...
Applying the Soft Edge filter to the visible part
The background with the shape cut out
The two composited together in the program
What I'm actually getting
I can't share a good bit of the code, due to parts of it being proprietary, but the mask is a byte array. I'm building it using the following code:
SingleChannelBitmap mask = new SingleChannelBitmap(MaxRequiredPixels.Width, MaxRequiredPixels.Height, 255);
mask.FillShape(new RectangleF(new PointF(0,0), mask.Size), this.Geometry, 0);
255 is the maximum Alpha value (transparent). I invert it to take the slice out of the background. The only thing I can think of is that, when I do the masking, it's not including the outer edge of the Geometry. I'm going to try expanding the mask by one pixel in the crudest way possible (basically, scanning through and taking anything which is 0 transparency and adding a 0 transparency pixel to the left, right, up, and down), but I know there has to be a more elegant solution.
This has to work for the 3D Edge bevel filter as well, so doing an arbitrarily large whitespace probably won't work for me either.
What you describe is essentially the same haloing problem that sometimes occurs with displaying PNG images. The PNG export process from several programs will store a solid color for any portions of the PNG that has zero alpha, instead of the actual color at those pixels. This makes them function similar to other image formats (GIF) which use a specific color to encode transparent pixels. This significantly reduces the size of the file, however, can cause issues when sampling the image.
Your situation is similar. Although the masked pixels have zero alpha, when doing bilinear sampling, you may sample in between pixels, mixing both color and alpha values (unless pixel and texel centers are perfectly aligned). For example, if you have a 100% alpha, white pixel, next to a 0% alpha red pixel, and sample in between both, the result will be a pink pixel at 50% alpha.
There are several possible solutions:
You could extend the borders of the color layer, such that the 0% alpha border has the same color as its non-0% alpha adjacent pixels.
Intentionally line up the pixel and texel centers, although this can be tricky and/or not possible, depending on your requirements (mostly dependent on resolution).
Use 'nearest' sampling, instead of bilinear when displaying the image. This way, you will never blend in a 0% alpha pixel. However, this may also not be desirable, because your image will likely exhibit more aliasing effects.

Why this Bitmap Image changes its size after I load it?

Quick question:
I have this 1000 x 1000 bitmap image:
and I use this routine to load it:
private BitmapSource initialBitmap = new BitmapImage(new Uri("C:\\Users\\...\\Desktop\\Original.bmp"));
Why after I load it, and right after I step over the above line, I see it as 800 x 800?
P.S I want it to be 1000 x 1000 and without using any Resize functions. It was working and suddenly it is 800*800 !
The values returned by BitmapSource.Width and BitmapSource.Height are not in pixels, but rather WPF's device-independent units, which are always 96 dpi. E.g.:
Gets the width of the bitmap in device-independent units (1/96th inch per unit).
If you want to know the actual pixel width and height, you need to use the PixelWidth and PixelHeight properties.
Your question isn't very specific, but if what you are actually concerned about is having the bitmap display at the same size in which it was authored, then the easiest solution is to make sure you author it at 96 dpi. Whatever program you're using to author the bitmap likely has a place where you can set the bitmap resolution. Typically this can be set with or without changing the pixel dimensions of the image (i.e. scaling the image larger or smaller); you want to do it without scaling the image, so that the pixel dimensions remain the same but the dpi changes to match what WPF is using.
Note that this still won't guarantee the bitmap displays at a specific pixel size. The display resolution can be and often is different from 96 dpi, in which case WPF will scale images to ensure that the physical dimensions of the image (i.e. the dimensions in inches, millimeters, etc.) are correct according to the information in the bitmap. For example, 960 pixels wide at 96 dpi means 10" wide. On a 120 dpi display, this means displaying the bitmap large enough so that its width uses 1200 display pixels.
If you want or need the bitmap to display at exactly the same number of display pixels regardless of the display resolution, then you'll have to set a transform where you display the image to reverse the effect of the scaling that WPF would otherwise do. This requires knowing the display resolution, of course.
Here are some other related Stack Overflow questions which you might find useful:
RenderTargetBitmap renders image of a wrong size
WPF for LCD screen Full HD
Screen Resolution Problem In WPF?
This is by design. Note the MSDN documentation for BitmapSource.Width/Height:
Gets the width of the bitmap in device-independent units (1/96th inch
per unit). (Overrides ImageSource.Width.)
Instead you should use the PixelWidth / PixelHeight property:
Gets the width of the bitmap in pixels.
A rather confusing choice or terms, imo, but there you go..

Going fullscreen without stretching in an XNA game

I've got a 2D game that I'm working on that is in 4:3 aspect ratio. When I switch it to fullscreen mode on my widescreen monitor it stretches. I tried using two viewports to give a black background to where the game shouldn't stretch to, but that left the game in the same size as before. I couldn't get it to fill the viewport that was supposed to hold the whole game.
How can I get it to go fullscreen without stretching and without me needing to modify every position and draw statement in the game? The code I'm using for the viewports is below.
// set the viewport to the whole screen
GraphicsDevice.Viewport = new Viewport
{
X = 0,
Y = 0,
Width = GraphicsDevice.PresentationParameters.BackBufferWidth,
Height = GraphicsDevice.PresentationParameters.BackBufferHeight,
MinDepth = 0,
MaxDepth = 1
};
// clear whole screen to black
GraphicsDevice.Clear(Color.Black);
// figure out the largest area that fits in this resolution at the desired aspect ratio
int width = GraphicsDevice.PresentationParameters.BackBufferWidth;
int height = (int)(width / targetAspectRatio + .5f);
if (height > GraphicsDevice.PresentationParameters.BackBufferHeight)
{
height = GraphicsDevice.PresentationParameters.BackBufferHeight;
width = (int)(height * targetAspectRatio + .5f);
}
//Console.WriteLine("Back: Width: {0}, Height: {0}", GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight);
//Console.WriteLine("Front: Width: {0}, Height: {1}", width, height);
// set up the new viewport centered in the backbuffer
GraphicsDevice.Viewport = new Viewport
{
X = GraphicsDevice.PresentationParameters.BackBufferWidth / 2 - width / 2,
Y = GraphicsDevice.PresentationParameters.BackBufferHeight / 2 - height / 2,
Width = width,
Height = height,
MinDepth = 0,
MaxDepth = 1
};
GraphicsDevice.Clear(Color.CornflowerBlue);
The image below shows what the screen looks like. The black on the sides is what I want (and is from the first viewport) and the second viewport is the game and the cornflower blue area. What I want is to get the game to scale to fill the cornflower blue area.
Use a viewport http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.viewport_members.aspx
As is also the case in commercial games, you should provide an option to the user that allows them to switch between 4:3 aspect and 16:9 aspect. You should be able to just modify the camera viewing ratio accordingly.
EDIT:
As far as I have seen, there are no games that 'auto-detect' the proper aspect ratio to use.
As has been pointed out, there are ways to make a good guess as to what the proper aspect ratio is. If XNA allows you to get at the current Windows user's screen settings data, you can determine an aspect ratio based off of the monitor resolution.
Once you have determined the monitor resolution of the user, you can best decide how to deal with it. At first, the best bet may be to just put black bars on the left/right side of the screen to allow full-screen with a 16:9 aspect ratio that is essentially still using the 4:3 artwork.
Eventually you could modify the game so that it changes the viewing port size when the aspect ratio is 16:9. This wouldn't require changing any art assets, just how they are being rendered.
First of all I'm assuming you're talking about XNA 4.0, which AFAIK there are breaking changes between XNA 3.x and XNA 4.0.
I'm relatively new at XNA, however it seems to me that your assets does not fit the size of the window. Let's say that your game are is 320x240 and your window is bigger e.g. 640x480.
Thus you can specify PreferredBuffer in order to scale up your application. So, tell to XNA you are going to use 320x240 by setting the following values;
_graphics.PreferredBackBufferWidth = 320;
_graphics.PreferredBackBufferHeight = 240;
Additionally you can start fullscreen mode by setting:
_graphics.IsFullScreen = true;
Also, you have to handle manually the how the items should change their size once the Window has changed their size.
Checkout my sample at.
https://github.com/hmadrigal/xnawp7/tree/master/XNASample02
(BTW, you can press F11 to switch between fullscreen and normal view)
Best regards,
Herber
I'm not sure if you can actually scale your view port like that. I understand what you're trying to do, but to do it you'd have to do the following.
Set your screen backbuffer width and height to the 16:9 resolution.
Program in the displacement so that objects didn't draw in those borders.
The thing is, all major games these days, if you play them on a 16:9 monitor and select a 4:3 resolution, will stretch to fit the screen. This isn't something you usually want to overcome. You either support many resolutions in your game, or you will get stretching when a user uses the wrong resolution for his or her screen type.
Usually, one sets up their game, and their textures to work based on the relative dimensions of the current viewport or backbuffer width and height. This way, regardless of the resolution inputted, the game scales to work with that width/height ratio.
It's a bit more work, but in the end, makes your game far more polished and compatible with a wide array of systems.
The only time this may not be done is if the app runs in a window (NOT fullscreen).

Resolution Free Application

What is meant by the Resolution free application, As I have discussed it with many of my friend and they says that resolution free mean what ever resolution user want to see an application it should adjust it position, the resolultion is monitor resolution or any say 100 by 100 what is resolution?
Ideally, applications would use higher pixel densities to show more detail. For example, a high-resolution monitor could display similarly sized toolbar icons but use the extra pixels to render sharper graphics. That way you could keep the same basic layout but offer increased clarity and detail. For a variety of reasons, this solution hasn’t been possible in the past. Although you can resize graphical content that’s drawn with GDI/GDI+, User32 (which generates the visuals for common controls) doesn’t support true scaling.
WPF doesn’t suffer from this problem because it renders all user interface elements itself, from simple shapes to common controls such as buttons. As a result, if you create a button that’s 1 inch wide on your computer monitor, it can remain 1 inch wide on a high-resolution monitor—WPF will simply render it in greater detail and with more pixels.
WPF bases its scaling on the system DPI setting, not the DPI of your physical display device. It uses the system DPI setting when it calculates sizes.
WPF Units
A WPF window and all the elements inside it are measured using device-independent units. A single device-independent unit is defined as 1/96 of an inch. To understand what this means in practice, you’ll need to consider an example.
Imagine that you create a small button in WPF that’s 96 by 96 units in size. If you’re using the standard Windows DPI setting (96 dpi), each device-independent unit corresponds to one real, physical pixel. That’s because WPF uses this calculation:
[Physical Unit Size] = [Device-Independent Unit Size] × [System DPI]
= 1/96 inch × 96 dpi
= 1 pixel
Essentially, WPF assumes it takes 96 pixels to make an inch because Windows tells it that through the system DPI setting. However, the reality depends on your display device.
For example, consider a 20-inch LCD monitor with a maximum resolution of 1600 by 1200 pixels. Using a dash of Pythagoras, you can calculate the pixel density for this monitor, as shown here:
[Screen DPI] = ((Sqroot of)(1600*1600)+(1200*1200))pixels/19 inches = 100dpi
In this case, the pixel density works out to 100 dpi, which is slightly higher than what Windows assumes. As a result, on this monitor a 96-by-96-pixel button will be slightly smaller than 1 inch.
On the other hand, consider a 15-inch LCD monitor with a resolution of 1024 by 768. Here, the pixel density drops to about 85 dpi, so the 96-by-96 pixel button appears slightly larger than 1 inch.
In both these cases, if you reduce the screen size (say, by switching to 800 by 600 resolution), the button (and every other screen element) will appear proportionately larger. That’s because the system DPI setting remains at 96 dpi. In other words, Windows continues to assume it takes 96 pixels to make an inch, even though at a lower resolution it takes far fewer pixels.
Reference:
Pro WPF in C# 2008: Windows Presentation Foundation with .NET 3.5, Second Edition
WPF uses device-independent coordinates which means applications scale correctly and uniformly at different DPI settings.
Another very useful feature of WPF is the ViewBox which can be used to create a scalable application. Try the following.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300">
<Viewbox Stretch="Uniform">
<Grid Width="300" Height="300">
<Button Content="Button" Margin="16,8,0,0"
Width="104" Height="32"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
<CheckBox Content="CheckBox" Margin="136,8,0,0"
Width="112" Height="24"
HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</Viewbox>
</Window>
This simple example is a little off cause of the window title bar and border but you get the idea. When this window is resized, the content scales uniformly so it looks the same at any resolution.
You can use WPF for your application. Every control that you create on a form will be the in different screen resolutions.
One of the main claimed benefits of WPF is “resolution independence”. Often this benefit is described in relatively vague terms leading people to believe that it means that the same WPF window will display on any monitor at the same size regardless of the resolution at which that monitor is set.
I think it means that the proportions should be the same, on different resolutions. It doesn't mean the application will resize its windows internally to adjust to the new resolution. But this is not universal.
Another very important thing to mention, is the context this is brought on. For example there are different expectations for a web application, which may change the layout or page size depending on the resolution. Video games, which may render things very differently depending on the screen resolution and aspect. And regular windows applications (GUI), which I already mentioned.

How do I determine the true pixel size of my Monitor in .NET?

I want to display an image at 'true size' in my application. For that I need to know the pixel size of the display.
I know windows display resolution is nominally 96dpi, but for my purposes I want a better guess. I understand this information may not always be available or accurate (e.g. older CRT displays), but I imagine with the prevelance of LCD displays that this should be possible!
Is there a way to get the pixel size of my display?
Is there a way to determine if the pixel size is accurate?
.NET API's preferred (I couldn't find them), but Win32 is OK too, I'm happy to P/Invoke.
For the display size you'll want Screen.PrimaryScreen.Bounds.Size (or Screen.GetBounds(myform)).
If you want the DPI, use the DpiX and DpiY properties of Graphics:
PointF dpi = PointF.Empty;
using(Graphics g = this.CreateGraphics()){
dpi.X = g.DpiX;
dpi.Y = g.DpiY;
}
Oh, wait! You wanted actual, hold a ruler up to the monitor and measure, size?! No. Not possible using any OS services. The OS doesn't know the actual dimensions of the monitor, or how the user has it calibrated. Some of this information is theoretically detectable, but it's not deterministic enough for the OS to use it reliably, so it doesn't.
As a work around, you can try a couple of things.
You can try to query the display string of the installed monitor device (I'm not sure how to do that) and see if you can parse out a sensible size out of that. For example, the monitor might be a "ValueBin E17p", and you might deduce that it's a 17" monitor from that. Of course, this display string is likely to be "Plug and Play Monitor". This scheme is pretty sketchy at best.
You could ask the user what size monitor they have. Maybe they'll know.
Once you know (or think you know) the monitor's diagonal size, you need to find its physical aspect ratio. Again, a couple of things:
Assume the current pixel aspect ratio matches the monitor's physical aspect ratio. This assumes that (A) the user has chosen a resolution that is ideal for their monitor, and that (B) the monitor has square pixels. I don't know of a current consumer-oriented computer monitor that doesn't have square pixels, but older ones did and newer ones might.
Ask the user. Maybe they'll know.
Once you know (or think you know) what the monitor's diagonal size and physical aspect ratio are, then you you can calculate it's physical width and height. A2 + B2 = C2, so a few calculations will give it to you good:
If you found out that it's a 17" monitor, and its current resolution is 1280 x 1024:
12802 + 10242 = 2686976
Sqrt(2686976) = 1639.1998047828092637409837247032
17" * 1280 / 1639.2 = 13.274768179599804782820888238165"
17" * 1024 / 1639.2 = 10.619814543679843826256710590532"
This puts the physical width at 13.27" and the physical height at 10.62". This makes the pixels 13.27" / 1280 = 10.62" / 1024 = 0.01037" or about 0.263 mm.
Of course, all of this is invalid if the user doesn't have a suitable resolution, the monitor has wacky non-square pixels, or it's an older analog monitor and the controls aren't adjusted properly for the display to fill the entire physical screen. Or worse, it could be a projector.
In the end, you may be best off performing a calibration step where you have the user actually hold a ruler up to the screen, and measure the size of something for you. You could:
Have the user click the mouse on any two points an inch (or a centimeter) apart.
Draw a box on the screen and have the user press the up and down arrows to adjust its height, and the left and right arrows to adjust its width, until the box is exactly one inch (or centimeter) square according to their ruler.
Draw a box on the screen and have the user tell you how many inches/centimeters it is in each dimension.
No matter what you do, don't expect your results to be 100% accurate. There are way too many factors at play for you (or the user) to get this exactly correct, every time.
Be aware that 96 dpi is usually pretty close to accurate. Modern pixels on non-projected screens all tend to be about 0.25 mm, give or take, so you usually end up with about 100 physical pixels per inch, give or take, if the monitor is set to its native resolution. (Of course, this is a huge generalization and does not apply to all monitors. Eee PCs, for example, have pixels about 0.19 mm in size, if I remember the specs correctly.)
sorry, you've got to P/Invoke for this information.
Here's the link that I utilized for it a while ago:
http://www.davidthielen.info/programming/2007/05/get_screen_dpi_.html
You can check by just manually calculating from your screen size
cos(45)*LCD_SCREEN_DIAGONAL_IN_INCHES/sqrt(HORZ_RES^2 + VERT_RES^2)
That would give you the pixel width in inches
GetDeviceCaps can be P/Invoke'd to get some figures, but I've never known the figures to be that trustworthy...
You may obtain the physical dimensions of the display using the EDID information stored in the registry. You can obtain the appropriate monitor's registry key using the EnumDisplayDevices windows API call.
Physical Dimensions to the Screen object:
TL;DR
WPF's True Size = Pixels * DPI Magnification
DPI Magnification:
Matrix dpiMagnification
= PresentationSource.FromVisual(MyUserControl).CompositionTarget.TransformToDevice;
double magnificationX = dpiMagnification.M11;
double magnificationY = dpiMagnification.M22;
Discussion
I had trouble solving this question still in 2020. Back when this question was asked/answered in 2009, .NET C# probably meant Windows Forms. But WPF is the de facto standard of the day...
By asking about "true size" you have probably already figured out that the operating system does some calculation with actual pixels (say 1366x768, which I understand is usual laptop resolutions) and the DPI (hard to find) in order to give a control's true size. And you are trying to make an app that scales to different monitors.
This DPI actual number seems to be hidden, but it has been normalized (converted to a percentage). Assume 100% = 96 DPI, just because the actual number does not matter anymore. People can easily increase the system-wide text size by going to Desktop on Windows 10 > right click > Display settings > section Scale and layout > change the percentage to magnify text and other elements.
You can find the pixels another way, and multiple/divide the pixel by the DPI percentage in order to get true size. For instance, I want to drag a UserControl around a canvas element of a WPF window with the mouse. The user control's pixel count and the mouse xy-coordinates were off by the normalized DPI. In order to keep the mouse moving at the same rate as the user control, I use:
double newXCoord = System.Windows.Forms.Cursor.Position.X;
double newYCoord = System.Windows.Forms.Cursor.Position.Y;
double deltaX = newXCoord - oldXCoord;
double deltaY = newYCoord - oldYCoord;
double magnificationX = 1;
double magnificationY = 1;
Matrix dpiMagnification
= PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
if (magnificationMatrix != null)
{
magnificationX = dpiMagnification.M11;
magnificationY = dpiMagnification.M22;
}
PixelsFromLeft += deltaX / m_magnificationX;
PixelsFromTop += deltaY / m_magnificationY;

Categories