How to resize the images without disturbing the aspect ratio - c#

I am working on Windows 8 Phone app, I have some images displaying in my app, the images which I have are very big with good quality, now in my app I need to resize the image without disturbing the aspect ratio.
I have searched for it and couldnt find a suitable soultion.
How to achieve this?
Here is my code in .CS file.
string imageName= "path to folder" + name + ".png";
BitmapImage bmp = new BitmapImage(new Uri(imageName, UriKind.Relative));
Image.Source = bmp;
EDIT
More info: Currently i am displaying images in my List Box, so the images are looking very big, so i want to decrease it to lower size without affecting the aspect ratio of the image.

If you want to load reduced image into memory then set DecodePixelWidth without setting
DecodePixelHeight (or other way round)
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.DecodePixelWidth = 80;
bitmapImage.UriSource = new Uri(imageName, UriKind.Relative);
EDIT
Or if you want to keep high resolution image in memory set size for Image control.
<Image ... Width="80"/>
Stretch property is set by default to Uniform which means:
The content is resized to fit in the destination dimensions while it preserves its native aspect ratio.

This should do:
static void Main(string[] args)
{
int _newWidth = 60; //the new width is set, the height will be calculated
var originalImage = Bitmap.FromFile(#"C:\temp\source.png");
float factor = originalImage.Width / (float)_newWidth;
int newHeight = (int)(originalImage.Height / factor);
Bitmap resizedImage = ResizeBitmap(originalImage, _newWidth, newHeight);
resizedImage.Save(#"c:\temp\target.png");
}
private static Bitmap ResizeBitmap(Image b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage(result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}

If you want to reduce image size proportionally on display, try to play with Image control's Stretch property as demonstrated and explained very well in this blog post.
<Image x:Name="Image" Stretch="UniformToFill"></Image>

The size of an image element is influenced by its containing panel, but this should work in any panel.
<Grid>
<Image Source='flower.png' Width='120' Stretch='Uniform'/>
</Grid>

Related

Image is not drawn at the correct spot

Bitmap image = ReadBitmap("image.png");
Bitmap imageCopy = new Bitmap(image);
Bitmap canvas = new Bitmap(imageCopy.Width+100, imageCopy.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(canvas))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(image, 0, 0);
}
// Use tempBitmap as you would have used originalBmp
InputPictureBox.Image = image;
OutputPictureBox.Image = canvas;
I haven't understood the output of this c# code.
The original image is not placed at the correct position. It should have been at (0, 0).
Also, I need a black background.
So, what is going on and how to correct this?
You are loading an Image, then a copy of this source is created using:
Bitmap bitmap = new Bitmap();
When you create a copy of an Image this way, you sacrifice/alter some details:
Dpi Resolution: if not otherwise specified, the resolution is set to the UI resolution. 96 Dpi, as a standard; it might be different with different screen resolutions and scaling. The System in use also affects this value (Windows 7 and Windows 10 will probably/possibly provide different values)
PixelFormat: If not directly copied from the Image source or explicitly specified, the PixelFormat is set to PixelFormat.Format32bppArgb.
From what you were saying, you probably wanted something like this:
var imageSource = Image.FromStream(new MemoryStream(File.ReadAllBytes(#"[SomeImageOfLena]"))), true, false)
var imageCopy = new Bitmap(imageSource.Width + 100, imageSource.Height, imageSource.PixelFormat))
imageCopy.SetResolution(imageSource.HorizontalResolution, imageSource.VerticalResolution);
using (var g = Graphics.FromImage(imageCopy)) {
g.Clear(Color.Black);
g.CompositingMode = CompositingMode.SourceCopy;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imageSource, (imageCopy.Width - imageSource.Width) / 2, 0);
pictureBox1.Image?.Dispose();
pictureBox2.Image?.Dispose();
pictureBox1.Image = imageSource;
pictureBox2.Image = imageCopy;
}
This is the result:
(The upper/lower frame black color is actually the Picturebox background color)
When the original Image Dpi Resolution is different from the base Dpi Resolution used when creating an Image copy with new Bitmap(), your results may be different from what is expected.
This is what happens with a source Image of 150, 96 and 72 Dpi in the same scenario:
Another important detail is the IDisposable nature of the Image object.
When you create one, you have to Dispose() of it; explicitly, calling the Dispose method, or implicitly, enclosing the Image contructor in a Using statement.
Also, possibly, don't assign an Image object directly loaded from a FileStream.
GDI+ will lock the file, and you will not be able to copy, move or delete it.
With the file, all resources tied to the Images will also be locked.
Make a copy with new Bitmap() (if you don't care of the above mentioned details), or with Image.Clone(), which will preserve the Image Dpi Resolution and PixelFormat.
I am not completely clear on what you are actually needing to do. But anyway, here is a WPF-friendly example of how to draw an image at a specific position inside another image.
Note if all you want to do is display the image in different size and/or put a black border around it, there are much simpler ways to do simply that, without having to create a second image, such as just laying out the image inside a panel that already has the border style you want.
Notice that I am using classes from the System.Windows.Media namespace because that is what WPF uses. These don't mix easily with the older classes from System.Drawing namespace (some of the class names conflict, and Microsoft's .Net framework lacks built-in methods for converting objects between those types), so normally one needs to simply decide whether to use one or the other sets of drawing tools. I assume you have been trying to use System.Drawing. Each has its own pros and cons that would take too long to explain here.
// using System.Windows.Media;
// using System.Windows.Media.Imaging;
private void DrawTwoImages()
{
// For InputPictureBox
var file = new Uri("C:\\image.png");
var inputImage = new BitmapImage(file);
// If your image is stored in a Resource Dictionary, instead use:
// var inputImage = (BitmapImage) Resources["image.png"];
InputPicture.Source = inputImage;
// imageCopy isn't actually needed for this example.
// But since you had it in yours, here is how it's done, anyway.
var imageCopy = inputImage.Clone();
// Parameters for setting up our output picture
int leftMargin = 50;
int topMargin = 5;
int rightMargin = 50;
int bottomMargin = 5;
int width = inputImage.PixelWidth + leftMargin + rightMargin;
int height = inputImage.PixelHeight + topMargin + bottomMargin;
var backgroundColor = Brushes.Black;
var borderColor = (Pen) null;
// Use a DrawingVisual and DrawingContext for drawing
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
// Draw the black background
dc.DrawRectangle(backgroundColor, borderColor, new Rect(0, 0, width, height));
// Copy input image onto output image at desired position
dc.DrawImage(inputImage, new Rect(leftMargin, topMargin,
inputImage.PixelWidth, inputImage.PixelHeight));
}
// For displaying output image
var rtb = new RenderTargetBitmap( width, height, 96, 96, PixelFormats.Pbgra32 );
rtb.Render(dv);
OutputPicture.Source = rtb;
}

How to keep aspect ratio in panel C#?

I save an image in preview mode. Preview mode contains Picturebox and Label control.
The problem is when I save an image. Likely I recorded my screen.
The export image is different with my expect, it does not keep aspect ratio.
So, all control in panel after save to example.jpg will wrong position.
My code use to ScaleImage:
public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double) maxWidth/image.Width;
var ratioY = (double) maxHeight/image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int) (image.Width*ratio);
var newHeight = (int) (image.Height*ratio);
var newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
My code save image:
Graphics g = Graphics.FromImage(img);
string s = lstImgAdded.Items[k].Text;
Bitmap bm = new Bitmap(#"" + s);
panel2.BackgroundImage = bm;
PointF p1 = StretchImageSize(postPoint, panel2);
g.DrawImage(
DrawText(lstAliasImage[i - valuesFrom], fontType, colorInput,
Color.Transparent),
Point.Round(StretchImageSize(postPoint, panel2))); // Point.Round(StretchImageSize(postPoint, panel2)) ở đây dùng nhìu lần
g.DrawImage(ctrl.Image, Point.Round(StretchImageSize(postPointPicturebox, panel2)).X, Point.Round(StretchImageSize(postPointPicturebox, panel2)).Y,
ctrl.Width, ctrl.Height); // panel2 có phải cái hình nhỏ ko? ko. picturebox moi la nho, panel la background lon
g.Dispose();
string linkLocation = txtAddress.Text;
ScaleImage(img, witdhImg, heightImg)
.Save(linkLocation + "\\" + lstAliasImage[i - valuesFrom] + "." + imgType,
ImageFormat.Jpeg);
And class StretchImage to scale control(Picturebox, Label) with a panel.
Image show before image in a panel is different after save.
In my software, Image shown in the panel. It does not scale like in Preview software:
Applying Aspect Ratio to Windows Forms
Much like we intercepted Windows message in our Windows 7 Style Form, to keep the aspect ratio of a Windows Form we are going to intercept the resizing Windows messages. This is done by overriding WndProc.
Here is a list of the constants that we need for this algorithm:
WM_SIZING = 0x214
WMSZ_LEFT = 1
WMSZ_RIGHT = 2
WMSZ_TOP = 3
WMSZ_BOTTOM = 6
The first one is the way our C# application can tell when the Form
is being resized. The rest of the constants tell us exactly which
side of the Form is being resized. Note that for corners the edges
combine. So for example, the lower left corner would be resizing the
right and bottom parts of the form and thus be 2 + 6 = 8.
The advantage of using WndProc instead of Windows Form events is
that we can prevent the Form from flickering. Resize events in .NET
are called until after the resizing has been applied, so modifying
size in an event produces flickering.
Make sure to download the sample C# application. Notice how no
matter where you resize the Form from, the aspect ratio is
maintained. A quick peek at the source code shows how either the
height or width is adjusted depending on which side of the Form was
being resized. Another cool thing is that the Form can still be
maximized to fill the entire screen without any problems.
try this
public void ResizeImage(string OriginalFile, string NewFile, int NewWidth, int MaxHeight, bool OnlyResizeIfWider)
{
System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(OriginalFile);
// Prevent using images internal thumbnail
FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
FullsizeImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
if (OnlyResizeIfWider)
{
if (FullsizeImage.Width MaxHeight)
{
// Resize with height instead
NewWidth = FullsizeImage.Width * MaxHeight / FullsizeImage.Height;
NewHeight = MaxHeight;
}
System.Drawing.Image NewImage = FullsizeImage.GetThumbnailImage(NewWidth, NewHeight, null, IntPtr.Zero);
// Clear handle to original file so that we can overwrite it if necessary
FullsizeImage.Dispose();
// Save resized picture
NewImage.Save(NewFile);
}

Replicating WinRT Pixel Density changes in WPF possibly using DPI

I am using WPF to generate tile images for my WinRT app. I have a tile UserControl which is converted to PNG and this works well (It does, please don't tell me otherwise). WinRT passes me a scale property 100% (320x150), 140% (434x210) and 180% (558x270) which I use to generate the correct size of image.
For when I want to use images in my tile. I have replicated the image selection functionality of WinRT (You can supply thee scales of images and WinRT apps automatically select the correct scale) in my tile UserControl's code behind. So depending on the scale I select a bigger or smaller image source. However, on the larger scales my font size remains the same size and looks really small. I don't think I need to change the font size based on the scale as this is not what happens in WinRT, so it must be the code I'm using to convert my UserControl to a PNG and something to do with DPI. Here is my conversion Code:
// I pass in 320x150 or 434x210 or 558x270 depending on the scale.
public static MemoryStream ToPng(
this FrameworkElement frameworkElement,
double width,
double height)
{
BitmapSource bitmapSource = ToBitmapSource(frameworkElement, width, height);
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapSource));
MemoryStream memoryStream = new MemoryStream();
pngBitmapEncoder.Save(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
public static BitmapSource ToBitmapSource(
this FrameworkElement frameworkElement,
double width,
double height)
{
Size renderingSize = new Size(width, height);
frameworkElement.Measure(renderingSize);
Rect renderingRectangle = new Rect(new Point(0, 0), renderingSize);
frameworkElement.Arrange(renderingRectangle);
frameworkElement.UpdateLayout();
Rect bounds = VisualTreeHelper.GetDescendantBounds(frameworkElement);
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
(int)frameworkElement.ActualWidth,
(int)frameworkElement.ActualHeight,
96,
96,
PixelFormats.Pbgra32);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(frameworkElement);
drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
}
renderBitmap.Render(drawingVisual);
return renderBitmap;
}
Thanks for any help. Much appreciated.
I needed to change the above code so that I measure and arrange the frameworkElement as 310x150. I then render it to the final scale size e.g. 558x270 and set the DPI to (96/100)*scale where the scale is 180 in this case.

How to set Image Height and Width Properties of Bitmap

Pls. suggest how to set an image's width & Height Property while saving Images
If you want to save bitmap with some specific width and height (which differ from the Width and Height properties of the bitmap), you need to create a new Bitmap of corresponding size and draw the initial bitmap in needed size on it.
Like this:
using (Bitmap bmpToSave = new Bitmap(1000, 500)) {
using (Graphics g = Graphics.FromImage(bmpToSave)) {
g.DrawImage(bmp, 0, 0, 1000, 500);
}
bmpToSave.Save(#"bitmap.bmp");
}
Where bmp is your original bitmap.
Also, read up on some options that affect the quality of resampling if you need the high quality result.

List view control displaying distorted images

I have a problem with the ListView control in a windows forms application.
Even if I create a thumbnail image or resize the real one I get distorted images in the list view.
The image looks like when you zoom in an image very much.
I first thought that the GetThumbnailImage is couseing this but I used a resize code I found here and I have the same result.
I also did not found any bug related to list view control so I gues I'm doing something wrong but I just can't figure out what.
Here is the code I use:
lsvPictures.LargeImageList = m_imagesList;
lsvPictures.LargeImageList.ImageSize = new Size(100, 100);
lsvPictures.View = View.LargeIcon;
lsvPictures.CheckBoxes = true;
for (int i = 0; i < ofd.FileNames.Length; i++)
{
filename = ofd.FileNames[i].ToString();
ListViewItem lvi = new ListViewItem(filename);
m_imagesList.Images.Add(ResizeImage(Image.FromFile(filename), 100, 100));
lvi.ImageIndex = i;
lsvPictures.Items.Add(lvi);
}
And this is the function that resizes images:
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image,
int width, int height)
{
//a holder for the result
Bitmap result = new Bitmap(width, height);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
Thank you!
Mosu'
I just found the source of the problems:
m_imagesList.ColorDepth = ColorDepth.Depth16Bit;
It seams that, as default, the ColorDepth of the ImageList is 8 bit (or 4 bit, but my guess is 8). If I change this to at least 16 bit everything looks very nice.
To those with similar problems: I changed my Thumbnail method a lot before I realised that the ListView control is not using the color depth the images were having. I put the result of my method on a PictureBox control and saw that the function was working corectly. Atfer this I googled a lot ... and found that silly ColorDepth property.
How did you set the resolution for your image. Also, did what did you set the PixelFormat value to when you created the bitmap? I have a list of images loading into my list view that I am resizing similar to how you are and it is working fine without any distortion in the resulting thumbnail images that are created.
Here is a snippet from my resize method.
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
bitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Red);
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(image,
new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight),
new Rectangle(sourceX, sourceY, originalWidth, originalHeight),
GraphicsUnit.Pixel);
}
return bitmap;
I was also using a ListView in WinForms to display directories, and had the same problem. I suggest that you check the image file type: icon files (.ico) tend to end up distorted, so try to use an image file with the .png extension. This works for me:
ListView listView = new ListView();
ImageList imageList = new ImageList();
// add image to list:
imageList.Images.Add("image_key", image_path);
// give the listview the imagelist:
listView.SmallImageList = imageList;
// add item to listview:
listView.Items.Add("item_text", "image_key");

Categories