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);
}
Related
Whilst working on a functionality for viewing embedded images that get fetched from one of our servers, one of the requirements is to make an overview page, where all images are displayed, in a fasion like the iOS Native Photos App (square tiles, in rows of 4/5 and when tapped, displays the image screen-wide.
This has been successfully implemented, except that the images were morphed (image aspects were never taken into account). Thus, they have asked me to devise a method to crop the image, so the original aspect stays unchanged.
In order to do this, I was thinking of first resizing the image until the width reaches the max width I want it to be ((screenwidth - 10) / 4 - 5).
If the height of the image is smaller than the width, I set the height to be equal to the width, and crop the width accordingly (with aspect unchanged).
If the height of the image is greater than the width, I just want to crop the height.
This is the method I am currently using:
//Images is an array/List with base64 strings
foreach (var image in Images)
{
UIImage imageToAdd = UIImage.LoadFromData(NSData.FromArray(Convert.FromBase64String(image.Content)));
var width = imageToAdd.Size.Width;
var height = imageToAdd.Size.Height;
//GlobalSupport.ScreenWidth is a static variable that contains the actual screensize
var newWidth = (GlobalSupport.ScreenWidth - 10) / 4 - 5;
var newHeight = height * newWidth / width;
var widthToCrop = 0.0f;
var heightToCrop = 0.0f;
//If the new height is smaller than the new width, make the new height equal to the new width, and modify the new width accordingly.
if (newHeight < newWidth)
{
newHeight = newWidth;
newWidth = width * newHeight / height;
widthToCrop = newWidth - newHeight;
}
//Or, if the new height is greater than the new width, just crop the height.
else if (newHeight > newWidth)
{
heightToCrop = newHeight - newWidth;
}
UIGraphics.BeginImageContext(new SizeF(newWidth, newHeight));
var cropRectangle = new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight);
imageToAdd.Draw(cropRectangle);
imageToAdd = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
UIButton btnToAdd = new UIButton(new RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight));
btnToAdd.SetBackgroundImage(imageToAdd, UIControlState.Normal);
btnToAdd.TouchUpInside += (sender, e) =>
{
Task.Factory.StartNew(() =>
{
//Displays a loading spinner
GlobalSupport.EnableLoadingOverlay(true, NSBundle.MainBundle.LocalizedString("txtOneMoment", "", ""));
}).ContinueWith(task => InvokeOnMainThread(() =>
{
//Navigate to the full-screen image viewer
this.NavigationController.PushViewController(
new VCDCMPhotoDetails(
UIImage.LoadFromData(
NSData.FromArray(
Convert.FromBase64String(image.Content)))), true);
GlobalSupport.EnableLoadingOverlay(false, "");
}));
};
//Add btn to the scrollable view's children
scrollView.AddSubview(btnToAdd);
}
This method works great, except for one thing: if the new height is smaller than the new width, the new height and new width successfully get modified, but after the ImageToAdd.Draw(RectangleF) the Size of the ImageToAdd still has the new width value, instead of the cropped width (that is, if I can assume that, if I draw an image with -20 as its x-value, the width gets modified with -20 as well).
I don't know if this is the right way to do it. If it isn't, then all help is welcome! If this is the right way to do it, but I'm missing something, please let me know! Thanks in advance!
When cropping the image, the x-coordinate is equal to the width of the image, minus the max width of a thumbnail: http://i.stack.imgur.com/uNm7k.png
After cropping, the image still had the original new width, instead of the cropped width: http://i.stack.imgur.com/rabeY.png
The resulting grid of images: http://i.stack.imgur.com/opo2f.jpg
Let me just show you a proper procedure on how to crop the image with a FILL operation. The code is in Swift but you should have no trouble porting it:
public static func resampleImageToSize(image: UIImage!, size: CGSize) -> UIImage {
let originalWidth = image.size.width
let originalHeight = image.size.height
let originalRatio = originalWidth/originalHeight
let targetRatio = size.width/size.height
var targetFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
if originalRatio>targetRatio {
// crop width
let targetHeight = size.height
let targetWidth = targetHeight * originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
} else if originalRatio<targetRatio {
// crop height
let targetWidth = size.width
let targetHeight = targetWidth / originalRatio
targetFrame = CGRect(x: (size.width-targetWidth)*0.5, y: (size.height-targetHeight)*0.5, width: targetWidth, height: targetHeight)
}
UIGraphicsBeginImageContext(size)
image.drawInRect(targetFrame)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
So this is a general method that takes in an original image and returns an image with a size of your choice. In your case that is the size of the icons you want to display. The operation here is fill, if you need to fit the image all you need to do is swap < and > in both if statements.
What you are doing is easiest to achieve with creating an image view with the size of your target image size. Then set your image to it and set the content mode to scale aspect fill and simply create a screenshot of the view (you can find very easy solutions for that on the web).
From your question I can not see if you have an issue at all or what it is. But one thing is bothering me. The line
RectangleF(-widthToCrop, -heightToCrop, newWidth, newHeight)
Will crop it the way you will always get the bottom right part of the image. You should most likely center it:
RectangleF(-widthToCrop*0.5, -heightToCrop*0.5, newWidth, newHeight)
Ok I think I found your problem. The frame you compute is the frame on which you should draw the image to put it on the square. But when you begin image context you should set the size to be a square. So if the width is larger the size should be (newHeight, newHeight) otherwise (newWidth, newWidth). The button frame makes no sense to me either. Why are you using the rectangle that should be used only to redraw the image?
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>
I have a Picturebox and a ton of Bitmaps that can be displayed in it. The relative size of the Bitmap when compared to the others is of importance to the user. They need to be able to see that one image is smaller or bigger than another. The Bitmap must also fit in the picturebox entirely and the picturebox cannot be resized.
When simply displaying the Bitmaps unscaled in a huge picturebox the relative sizes of the bitmaps is easy to see, but when trying to fit them in a small box and having to scale them down my problem starts.
When using the Stretch PictureBoxSizeMode as you would imagine the images sometimes appear distorted due to the nonspecific sizes of the Bitmaps and the fact they then get stretched to fill the whole box regardless, but the Stretch sizemod is the closest to the kind I need.
None of the other sizemodes suit my needs so I know now I need to create a function to resize the Bitmap and here was the start of my attempt until I realized I was going in completely the wrong direction, the image returned here retains no 'scale'.
private Bitmap ResizeBitmap(Bitmap img)
{
int newWidth = 0;
int newHeight = 0;
double imgRatio;
if (img.Width > img.Height)
{
imgRatio = ((double)img.Height / (double)img.Width) * 100;
newWidth = pictureBox.Width;
newHeight = (int)(((double)newWidth / 100) * imgRatio);
}
else
{
imgRatio = ((double)img.Width / (double)img.Height) * 100;
newHeight = pictureBox.Height;
newWidth = (int)(((double)newHeight / 100) * imgRatio);
}
Bitmap newImg = new Bitmap(newWidth, newHeight);
using (Graphics g = Graphics.FromImage(newImg))
g.DrawImage(img, 0, 0, newWidth, newHeight);
return newImg;
}
I've been staring at the screen for a while now and the math to do the scaling currently eludes me, I'm hoping someone can point me in the right direction. It's almost 4am so maybe my brain just isn't grasping some simple concepts.
Set the PictureBoxSizeMode to Zoom. This maintains the aspect ratio.
I am using the following code in my website, for thumbnail creation:
string furl = "~/images/thumbs/" + matchString;
lBlogThumb.ImageUrl = GetThumbnailView(furl, 200, 200);
private string GetThumbnailView(string originalImagePath, int height, int width)
{
//Consider Image is stored at path like "ProductImage\\Product1.jpg"
//Now we have created one another folder ProductThumbnail to store thumbnail image of product.
//So let name of image be same, just change the FolderName while storing image.
string thumbnailImagePath = originalImagePath.Replace("thumbs", "thumbs2");
//If thumbnail Image is not available, generate it.
if (!System.IO.File.Exists(Server.MapPath(thumbnailImagePath)))
{
System.Drawing.Image imThumbnailImage;
System.Drawing.Image OriginalImage = System.Drawing.Image.FromFile(Server.MapPath(originalImagePath));
imThumbnailImage = OriginalImage.GetThumbnailImage(width, height,
new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
imThumbnailImage.Save(Server.MapPath(thumbnailImagePath), System.Drawing.Imaging.ImageFormat.Jpeg);
imThumbnailImage.Dispose();
OriginalImage.Dispose();
}
return thumbnailImagePath;
}
public bool ThumbnailCallback() { return false; }
I would like to change this code, and be able to create a thumbnail defining width ONLY. What I have in mind is actually something like cropping/resizing image, using a static width, maintaining it's ratio. Is that possible;
You mention resizing and cropping. If you want the thumbnail heights to vary with a fixed width, the answers provided already will work for you.
The mention of cropping makes me think that you may want a fixed thumbnail size, with the width filled and any overflowing vertical portion cropped off. If that is the case, you'll need to do a bit more work. I needed something similar recently, and this is what I came up with.
This will create a thumbnail of the original that is sized and cropped in such a way that the source image completely fills the target thumbnail, cropping any overflow. There will be no borders within the thumbnail, even if the original and thumbnail aspect ratios are different.
public System.Drawing.Image CreateThumbnail(System.Drawing.Image image, Size thumbnailSize)
{
float scalingRatio = CalculateScalingRatio(image.Size, thumbnailSize);
int scaledWidth = (int)Math.Round((float)image.Size.Width * scalingRatio);
int scaledHeight = (int)Math.Round((float)image.Size.Height * scalingRatio);
int scaledLeft = (thumbnailSize.Width - scaledWidth) / 2;
int scaledTop = (thumbnailSize.Height - scaledHeight) / 2;
// For portrait mode, adjust the vertical top of the crop area so that we get more of the top area
if (scaledWidth < scaledHeight && scaledHeight > thumbnailSize.Height)
{
scaledTop = (thumbnailSize.Height - scaledHeight) / 4;
}
Rectangle cropArea = new Rectangle(scaledLeft, scaledTop, scaledWidth, scaledHeight);
System.Drawing.Image thumbnail = new Bitmap(thumbnailSize.Width, thumbnailSize.Height);
using (Graphics thumbnailGraphics = Graphics.FromImage(thumbnail))
{
thumbnailGraphics.CompositingQuality = CompositingQuality.HighQuality;
thumbnailGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
thumbnailGraphics.SmoothingMode = SmoothingMode.HighQuality;
thumbnailGraphics.DrawImage(image, cropArea);
}
return thumbnail;
}
private float CalculateScalingRatio(Size originalSize, Size targetSize)
{
float originalAspectRatio = (float)originalSize.Width / (float)originalSize.Height;
float targetAspectRatio = (float)targetSize.Width / (float)targetSize.Height;
float scalingRatio = 0;
if (targetAspectRatio >= originalAspectRatio)
{
scalingRatio = (float)targetSize.Width / (float)originalSize.Width;
}
else
{
scalingRatio = (float)targetSize.Height / (float)originalSize.Height;
}
return scalingRatio;
}
To use with your code, you could replace your call to OriginalImage.GetThumbnailImage with this:
imThumbnailImage = CreateThumbnail(OriginalImage, new Size(width, height));
Note that for portrait images, this code will actually shift the thumbnail's viewport slightly higher on the original image. This was done so that portrait shots of people wouldn't result in headless torsos when the thumbnails were created. If you don't want that logic, simply remove the if block following the "portrait mode" comment.
let's have originalWidth=the original image width and thumbWidth. You can simply choose the thumbWidth to your desired value, and calculate the thumbHeigth=originalHeigth*thumbWidth/originalWidth
I got sick of needed to do this and created a lib that does this easily: Link To Documentation & Download
A basic example without rounding with a thumbnail width of 140, below the 'file' is a HttpPostedFile uploaded from an ASP.Net FileUpload control, the HttpPostedFile exposes a stream.
// Save images to disk.
using (System.Drawing.Image image = System.Drawing.Image.FromStream(file.InputStream))
using (System.Drawing.Image thumbnailImage = image.GetThumbnailImage(140, Convert.ToInt32((image.Height / (image.Width / 140))), null, IntPtr.Zero))
{
if (image != null)
{
image.Save(imageFilePath);
thumbnailImage.Save(thumbnailImagePath);
}
else
throw new ArgumentNullException("Image stream is null");
}
In my site, users can upload photos. I currently compress and resize the photo to make sure they aren't huge files. But this creates photos that are of varying dimensions... which makes the site a bit "ugly" in my opinion.
I'd like to ensure the thumbnails are square images, but not by using padding. It's ok if there is some loss of the photo in the thumbnail. I'd like to keep the fidelity of the photo high, even if it means some cropping needs to occur.
I wrote some code to do this exact thing. I chose to crop it because resizing without preserving aspect ratio looks pretty horrible. I do a crop then a resize to create a thumnail image:
public Bitmap CreateThumbnail(Bitmap RawImage)
{
int width = RawImage.Width;
int height = RawImage.Height;
Size ThumbnailDimensions = new Size();
ThumbnailDimensions.Width = 100;
ThumbnailDimensions.Height = 100;
Rectangle cropArea = new Rectangle();
if (width > height)
{
cropArea.Width = height;
cropArea.Height = height;
cropArea.X = 0;
cropArea.Y = 0;
}
else if (width < height)
{
cropArea.Width = width;
cropArea.Height = width;
cropArea.X = 0;
cropArea.Y = 0;
}
if(width != height) Bitmap thumbnail = CropImage(RawImage, cropArea);
thumbnail = ResizeImage(thumbnail, ThumbnailDimensions);
return thumbnail;
}
This just crops from the top left corner then resizes it to my thumbnail dimensions.
I would imagine you need to take the shortest dimension (either w or h), and use that as your dimension for creating the cropped image, essentially you can crop and then scale the image. Check out this article as an example for cropping an image. Also check out this Stack Overflow question regarding image quality.
Rather than cropping, I would make the div or whatever you put them in a fixed square size. Scale the image to fit inside that square.
How would you decide to crop it? From the top-left? Bottom-right? Center?
To make a rectangle into a square you need to either pad, resize without preserving aspect ratio or crop (or combinations).
Here's some code for cropping
http://snippets.dzone.com/posts/show/1484
(I work for Atalasoft -- in our DotImage Photo SDK), it's
AtalaImage img = new AtalaImage("filename.jpg");
AtalaImage img2 = new CropCommand( /*point and size of crop */).Apply(img).Image;
img2.Save("filename", new JpegEncoder(quality), null);