This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
Resize an Image C#
How can I programmatically resize an image in C# so that it'll fit in a winforms control?
Scaling an image into a PictureBox:
class ImageHandling
{
public static Rectangle GetScaledRectangle(Image img, Rectangle thumbRect)
{
if (img.Width < thumbRect.Width && img.Height < thumbRect.Height)
return new Rectangle(thumbRect.X + ((thumbRect.Width - img.Width) / 2), thumbRect.Y + ((thumbRect.Height - img.Height) / 2), img.Width, img.Height);
int sourceWidth = img.Width;
int sourceHeight = img.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)thumbRect.Width / (float)sourceWidth);
nPercentH = ((float)thumbRect.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
if (destWidth.Equals(0))
destWidth = 1;
if (destHeight.Equals(0))
destHeight = 1;
Rectangle retRect = new Rectangle(thumbRect.X, thumbRect.Y, destWidth, destHeight);
if (retRect.Height < thumbRect.Height)
retRect.Y = retRect.Y + Convert.ToInt32(((float)thumbRect.Height - (float)retRect.Height) / (float)2);
if (retRect.Width < thumbRect.Width)
retRect.X = retRect.X + Convert.ToInt32(((float)thumbRect.Width - (float)retRect.Width) / (float)2);
return retRect;
}
public static Image GetResizedImage(Image img, Rectangle rect)
{
Bitmap b = new Bitmap(rect.Width, rect.Height);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(img, 0, 0, rect.Width, rect.Height);
g.Dispose();
try
{
return (Image)b.Clone();
}
finally
{
b.Dispose();
b = null;
g = null;
}
}
}
And how to use it:
Image img = Image.FromFile(imageFilePath);
Rectangle newRect = GetScaledRectangle(img, pictureBox1.ClientRectangle);
pictureBox1.Image = ImageHandling.GetResizedImage(img, newRect);
Sacle and center an ImageBox:
private void LoadAndResizeImage(string sImagepath, int iMaxHeight, int iMaxWidth, int iMinX, int iMinY)
{
int wdt = iMaxWidth;
int hgt = iMaxHeight;
int partialX = iMinX;
int partialY = iMinY;
pictureBox1.ImageLocation = sImagepath;
if ((pictureBox1.Image.Height > iMaxHeight) || (pictureBox1.Image.Width > iMaxWidth))
{
if ((pictureBox1.Image.Width / iMaxWidth) > (pictureBox1.Image.Height / iMaxHeight))
{
wdt = iMaxWidth;
double ratio = (double)pictureBox1.Image.Height / (double)pictureBox1.Image.Width;
hgt = (int)(iMaxWidth * ratio);
}
else
{
hgt = iMaxHeight;
double ratio = (double)pictureBox1.Image.Width / (double)pictureBox1.Image.Height;
wdt = (int)(iMaxHeight * ratio);
}
}
else
{
hgt = pictureBox1.Image.Height;
wdt = pictureBox1.Image.Width;
}
if (wdt < iMaxWidth)
{
partialX = (iMaxWidth - wdt) / 2;
partialX += iMinX;
}
else
{
partialX = iMinX;
}
if (hgt < iMaxHeight)
{
partialY = (iMaxHeight - hgt) / 2;
partialY += iMinY;
}
else
{
partialY = iMinY;
}
//Set size
pictureBox1.Height = hgt;
pictureBox1.Width = wdt;
pictureBox1.Left = partialX;
pictureBox1.Top = partialY;
}
Related
public Image resizeImage(int newWidth, int newHeight, string stPhotoPath)
{
Image imgPhoto = Image.FromFile(stPhotoPath);
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
if (sourceWidth < sourceHeight)
{
int buff = newWidth;
newWidth = newHeight;
newHeight = buff;
}
int sourceX = 0, sourceY = 0, destX = 0, destY = 0;
float nPercent = 0, nPercentW = 0, nPercentH = 0;
nPercentW = ((float)newWidth / (float)sourceWidth);
nPercentH = ((float)newHeight / (float)sourceHeight);
if (nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = System.Convert.ToInt16((newWidth -
(sourceWidth * nPercent)) / 2);
}
else
{
nPercent = nPercentW;
destY = System.Convert.ToInt16((newHeight -
(sourceHeight * nPercent)) / 2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap bmPhoto = new Bitmap(newWidth, newHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.Clear(Color.Black);
grPhoto.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX, destY, destWidth, destHeight),
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
imgPhoto.Dispose();
return bmPhoto;
}
I use such a code to scale down images, but there is a problem that after the images are scaled down, the remaining spaces remain black. Something that should definitely not happen. What is the solution? The background color must be white.
public static class ResizeImage
{
/// <summary>
/// Return new resized size to display the image
/// </summary>
/// <param name="srcrectanle">source rectangle of image or you can pass the
bitmap and set the size accrodingly</param>
/// <param name="initSize">initial size of the page to draw image</param>
/// <returns></returns>
public static SizeF getResizedRectangle(RectangleF srcrectanle, SizeF initSize)
{
float sw = srcrectanle.Width;
float sh = srcrectanle.Height;
float dw = initSize.Width;
float dh = initSize.Height;
float finalHeight, finalWidth;
float Sourceratio = sw / sh;
if (Sourceratio >= 1)
{
finalWidth = (int)dw;
float ratio = sw / dw;
finalHeight = (sh / ratio);
}
else
{
finalHeight = (int)dh;
float ratio = sh / dh;
finalWidth = (sw / ratio);
}
return new SizeF(finalHeight, finalHeight);
}
}
Use this function to resize your image. Hope you have the knowledge of source rectangle and destination rectangle.
I have this method in FileUtility Class to save my images.
public static void SaveImage(System.IO.Stream file, string savePath, Size size, bool enforceRatio, bool testReverse = false)
{
var image = Image.FromStream(file);
var imageEncoders = ImageCodecInfo.GetImageEncoders();
int enc = Path.GetExtension(savePath).ToLower().Contains("png") ? 4 : 1;
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 90L);
Size s = new Size(size.Width, size.Height);
if (testReverse)
{
if (image.Width <= image.Height)
{
if (size.Width <= size.Height)
{
s.Width = size.Width;
s.Height = size.Height;
}
else
{
s.Width = size.Height;
s.Height = size.Width;
}
}
else
{
if (size.Width <= size.Height)
{
s.Width = size.Height;
s.Height = size.Width;
}
else
{
s.Width = size.Width;
s.Height = size.Height;
}
}
}
var canvasWidth = s.Width;
var canvasHeight = s.Height;
var newImageWidth = s.Width;
var newImageHeight = s.Height;
var xPosition = 0;
var yPosition = 0;
if (enforceRatio)
{
var ratioX = s.Width / (double)image.Width;
var ratioY = s.Height / (double)image.Height;
var ratio = ratioX < ratioY ? ratioX : ratioY;
newImageHeight = (int)(image.Height * ratio);
newImageWidth = (int)(image.Width * ratio);
}
var thumbnail = new Bitmap(canvasWidth, canvasHeight);
var graphic = Graphics.FromImage(thumbnail);
if (enforceRatio)
{
graphic.Clear(Color.White);
}
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.DrawImage(image, xPosition, yPosition, newImageWidth, newImageHeight);
thumbnail.Save(savePath, imageEncoders[enc], encoderParameters);
}
It work well. But for some images files (not every file) I get
Argument Exception : Parameter is not valid
in first line var image = Image.FromStream(file);
I call method like this:
FileUtility.SaveImage(add2.IconSquare.InputStream, Server.MapPath(program.IconSquare), new System.Drawing.Size { Width = 250, Height = 250 }, false);
That add2.IconSquare is a HttpPostedFileBase
public HttpPostedFileBase IconSquare { get; set; }
The only thing is that add2 come from another action with TempData.
var add2 = TempData["Create_Step2"] as AddStep2;
I mean I take files from user in Action Create_Step2 and save them in TempData then I try to save them in Action Create_Step3. Can anyone tell me what I doing wrong?
My requirement is to resize an image if it is bigger than 800 X 600 pixels. I have to resize it to 800 X 600 resolution as per aspect ratio.
I am using below code to re-size it:
public string ResizeUserImage(string fullFileName, int maxHeight, int maxWidth, string newFileName)
{
string savepath = System.Web.HttpContext.Current.Server.MapPath("//InitiativeImage//");
try
{
using (Image originalImage = Image.FromFile(fullFileName))
{
int height = originalImage.Height;
int width = originalImage.Width;
int newHeight = maxHeight;
int newWidth = maxWidth;
if (height > maxHeight || width > maxWidth)
{
if (height > maxHeight)
{
newHeight = maxHeight;
float temp = ((float)width / (float)height) * (float)maxHeight;
newWidth = Convert.ToInt32(temp);
height = newHeight;
width = newWidth;
}
if (width > maxWidth)
{
newWidth = maxWidth;
float temp = ((float)height / (float)width) * (float)maxWidth;
newHeight = Convert.ToInt32(temp);
}
Image.GetThumbnailImageAbort abort = new Image.GetThumbnailImageAbort(ThumbnailCallback);
using (Image resizedImage = originalImage.GetThumbnailImage(newWidth, newHeight, abort, System.IntPtr.Zero))
{
//When image is compress then store the image
var guid = Guid.NewGuid().ToString();
string latestFileName = guid + Path.GetExtension(newFileName);
resizedImage.Save(savepath + #"\" + latestFileName);
string finalPath = AppSettings.Domain + "InitiativeImage/" + latestFileName;
return finalPath;
}
}
else if (fullFileName != newFileName)
{
//var guid = Guid.NewGuid().ToString();
//newFileName = guid + Path.GetExtension(newFileName);
//// no resizing necessary, but need to create new file
//originalImage.Save(savepath + #"\" + newFileName);
//string finalPath = AppSettings.Domain + "UserImage/" + newFileName;
return newFileName;
}
return fullFileName;
}
}
catch (Exception)
{
throw;
}
}
It works perfect but resizing image gets damaged in pixel.
See below original image which I am uploading to the server:
After re-sizing it, it is damages like below image:
Hope you got my question. The resized image get damage in pixel. Please let me know where I am wrong or where is the issue.
Pls try out below function.
public void FixedSize(string oldImageFile, int Width, int Height,string finalpath)
{
Bitmap imgPhoto = new Bitmap(oldImageFile);
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)Width / (float)sourceWidth);
nPercentH = ((float)Height / (float)sourceHeight);
if (nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = System.Convert.ToInt16((Width -
(sourceWidth * nPercent)) / 2);
}
else
{
nPercent = nPercentW;
destY = System.Convert.ToInt16((Height -
(sourceHeight * nPercent)) / 2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap bmPhoto = new Bitmap(destWidth, destHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.Clear(Color.White);
grPhoto.InterpolationMode =
InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(0, 0, destWidth, destHeight),
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
bmPhoto.Save(finalpath);
}
From the documentation on Image.GetThumbnailImage method:
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.
But your objective size is around 800x600 pixels, which is way above the indented use.
Thus, I recommend you to take a look at this answer about image resizing.
I'm trying to resize user uploaded images to a landscape dimensions e.g. 450w and 250h while maintaining aspect ratio, but to avoid resized images like portrait images having gaps on the side I would like to crop the centre of the image to fill the resized dimensions.
I have found plenty of code to resize images while maintaining aspect ratio but not what I am looking for above, I'm hoping someone can help.
You should pass needToFill = true:
public static System.Drawing.Image FixedSize(Image image, int Width, int Height, bool needToFill)
{
#region calculations
int sourceWidth = image.Width;
int sourceHeight = image.Height;
int sourceX = 0;
int sourceY = 0;
double destX = 0;
double destY = 0;
double nScale = 0;
double nScaleW = 0;
double nScaleH = 0;
nScaleW = ((double)Width / (double)sourceWidth);
nScaleH = ((double)Height / (double)sourceHeight);
if (!needToFill)
{
nScale = Math.Min(nScaleH, nScaleW);
}
else
{
nScale = Math.Max(nScaleH, nScaleW);
destY = (Height - sourceHeight * nScale) / 2;
destX = (Width - sourceWidth * nScale) / 2;
}
if (nScale > 1)
nScale = 1;
int destWidth = (int)Math.Round(sourceWidth * nScale);
int destHeight = (int)Math.Round(sourceHeight * nScale);
#endregion
System.Drawing.Bitmap bmPhoto = null;
try
{
bmPhoto = new System.Drawing.Bitmap(destWidth + (int)Math.Round(2 * destX), destHeight + (int)Math.Round(2 * destY));
}
catch (Exception ex)
{
throw new ApplicationException(string.Format("destWidth:{0}, destX:{1}, destHeight:{2}, desxtY:{3}, Width:{4}, Height:{5}",
destWidth, destX, destHeight, destY, Width, Height), ex);
}
using (System.Drawing.Graphics grPhoto = System.Drawing.Graphics.FromImage(bmPhoto))
{
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.CompositingQuality = CompositingQuality.HighQuality;
grPhoto.SmoothingMode = SmoothingMode.HighQuality;
Rectangle to = new System.Drawing.Rectangle((int)Math.Round(destX), (int)Math.Round(destY), destWidth, destHeight);
Rectangle from = new System.Drawing.Rectangle(sourceX, sourceY, sourceWidth, sourceHeight);
//Console.WriteLine("From: " + from.ToString());
//Console.WriteLine("To: " + to.ToString());
grPhoto.DrawImage(image, to, from, System.Drawing.GraphicsUnit.Pixel);
return bmPhoto;
}
}
I have hit a brick wall with this... I know my flaw is in the logic somehow but I'll explain where I'm at.
I'm (trying) to write code to dynamically create a square (75x75) pixel thumbnail for a navigation bar along the bottom of my Silverlight app.
When I debug my code below I keep getting an "Array index out of bounds" error during the scaling nested loops
(I've only debugged with srcWidth > srcHeight, one step at a time)
The source image I'm testing with is 307x162 (49734 pixels)
The dest size (per current logic) for this is 150x75 (11250 pixels)
Of course after this then I intend to crop the result down to 75x75
It's only getting about half through the source image before hitting 11251 as the index of the destination.
I know it's my logic,
I just don't know if I went wrong in the sizes I used to construct the destination,
or in the approximations used from casting the floats to ints,
or maybe it's neither
I don't know, which is why I'm posting here... anyhow, here's my source:
private void ResizeNavBarImage(Image src)
{
const int SizeOfRGB = 4;
WriteableBitmap bmpSource = new WriteableBitmap((BitmapSource)src.Source);
int[] iSourcePixels = bmpSource.Pixels;
int srcWidth = bmpSource.PixelWidth;
int srcHeight = bmpSource.PixelHeight;
int destWidth = 75;
int destHeight = 75;
float xFactor = srcWidth / destWidth;
float yFactor = srcHeight / destHeight;
float xSource, ySource;
int iApprox;
int iIndex = 0;
if (srcWidth > srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap((int)(destWidth * yFactor), destHeight);
int[] iDestPixels = bmpDest.Pixels;
// Resize srcHeight to destHeight, srcWidth to same ratio
for (int i = 0; i < srcHeight; i++)
{
for (int j = 0; j < srcWidth; j++)
{
xSource = j * yFactor;
ySource = i * yFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
// Crop half of difference from each side of the width
srcWidth = bmpDest.PixelWidth;
srcHeight = bmpDest.PixelHeight;
int xLeftOffset = (srcWidth - srcHeight) / 2;
WriteableBitmap bmpFinalDest = new WriteableBitmap(destWidth, destHeight);
for (int iPixelRow = 0; iPixelRow < destHeight; iPixelRow++)
{
int srcOffset = (iPixelRow * srcWidth + xLeftOffset) * SizeOfRGB;
int destOffset = iPixelRow * destWidth * SizeOfRGB;
Buffer.BlockCopy(bmpDest.Pixels, srcOffset, bmpFinalDest.Pixels, destOffset, destWidth * SizeOfRGB);
}
src.Source = (ImageSource)bmpFinalDest;
}
else if (srcWidth < srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap(destWidth, (int)(destHeight * xFactor));
int[] iDestPixels = bmpDest.Pixels;
// Resize srcWidth to destWidth, srcHeight to same ratio
for (int i = 0; i < srcHeight; i++)
{
for (int j = 0; j < srcWidth; j++)
{
xSource = j * xFactor;
ySource = i * xFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
// Crop half of difference from each side of the height
srcWidth = bmpDest.PixelWidth;
srcHeight = bmpDest.PixelHeight;
int yTopOffset = (srcHeight - srcWidth) / 2;
WriteableBitmap bmpFinalDest = new WriteableBitmap(destWidth, destHeight);
for (int iPixelRow = yTopOffset; iPixelRow < (destHeight - (yTopOffset * 2)); iPixelRow++)
{
int srcOffset = iPixelRow * srcWidth * SizeOfRGB;
int destOffset = iPixelRow * destWidth * SizeOfRGB;
Buffer.BlockCopy(bmpDest.Pixels, srcOffset, bmpFinalDest.Pixels, destOffset, destWidth * SizeOfRGB);
}
src.Source = (ImageSource)bmpFinalDest;
}
else // (srcWidth == srcHeight)
{
WriteableBitmap bmpDest = new WriteableBitmap(destWidth, destHeight);
int[] iDestPixels = bmpDest.Pixels;
// Resize and set source
for (var i = 0; i < srcHeight; i++)
{
for (var j = 0; j < srcWidth; j++)
{
xSource = j * xFactor;
ySource = i * yFactor;
iApprox = (int)(ySource * srcWidth + xSource);
iDestPixels[iIndex++] = iSourcePixels[iApprox];
}
}
src.Source = (ImageSource)bmpDest;
}
}
===============================================================================
Here's my working code (with WriteableBitmapEx) for posterity:
private void ResizeNavBarImage(Image src)
{
WriteableBitmap bmpSource = new WriteableBitmap((BitmapSource)src.Source);
int srcWidth = bmpSource.PixelWidth;
int srcHeight = bmpSource.PixelHeight;
int finalDestWidth = 75;
int finalDestHeight = 75;
// Resize
float xFactor = ((float)finalDestWidth / (float)srcWidth);
float yFactor = ((float)finalDestHeight / (float)srcHeight);
float Factor = 0;
if (xFactor < yFactor)
Factor = yFactor;
else
Factor = xFactor;
int destWidth = (int)(srcWidth * Factor);
int destHeight = (int)(srcHeight * Factor);
if (destWidth < destHeight && destWidth != finalDestWidth)
destWidth = finalDestWidth;
else if (destWidth > destHeight && destHeight != finalDestHeight)
destHeight = finalDestHeight;
WriteableBitmap bmpDest = bmpSource.Resize(destWidth, destHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
// Crop
int Offset;
WriteableBitmap bmpFinalDest = new WriteableBitmap(finalDestWidth, finalDestHeight);
if (destWidth > destHeight)
{
Offset = (bmpDest.PixelWidth - bmpDest.PixelHeight) / 2;
if (finalDestWidth % 2 != 0 && Offset % 2 == 0)
Offset -= 1;
bmpFinalDest = bmpDest.Crop(Offset, 0, finalDestWidth, finalDestHeight);
}
else if (destWidth < destHeight)
{
Offset = (bmpDest.PixelHeight - bmpDest.PixelWidth) / 2;
if (finalDestHeight % 2 != 0 && Offset % 2 == 0)
Offset -= 1;
bmpFinalDest = bmpDest.Crop(0, Offset, finalDestWidth, finalDestHeight);
}
else
bmpFinalDest = bmpDest;
src.Source = (ImageSource)bmpFinalDest;
}
Your loops for i and j are going to srcHeight and srcWidth instead of destHeight and destWidth.
That's probably not the last bug in this code either.
I agree with Mark Ransom, the correct way to resize the image would be to draw into another image using a Graphics object.
http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing
This will allow you to use different, better filters to get good image quality, not to mention speed.
Why not use the built-in scale functionality of WriteableBitmap?
WriteableBitmap wb = new WriteableBitmap(src, new ScaleTransform() { ScaleX = 0.25, ScaleY = 0.25 });
wb.Invalidate();
src.Source = wb;