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);
}
}
Related
I really don't get how to adjust the right settings for printing.
Using OriginAtMargins makes my margins work, but Margin(0, 0, 0, 0) goes outside the paper size (which is 302x700), and drawing a rectangle using the PageBounds sets it in the center, with I estimate to have 1-inch margins, and the MarginBounds sets in at the center of the PageBounds which also has a 1-inch margins. And most of the time, the preview is very different from the printed one, especially at the margins.
When I'm not using OriginAtMargins, the PageBounds is the same as the paper size, and the MarginsBounds is set at the center with 1-inch margins. The problem here is that although the preview shows the contents nicely, the right side of the document is being cut off when printed, so I had to adjust the rectangle.
Overall, I can hard-code the "just right" sizes of the rectangle/margin, but there will be too much unnecessary estimates and trial-and-error.
My question is: What are the proper settings for printing? So that the preview and the printed is in sync, and what you coded is what you see, especially in sizes
The "game" I am trying to create has many buttons and images on the screen at once, and the buttons are designed for the base (what I believe to be 800x600) console size. The buttons and sprites are all in set positions.
The issue I am having is trying to get every image to scale when I do isfullscreen=true. The images stay in their relative position, but I need them to 'scale' based on the the actual size of the window.
While searching for an answer, I have found many that scale individual images or scale them based on the aspect ratio but what I am attempting to do is scale all images, no matter the aspect ratio, depending on the actual size of the XNA window. For example, If I have 3 100x60 sprites and 2 200x90 sprites placed on a 800x600 screen, how would I change the sprites to be the same relative size if the window size were to be changed to 1980x720 without having to manipulate each image?
Thanks
Edit: I've tried using a scale matrix, but that seems to require me setting the EXACT scale for that exact size, meaning I have to create a different scale matrix for each possible window size, which is not what I am trying to achieve.
I've fixed the issue i've been having using a ScaleMatrix, as I was using them incorrectly before.
Before, I was using a matrix of (800, 600) but now (I don't have access to the environment right now so it will have to be in pseudo) I have changed the code so its a variable scale:
(f)XScale = 800 / Viewport.Width
(f)Yscale = 600 / Viewport.Height
Matrix.createScale(XScale, YScale, 1 , 1)
I then passed this to the Spritebatch.Begin. The issue is, if you have something rendered to a triangle (Such as a background), then you may wish to render it in a different spritebatch.Begin as this scale will mess with the triangle.
I have a background rectangle and it applies the scale to it, which puts it off the screen. Its fine if it is something you wish to have scaled, such as a button rectangle.
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 :)
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.
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