I'm using WinForms to create a 512x512 px image with 96dpi resolution. After initialization, I use OnPaint to draw this image using Graphics.DrawImage. What I get as I run the program is the image shown below: essentially a 1024x1024 image which contains some jung vaguely resembling what I want to show, except it's made up of 2x2-pixel blocks all having uniformly identical colors. Which is weird.
Can someone help me figure out what is going on? This is only happening on high-DPI displays, the program works just fine on ordinary ones.
Update 1: I explicitly set the process to be DPI-unaware with SetProcessDpiAwareness(_Process_DPI_Awareness.Process_DPI_Unaware); and explicitly call DrawImage in a rectangle, i.e.,
e.Graphics.DrawImage(bmp,
new Rectangle(0, 0, 512, 512),
new Rectangle(0, 0, 512, 512), GraphicsUnit.Pixel);
and it did not help.
Edit 2: After turning DPI awareness on, I now get the same junk, but at the right size:
Edit 3: Finally got it, the artifacts were the results of my rendering things incorrectly on the CUDA side. The high-DPI nonsense was just a distraction.
This is only happening on high-DPI displays
That is entirely normal, you are seeing the image getting rescaled to match the DPI of the monitor. Which makes it bigger so the size in inches is the same.
The crude way to get this done is when you rely on DPI virtualization. Windows lets your program behave like it does on 96 DPI monitor, but gets it to draw into a bitmap. And rescales that bitmap to display it on the screen. Pretty crude, but your program is still usable, text quality suffers pretty noticeably.
The not-crude way is when you declared you program to be dpiAware. Now Windows no longer helps and it is Graphics.DrawImage() that takes care of the job. It by itself rescales the image to match the image DPI to the display DPI. Text looks very crisp. The image, meh, you have to move your chair back further.
If you don't want this rescaling to happen then you have to use the Graphics.DrawImage() overload that takes a Rectangle argument. But keep in mind that the image turns into a postage stamp on such a nice display. You'll have to move your chair closer :)
Related
I have a bitmap (from a .bmp file) that is actually a small part of a larger screen dump that I took earlier.
Now I want to display that bitmap in a PictureBox control (WinForms), but I have trouble getting it to display at the same ratio as when it was captured originally.
I assume it has something to do with Windows' DPI setting for my monitor, but how can I shortcut that and make the bitmap be displayed exactly as when captured?
I figured it out! The problem was not WinForms, but the horizontal and vertical resolution associated with the displayed bitmap. When copying the part of the original screendump into a new bitmap (the one that is later assigned to the Image property of the PictureBox), the new bitmap had a different (=default) resolution setting. By changing the resolution setting to match the one from the source bitmap (the original screendump), the problem went away!
Problem:
We need help with how to use WinForms' ability to auto-scale to different DPI’s to allow us to print our Forms at 600dpi, rather than at the screen DPI.
For What-You-See-Is-What-You-Get printing, we have been simply taking our nicely laid out window and printing it (turning off scrollbars and buttons and such). That works great EXCEPT for one thing: it comes out at 96dpi or 120dpi (whatever is the screen resolution)… either of which look grainy and unprofessional (our customers are complaining). And although it is as readable as what would be on the screen, you expect printed documents to be MORE readable than on-screen… you expect to be able to see additional details, to be able to read smaller text, etc.
Alternatives considered:
Given we have auto-scaling working great, such that our window looks good in 96dpi, 120dpi, 144 dpi, etc., we were hoping we could just draw our window at 600dpi and then print that.
OR, we looked at drawing the window off-screen 5-6x larger than normal such that we have the same number of pixels as 600dpi, but at 96 or 120 dpi… but then drawing that giant window to the printed page at 300 or 600 dpi (whatever the printer is).
If you can tell us how to do either of those alternatives, OR if you can give us a different way to accomplish our goal, then we would greatly appreciate it.
Current code:
In case it matters, our Form consists of a FlowLayoutPanel laying other smaller FlowLayoutPanels into columns, those smaller FlowLayoutPanels laying out a single column of TextBoxes, RichTextBoxes, a third-party RichTextEditor, PictureBoxes, and DataGridViews. We use a class derived from PrintDocument implementing OnBeginPrint, OnPrintPage, and OnEndPrint. In OnPrintPage, it manipulates our normal window off-screen (below and right of the actual screens) to fit the page size, then asks our main panel (the top FlowLayoutPanel) to DrawToBitmap, then uses the Graphics object passed into the PrintEventArgs to DrawImage that Bitmap. We also use Graphics.DrawString to apply a footer to each page. The main code:
using (Bitmap bm = new Bitmap(sz.Width, sz.Height))
{
Rectangle rect = new Rectangle(0, 0, sz.Width, sz.Height);
mp.DrawToBitmap(bm, rect);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; // so footer is anti-aliased
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; // so when we scale up, we smooth out the jaggies somewhat
e.Graphics.DrawImage(bm, this.MarginBounds, rect, GraphicsUnit.Pixel);
if (this.Footer != null)
e.Graphics.DrawImage(this.Footer, this.FooterLocation);
if (!string.IsNullOrEmpty(pageNumber))
{
e.Graphics.DrawString(pageNumber, KBStyle.Normal.Font, Brushes.Black,
this.MarginBounds.X, this.FooterLocation.Y + FooterOffset);
}
}
How should we do this to get 600dpi to the printed page? (Or even 300 dpi would be great!)
When we print this, it looks far better when printed from a 120dpi machine than when printed from a 96dpi machine, hence why we know it is printing at screen resolution. But also makes us wonder if there’s some simple way to tell it “this Form should draw at 600 dpi” and then all the rest of the code above just works.
Note: if we grab an EMF (Enhanced Metafile) and print it to the printer in the code above, that EMF comes out at 600dpi. Unfortunately, we haven’t found a DrawToEMF method that we can call on the FlowLayoutPanel instead of DrawToBitmap. Changing the Bitmap to be 600dpi doesn’t help… the DrawToBitmap method still seems to draw the bitmap at screen resolution.
Thanks!!
Okay, I have figured this out... and it works great!
I still don’t know how to create a form at 300dpi and use the auto-scaling functionality.
BUT…
I have proven that if you create the window 3.125x larger than needed at 96 dpi, and scale the font up 3.125x, and so on, such that everything is the pixel count you’d need to be at 300dpi, even though your screen is at 96dpi, then you can use the normal Control.DrawToBitmap() functionality to turn that into a Bitmap, and then you can use the GDI Graphics.DrawImage(thatGiantBitmap, giantSrcRect, pageSizeDestRect) to the printer Graphics object, and it will map those giant 96dpi pixels to the page-size 300dpi pixels, giving you a 300dpi print. Perfect.
For any of our windows that support resizing AND let our users zoom the contents arbitrarily, then printing What-You-See-Is-What-You-Get is easy:
In OnBeginPrint of your PrintDocument, do:
(1) Optionally dup the Form so as to not mess with what the user is looking at
(2) Move the Form you want to print off-screen (below and right of all your screens)
(3) Set the Form so that it is allowed to grow bigger than the screen size (by default WinForms won’t grow larger than the screen)
(4) Divide 300 dpi by your screen dpi to get the growth factor
(5) Grow your Form by growth factor
(6) Zoom its contents by growth factor (if they don’t auto-scale / auto-zoom with the Form’s size)
In OnPrintPage of your PrintDocument, do:
(7) On whatever Control in your Form you want to print, do DrawToBitmap() to a Bitmap the size of that Control
(8) On the e.Graphics do DrawImage(thatGiantBitmap, giantSrcRect, pageSizeDestRect)
That DrawImage call will draw at the printer’s resolution, if you have that many pixels in your thatGiantBitmap. In this case, I computed the Bitmap to give the number of pixels needed for 300 dpi, so I will get 300 dpi print-outs even if the printer is 600 dpi. If you need full 600 dpi, just use 600 dpi in step 4's calculation.
Total guess here, but what about using CSS? I'm guessing at how much you want to scale, and don't know how you would know what scale the printer is.
Using the print media query makes this work for print but leaves the screen view alone.
#media print {
* {
transform: scale(2000px,2000px);
}
}
I need to know whether there is any advantages or disadvantages on using 2d graphics in wpf over 2d images or not?
I mean if I'm going to display an ellipse in a wpf window which one is more useful and why? To create an Ellipse object or to create an Image control and then load an pre-designed ellipse image into it?
Does using 2D graphics have any advantages in the sense that they consume less memory or increase performance or anything like that?
Thanks in advance.
An Image is not realy good scalable.
An vector Graphics is.
On a high dpi display your Image with Width = 300 (Units not pixels) and Height = 300 (units) is on a Display with 96 dpi not bigger than a Display with 144dpi. But the Image on the 144 dpi display needs more Pixels for the same size.
1 Unit is 1/96 inch.
So it is better to have a scalable "image" than a fixed one.
Its one of the features of wpf that winform don't have! and the reason why you should not use Pixels as a Size / Width / Height / Position / etc.
Excursus:
In winform it is hard to programm a scalable programm. on high dpi monitors the font, buttons etc. looks very small. there was no option to solve this problem.
so windows programmed an algorithm that creates a bitmap of the programm and scales this up.
So: the progamms width and height is the same as before, but the user sees a much bigger one (the Bitmap). The user input is then recalculated on the real sized application; Everything is working fine - and looking fine.
Hope that helps and is correct.
This is a follow on to my previous question Speeding Up Image Handling
Apologies if I should have amended that question in some way rather than starting a new one.
I have tried all sorts of different things to speed up the drawing of an image on screen.
I thought that compressing the image until it is smaller would have an effect. However while this might save memory for the object I do not think it has any effect on how long it takes to draw. I tried converting the image to jpeg and using 100% compression. However while this creates a blocky image it does not affect the drawing time. I now think this is because the number of pixels that get rendered is not changed by this
I tried reducing the color palette to 256 colors. This makes the size smaller since it uses less bytes per pixel but does not seem to affect drawing on screen. I had thought that reducing the bytes per pixel GDI+ has to handle might save some time but it is not enough for me to see so far.
So am I wasting my time looking at compression and palette?
I assume that time taken will be affected by the number of pixels to be drawn (width x height) and I have resized the image to match the pixel size as displayed on screen. I think this is the one thing that does have an effect....
I have looked at how to stop autoscaling of the image - does my resize stop that or can the image still be autoscaled when it is rendered?
I am wondering if I can replace the DrawImage call with something using p/Invoke or other API call (which I admit I don't really understand).
You could use the Bitblt P/Invoke, which copies image data directly. It does require some initialisation to convert the image data to something the display device understands, but this copy operation is lightning fast. If you would like to know what exactly is happening in DrawImage btw, use a tool like ILSpy or Reflector.NET to inspect the method.
See BitBlt code not working for an example. For some information about Bitblt see http://www.codeproject.com/KB/GDI-plus/flicker_free.aspx.
I found an article on image processing from here: http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing Everything works fine.
I want to keep the high quality when resizing the image. I think if I can increase the DPI value I can achieve this. Does anyone know if this is possible? And if so, how can I implement it in C#?
For starters, it's worth pointing out that there are two general categories of images; vector [e.g. SVG, WMF, Adobe Illustrator and Corel Draw Graphics] and bitmap (also called raster) images [e.g. Bitmap, JPEG and PNG Images].
Vector images are formed from a series of mathematical equations and/or calculations. Bitmap images, on the other hand, are made up of individual dots (pixels) each corresponding to a particular feature on the object the image is taken of.
If it should happen that you want to resize an image, the first thing to consider is if it is a bitmap or vector image. By virtue of the fact that vector images are obtained from calculations, they can be perfectly resized without losing any detail. The case is different for bitmap images. Since each pixel is independent of the other, when you desire to resize it, you are simply increasing or decreasing the source to target pixel ratio.
So in order to double the size of a vector image, simply multiply the target dimensions by two and everything comes out all right. If you should apply the same effect on a bitmap, you are actually increasing each source pixel to cover four pixels (two rows of two horizontal pixels).
Of course, by applying interpolation and filtering, the computer can "smooth" out the edges of the target pixels so they seem to blend into each other and give the appearance of a reasonably resized image but this output is never the same as resizing a vector image; vector images resize perfectly.
You also mentioned DPI in your question. DPI is essentially the number of pixels that correspond to an inch when the image is printed not when it is viewed on a screen. Therefore by increasing the DPI of the image, you do not increase the size of the image on the screen. You only increase the quality of print [which needless to say depends on the maximum resolution of the printer].
If you really desire to resize the image and the image is a bitmap, as a rule of thumb, do not increase the size beyond 200% of the original image's size else you'll lose the quality.
You can see this answer for code to resize bitmap images.
To see a sample vector image, go to this link.
Note Try zooming in and out of the image to see how well it resizes.
A typical bitmap are the StackOverflow sprites. They do not keep their quality resized.
Further Reading
Vector Graphics: http://en.wikipedia.org/wiki/Vector_image
Bitmap Graphics: http://en.wikipedia.org/wiki/Bitmap_image
Simply If the original image is smaller then the re-sized image then there is hardly anything you can do. Rest is a research problem.
This would only be possible if it's a vector graphic. Look into SVG. Otherwise, I think you might need Silverlight or Flex for that part.
What you're asking isn't really possible. You can't enlarge an image while maintaining the same quality.
If you think about an image as a mapped array of pixels (literally, a "bit-map"), this makes sense. The image is saved with a fixed amount of data, and that's all you have to work with when you resize it. Any examples to the contrary (like TV shows, as suggested by one of the comments) are purely fictional.
The best that you can do is set the InterpolationMode property of the Graphics object you're using to do the resizing to "HighQualityBicubic", which is the highest quality smoothing algorithm supported by GDI+ and in fact by every major graphics package on the market. It's the best that even Adobe Photoshop has to offer. Essentially, interpolation means that the computer is calculating the approximate value of the new pixels you're adding to make the image larger from the relative values of neighboring pixels. It's a "best guess" method, but it's the best compromise we've come up with yet.
At the very least, the resulting images won't have "jaggies" or rough, pixelated lines.
Sample code:
Graphics g;
g.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// ... insert the rest of your code here
Beyond that, it's worth noting that GDI+ (which the .NET Framework uses internally for graphics routines) works best with image sizes that are multiples of 16. So if it all possible, you should try and make the width and height of your resized images a multiple of 16. This will preserve as much of the original image quality as possible.
The ideal solution is to switch to vector graphics that can be resized at will. Instead of pixel information, they contain mathematical information used to draw or "render" the image. Read more on Wikipedia.
let's try metadata in GDI+, may be it can suit your request