I have a Bitmap variable and I copy smaller 32x32 png files (loaded as Bitmaps) onto the bitmap. However, some png's are scaled up (always the same ones) and appear as 36x36 for example after copying. Almost as if some png's have another DPI or something? How can I prevent this?
Graphics g = Graphics.FromImage(destinationImage);
g.DrawImage(sourceImage, location); // sourceImage is sometimes larger than it actually is. On disk it is 32x32 but after copying it might be bigger...
g.Dispose();
I guess you are right about DPI, as it's stated in the documentation:
This method draws an image using its physical size...
I'm too lazy to make a test project, but I think Graphics.DrawImage(Image, Rectangle) with rectangle size equals to source image size will fix your problem.
The Image.Horizontal/VerticalResolution property matters. If it doesn't match the dots-per-inch setting of your monitor then the image is going to be drawn proportionally larger or smaller. This tends to be undesirable, use the DragImage(Image, Rectangle) overload to force it to display at exact 32 x 32 pixels.
Related
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..
I have a Bitmap which is in 256-colour indexed format, I need to resize it so I create a new Bitmap in 24-bit RGB format and draw it using a Graphics object as I cannot create a graphics object from an indexed colour bitmap. I then need to save the resized image back as an indexed colour format so I use FormatConvertedBitmap to convert to indexed colour like this:
BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
FormatConvertedBitmap converted = new FormatConvertedBitmap();
converted.BeginInit();
converted.Source = bitmapSource;
converted.DestinationFormat = System.Windows.Media.PixelFormats.Indexed8;
converted.DestinationPalette = new BitmapPalette(bitmapSource, 256);
converted.EndInit();
This works but the solid colours are now grainy and contain other colour pixels. Is there a better method to either resize an indexed colour image or to maintain the solid colours?
Yes, this is pretty inevitable. The core property you want to control to influence this is Graphics.InterpolationMode. A high quality selection like Bicubic will add a lot of colors to the resized image. Trying to whack it back to 256 colors is going to produce a grainy image. You could use NearestNeighbor instead to reduce the number of added colors but you'll end up with a blocky image.
You could try the GIF encoder, it is forced to resample the image back to 256 colors as well. It uses a dithering algorithm. But tends to produce flecky artifacts that become noticeable when the image has large areas of similar colors, like a blue sky. There is no magical cure beyond simply not resampling back. There is just no point in doing that with the wonderful hardware we have these days.
My application is used to design airports for a flight simulator. Users can add one or more images as background. The image(s) can be sized accurately and then used as a template to lay down features such as runways, aprons and so on.
I use a third party graphics library (Piccolo) which has an image class (as far as I can see it is a simple wrapper for System.Drawing.Image).
So far I have done little except allow the user to add an image, size it and so on. It will be no surprise that users sometimes complain of poor performance. We tell them not to load large images (up to 100k seem OK) but don't stop them and 100Mb bitmaps have been used with horrible results.
I need to fix this in a couple of ways. First by converting any image they use to an efficient format (size vs definition) and second by ensuring that the loaded image is suitably sized for the dimensions - at the moment I don't do anything specific to deal with the resizing of say a 2000 x 2000 image to fit a 500 x 500 area of the display.
The default 1:1 display of the application represents 1m per pixel. Once the user has resized the image to fit accurately would I be right in thinking that the best resoultion for the image would be to resample it to that size? I am aware that if the user zooms in way past 1:1 which they will probably do then the clarity of the image will fall.
My ignorance of handling images is complete. I have looked at some image manipulation libraries (ImageMagick and the free version of dotIamge) first for converting the input image to a standard one and second for resizing -resampling. The truth is that they do far more than I need.
Any pointer much appreciated.
Yes, resampling so that the bitmap doesn't constantly have to be rescaled for every paint should make a big difference. The default Graphics.InterpolationMode makes pretty images but is not cheap.
Another biggie is the pixel format of the bitmap. Format32bppPArgb is 10 times faster than any of the others on most video adapters.
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}
The ImageList has a method named "Draw":
imageList.Draw(graphics, bounds.X, bounds.Y, bounds.Width, bounds.Height, imgIndex);
I use this method to draw an image on a graphics object of a PrintDocument. When using the original image size (16 x 16 pixels), the image is drawn correct. If however, I change the bounds size, nothing is drawn. Even changing the size to 32 x 32 (double size) has no effect. Nothing is drawn. I need to change the drawn size because of the different dpi ... Where am I gong wrong ?
Edit: The solution seems to be simply to use the g.DrawImage method instead. Why imageList.Draw() doesn't draw is still a mistery to me ...
g.DrawImage(imageList.Images[imgIndex], bounds);
ImageList.Draw() is a bit unusual, it takes advantage of the built-in support that the native image list code inside of Windows has for rendering an image in the list. This is an optimization, it avoids the cost of converting the internal image as stored in the native image list back to a managed Image object.
One side-effect however is that this drawing happens without regard for any of the transforms that were applied to the Graphics object. A 16x16 image in the list is going to be rendered as 16x16 pixels on paper. Which is indeed a bit hard to find back, printers have very high resolution (600 dots per inch is typical), that image turns into a decimal point.
Image lists were really meant to be the source of images for the TreeView and ListView controls, it is not a good general purpose collection object for images. Like a List<Image>. Your workaround is good, the Image property converts the internal bitmap back to a managed Image, Graphics.DrawImage() will then scale it appropriately to get a size on paper that's close to the size on the screen. However with the graininess you get from making an image 6 times larger. Note that you should Dispose() that object.
Few days ago, I asked a question about how to reduce the size of an image while keeping its ratio's dimensions. I was finally able to get it work. Now when the user uploads an image, 3 copies of it (with different dimensions) are saved to the database.
But, unfortunately, that dimensions' reduction degrades sensibly the quality of images uploaded. Only the copy that has been saved without dimensions has kept its quality.
Am I supposed to expect those reduction of quality? the image look really bad (like a news paper's photo).
Is there anything I can do? mighty an option I need to set up in my code.
Thanks for helping
The reason is because the Image.GetThumbnailImage function that you are using is only intended to make low quality images.
See these remarks from the MSDN documentation on GetThumbnailImage:
The GetThumbnailImage method works
well when the requested thumbnail
image has a size of about 120 x 120
pixels. If you request a large
thumbnail image (for example, 300 x
300) from an Image that has an
embedded thumbnail, there could be a
noticeable loss of quality in the
thumbnail image. It might be better to
scale the main image (instead of
scaling the embedded thumbnail) by
calling the DrawImage method.
For a good example of how to perform high quality image scaling in C# see this question:
High Quality Image Scaling C#
I'm not quite sure how the code you linked to works.
Here's a better example.
Resizing a Photographic image with GDI+ for .NET
The essence of it:
Bitmap bmPhoto = new Bitmap(destWidth, destHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX,destY,destWidth,destHeight),
new Rectangle(sourceX,sourceY,sourceWidth,sourceHeight),
GraphicsUnit.Pixel);
Look at the settings like InterpolationMode which allows you to control the quality (processing speed vs. quality).