Link to Project: https://github.com/FFladenmuller/resize-bmp
Code works for a resize with a factor of 1. However, if trying a larger factor and I open the image, Photos says: "It looks like we don't support this file format".
I have not added in padding yet but I have only worked with images whose width is divisible by 4.
For loop to add BGR bytes to new Image:
for (int i = 54; i < oldBMP.Info.Count - 2; i += 3)
{
for(int j = 0; j < sizeMultiplier; j++)
{
newBMP.Info.Add(oldBMP.Info[i]);
newBMP.Info.Add(oldBMP.Info[i + 1]);
newBMP.Info.Add(oldBMP.Info[i + 2]);
}
}
First for loop to increment through BGR triples, second for loop to add each pixel sizeMultiplier amount of times.
Define the following method:
public static Bitmap ResizeImage(Bitmap image, Size size)
{
try
{
Bitmap result = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage((Image)result))
{
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(image, 0, 0, size.Width, size.Height);
}
return result;
}
catch
{
return image;
}
}
Then, within your code, whenever a resize is needed:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Size size = new Size(targetWidth, targetHeight);
Bitmap imageResized = ResizeImage(image, size);
An alternative (that has the drawback of reducing the output quality) is the following:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Bitmap imageResized = new Bitmap(image, targetWidth, targetHeight);
Related
I'm taking a screenshot of my form and then sending it to the printer. The image is too large, it goes off the sides of the page. I've been looking around for the past few hours to no avail. Can someone assist?
When I open the file itself it looks good in a print preview. If I then print from the preview its fine. But I wanted to do this with no user intervention.
public void SetupPrintHandler()
{
PrintDocument printDoc = new PrintDocument();
printDoc.PrintPage += new PrintPageEventHandler(OnPrintPage);
printDoc.DefaultPageSettings.Landscape = true;
printDoc.Print();
}
private void OnPrintPage(object sender, PrintPageEventArgs args)
{
using (Image image = Image.FromFile(#"C:/temp2.bmp"))
{
Graphics g = args.Graphics;
g.DrawImage(image, 0, 0);
}
}
private void printscreen()
{
ScreenCapture sc = new ScreenCapture();
Image img = sc.CaptureScreen();
sc.CaptureWindowToFile(this.Handle, "C:/temp2.bmp", ImageFormat.Bmp);
SetupPrintHandler();
}
So what I did just now as instead of the screen shot, for testing, I save what was in panel3 which is 2 pictureboxes. So i'm taking the size of panel3.
Bitmap bmp = new Bitmap(panel3.ClientSize.Width, panel3.ClientSize.Height);
panel3.DrawToBitmap(bmp, panel3.ClientRectangle);
bmp.Save(subPath + file + ".bmp");
Which again, looks great on a print preview and if I click print from the print preview it prints fine. But if I just send it straight to the printer it doesn't fit. So maybe its not a size issue but a setting I have to send to the printer when not using print preview?
Update
So I may have found the issue. When you un-check "Fit to frame" when printing a picture it fits perfectly. However there doesn't seem to be an option when sending directly to the printer the way I am where I can disable "Fit to frame"
If you want to resize and keep the aspect of the image I do the following
public Stream ResizeImage(Stream stream, ImageFormat imageFormat, int width, int height)
{
var originalImage = Image.FromStream(stream);
var sourceWidth = originalImage.Width;
var sourceHeight = originalImage.Height;
const int sourceX = 0;
const int sourceY = 0;
var destX = 0;
var destY = 0;
float nPercent;
var nPercentW = ((float)width / sourceWidth);
var nPercentH = ((float)height / sourceHeight);
if (nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = Convert.ToInt16((width - (sourceWidth * nPercent)) / 2);
}
else
{
nPercent = nPercentW;
destY = Convert.ToInt16((height - (sourceHeight * nPercent)) / 2);
}
var destWidth = (int)(sourceWidth * nPercent);
var destHeight = (int)(sourceHeight * nPercent);
// specify different formats based off type ( GIF and PNG need alpha channel - 32aRGB 8bit Red 8bit Green 8bit B 8bit Alpha)
Bitmap newImage;
if (imageFormat.Equals(ImageFormat.Png) || imageFormat.Equals(ImageFormat.Gif))
{
newImage = new Bitmap(width, height, PixelFormat.Format32bppArgb);
}
else
{
newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb);
}
newImage.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
var newGraphics = Graphics.FromImage(newImage);
// don't clear the buffer with white if we have transparency
if (!imageFormat.Equals(ImageFormat.Png) && !imageFormat.Equals(ImageFormat.Gif))
{
newGraphics.Clear(Color.White);
}
newGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
newGraphics.DrawImage(originalImage,
new Rectangle(destX, destY, destWidth, destHeight),
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
GraphicsUnit.Pixel);
newGraphics.Dispose();
originalImage.Dispose();
var memoryStream = new MemoryStream();
newImage.Save(memoryStream, imageFormat);
memoryStream.Position = 0;
return memoryStream;
}
Using this you can resize the image. You need to pick up a scaling size before print.
As example - rescale 'image' to 227x171 pixels.
image = new Bitmap(image, new Size(227, 171));
I am facing an interesting issue when re-sizing images of average dimension 1200x700. I am using following method to do the same. I see original image size is 210KB and output image size is 255KB.
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = imageFile.Width;
int originalHeight = imageFile.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.CompositingQuality = CompositingQuality.HighQuality;
graphicsHandle.SmoothingMode = SmoothingMode.HighQuality;
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(imageFile, 0, 0, newWidth, newHeight);
}
return newImage;
}
To call above method I'm using following code:
WebClient wc = new WebClient();
try
{
using (var image = Image.FromStream(new MemoryStream(wc.DownloadData(remoteFileLocation))))
{
Image resized = ResizeImage(image, new Size(660, 660));
resized.Save(saveFileLocation);
}
}
catch(Exception)
{
//todo
}
I tried different InterpolationMode and CompositingQuality but result is always same. Is there anything I'm missing to reduce image dimension and size? I mean, I'm expecting if dimension is decreased image size should also decrease.
I'm using the following system drawing code to resize images on Upload. The problem is that landscape or portrait images get distorted cause the sytem drawing is making them square. Is it possible to resize the width only and keep the height proportional? and How? Thanks
HttpPostedFile imageFile = UploadImages.PostedFile;
System.Drawing.Image ri = System.Drawing.Image.FromStream(imageFile.InputStream);
ri = ResizeBitmap((Bitmap) ri, 200, 200);
private Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
If what you want to do is create a new bitmap 200 pixels wide and with height scaled proportionately, you can do this:
private static int CalculateProportionalHeight(int oldWidth, int oldHeight, int newWidth)
{
if (oldWidth <= 0 || oldHeight <= 0 || newWidth <= 0)
// For safety.
return oldHeight;
double widthFactor = (double)newWidth / (double)oldWidth;
int newHeight = (int)Math.Round(widthFactor * (double)oldHeight);
if (newHeight < 1)
newHeight = 1; // just in case.
return newHeight;
}
private static Bitmap ResizeBitmap(Bitmap b, int nWidth)
{
int nHeight = CalculateProportionalHeight(b.Width, b.Height, nWidth);
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
Or are you looking to create a 200x200 bitmap with the old image scaled to fit inside, and letterboxed if necessary?
Update
If you are looking to create an image of a fixed 200x200 size, with the image scaled down proportionately to fit and letterboxed, this should do it:
static RectangleF PlaceInside(int oldWidth, int oldHeight, int newWidth, int newHeight)
{
if (oldWidth <= 0 || oldHeight <= 0 || newWidth <= 0 || newHeight <= 0)
return new RectangleF(oldWidth, oldHeight, newWidth, newHeight);
float widthFactor = (float)newWidth / (float)oldWidth;
float heightFactor = (float)newHeight / (float)oldHeight;
if (widthFactor < heightFactor)
{
// prefer width
float scaledHeight = widthFactor * oldHeight;
// new new RectangleF(x, y, width, height)
return new RectangleF(0, (newHeight - scaledHeight) / 2.0f, newWidth, scaledHeight);
}
else
{
// prefer height
float scaledWidth = heightFactor * oldWidth;
// new new RectangleF(x, y, width, height)
return new RectangleF((newWidth - scaledWidth) / 2.0f, 0, scaledWidth, newHeight);
}
}
private static Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
int oldWidth = b.Width;
int oldHeight = b.Height;
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
var box = PlaceInside(oldWidth, oldHeight, nWidth, nHeight);
g.DrawImage(b, box);
}
return result;
}
Update 2
And here's a version that creates an image of width 200 and proportional height if landscape and height 200 and proportional width if portrait:
private static Bitmap ResizeBitmapUpto(Bitmap b, int nWidth, int nHeight, System.Drawing.Drawing2D.InterpolationMode interpolationMode)
{
int oldWidth = b.Width;
int oldHeight = b.Height;
var box = PlaceInside(oldWidth, oldHeight, nWidth, nHeight);
int actualNewWidth = (int)Math.Max(Math.Round(box.Width), 1);
int actualNewHeight = (int)Math.Max(Math.Round(box.Height), 1);
Bitmap result = new Bitmap(actualNewWidth, actualNewHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.InterpolationMode = interpolationMode;
g.DrawImage(b, 0, 0, actualNewWidth, actualNewHeight);
}
return result;
}
I added an interpolationMode so you can experiment with different qualities as per Ksv3n's answer.
(hopefully) Last Update
Here's the test setup I used to validate the code. I was able to open, resize and save a variety of images successfully on my computer.
public static void TestResizeBitmapUpto(string file, string newFile)
{
try
{
using (var image = Bitmap.FromFile(file))
{
if (image == null)
return;
using (Bitmap b = new Bitmap(image))
{
using (var newBitmap = ResizeBitmapUpto(b, 200, 200, System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor))
{
newBitmap.Save(newFile);
}
}
}
}
catch (System.IO.FileNotFoundException e)
{
Debug.WriteLine(e.ToString());
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
What's missing in your code is :
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
Here is the method you can use to resize your image with keeeping it proportional :
private Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((System.Drawing.Image)result))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(b, 0, 0, nWidth, nHeight);
}
return result;
}
g.DrawImage will stretch the image to what you defined (in your case 200/200 which is a square).
You need to calculate the real values for nWidth and nHeight:
// original image (b.Width, b.Height)
double originalWidth = 200;
double originalHeight = 100;
// user defined wanted width
double wantedWidth = 200; // nWidth parameter to your method
double wantedHeight = 300; // nHeight parameter to your method
double ratioW = originalWidth / wantedWidth;
double ratioH = originalHeight / wantedHeight;
double ratio = Math.Max(ratioW, ratioH);
// rectangle proportional to the original that fits into the wanted
double destinationWidth = originalWidth / ratio; // what you pass to DrawImage as nWidth
double destinationHeight = originalHeight / ratio; // what you pass to DrawImage as nWidth
What it does is calculates the width and height ratio of the original and wanted image and takes the max of it. Uses that to divide the original values, which will make them fit perfectly inside the wanted rectangle.
This will draw the scaled image aligned top or left depending what is the orientation, since the resulting image will be equal or bigger then the wanted or original. To make it centered in the resulting image you would need to adjust left and top coordinates for DrawImage() by taking the difference in width/height and dividing by 2.
If the resulting image can be of different size then what the user specified (nWidth/nHeight) then you can simply initialize it with the destinationWidth/Height, and return that instead, without bothering for centering.
I have to display image in photo gallery # width=200 height=180, but while uploading images I have to resize it , but the problem is every image have different resolution. How can I resize the images with different resolution so that images remain intact.
Here is my code :
private void ResizeImage()
{
System.Drawing.Image ImageToUpload = System.Drawing.Image.FromStream(FileUpload1.PostedFile.InputStream);
byte[] image = null;
int h = ImageToUpload.Height;
int w = ImageToUpload.Width;
int r = int.Parse(ImageToUpload.VerticalResolution.ToString());
int NewWidth = 200;//constant
int NewHeight = 180;//constant
byte[] imagesize = FileUpload1.FileBytes;
System.Drawing.Bitmap BitMapImage = new System.Drawing.Bitmap(ImageToUpload, NewWidth, NewHeight);//this line gives horrible output
MemoryStream Memory = new MemoryStream();
BitMapImage.Save(Memory, System.Drawing.Imaging.ImageFormat.Jpeg);
Memory.Position = 0;
image = new byte[Memory.Length + 1];
Memory.Read(image, 0, image.Length);
}
if resolution is 96 and if I set maxwidth=200 then its height would be 150 then only the image looks small and accurate. Can't we resize image in desired way so that it looks exact?
The function will resize the image maintaining aspect ratio.
public static Image Resize(Image originalImage, int w, int h)
{
//Original Image attributes
int originalWidth = originalImage.Width;
int originalHeight = originalImage.Height;
// Figure out the ratio
double ratioX = (double)w / (double)originalWidth;
double ratioY = (double)h / (double)originalHeight;
// use whichever multiplier is smaller
double ratio = ratioX < ratioY ? ratioX : ratioY;
// now we can get the new height and width
int newHeight = Convert.ToInt32(originalHeight * ratio);
int newWidth = Convert.ToInt32(originalWidth * ratio);
Image thumbnail = new Bitmap(newWidth, newHeight);
Graphics graphic = Graphics.FromImage(thumbnail);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.Clear(Color.Transparent);
graphic.DrawImage(originalImage, 0, 0, newWidth, newHeight);
return thumbnail;
}
Usage
Image BitMapImage = Resize(ImageToUpload, NewWidth, NewHeight);
Here i keep height fixed to 180 to maintain aspect ratio. It will resize the image and save to disk. The return value is the percentage value which i use in 'background-size' css.
public float ResizePhoto(string filepath, string filename)
{
var path = Path.Combine(filepath, filename);
var newPath = Path.Combine(filepath, "sml_" + filename);
Image orgImage = Image.FromFile(path);
float fixedHt = 180f;
int destHeight, destWidth;
float reqScale;
if(orgImage.Height > fixedHt)
{
destHeight = (int)fixedHt;
destWidth = (int)(fixedHt / orgImage.Height * orgImage.Width);
reqScale = destWidth / destHeight * 100;
}
else
{
destHeight = orgImage.Height;
destWidth = orgImage.Width;
reqScale = fixedHt / destHeight * 100;
}
Bitmap bmp = new Bitmap(destWidth, destHeight);
bmp.SetResolution(orgImage.HorizontalResolution,orgImage.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmp);
grPhoto.DrawImage(orgImage,
new Rectangle(0, 0, destWidth, destHeight),
new Rectangle(0, 0, orgImage.Width, orgImage.Height),
GraphicsUnit.Pixel);
bmp.Save(newPath);
return reqScale;
}
How would you resize a JPEG image, to a fixed width whilst keeping aspect ratio? In a simple way, whilst preserving quality.
This will scale in the vertical axis only:
public static Image ResizeImageFixedWidth(Image imgToResize, int width)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = ((float)width / (float)sourceWidth);
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap b = new Bitmap(destWidth, destHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return (Image)b;
}
If you are reducing the width by 25 percent to a fixed value, you must reduce the height by 25 percent.
If you are increasing the width by 25 percent to a fixed value, you must increasing the height by 25 percent.
It's really straight forward.
Assuming there is a (double width) variable:
Image imgOriginal = Bitmap.FromFile(path);
double height = (imgOriginal.Height * width) / imgOriginal.Width;
Image imgnew = new Bitmap((int)width, (int)height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(imgnew);
g.DrawImage(imgOriginal, new Point[]{new Point(0,0), new Point(width, 0), new Point(0, height)}, new Rectangle(0,0,imgOriginal.Width, imgOriginal.Height), GraphicsUnit.Pixel);
In the end you´ll have a new image with widthxheight, then, you´ll need to flush the graphics e save the imgnew.
I think there are plenty of samples of this if you search for them. Here's the one I commonly use...
public static Stream ResizeGdi(Stream stream, System.Drawing.Size size)
{
Image image = Image.FromStream(stream);
int width = image.Width;
int height = image.Height;
int sourceX = 0, sourceY = 0, destX = 0, destY = 0;
float percent = 0, percentWidth = 0, percentHeight = 0;
percentWidth = ((float)size.Width / (float)width);
percentHeight = ((float)size.Height / (float)height);
int destW = 0;
int destH = 0;
if (percentHeight < percentWidth)
{
percent = percentHeight;
}
else
{
percent = percentWidth;
}
destW = (int)(width * percent);
destH = (int)(height * percent);
MemoryStream mStream = new MemoryStream();
if (destW == 0
&& destH == 0)
{
image.Save(mStream, System.Drawing.Imaging.ImageFormat.Jpeg);
return mStream;
}
using (Bitmap bitmap = new Bitmap(destW, destH, System.Drawing.Imaging.PixelFormat.Format48bppRgb))
{
using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
{
//graphics.Clear(Color.Red);
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.DrawImage(image,
new Rectangle(destX, destY, destW, destH),
new Rectangle(sourceX, sourceY, width, height),
GraphicsUnit.Pixel);
}
bitmap.Save(mStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
mStream.Position = 0;
return mStream as Stream;
}
Example of the calling code...
Stream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None);
resizedStream = ImageUtility.ResizeGdi(stream, new System.Drawing.Size(resizeWidth, resizeHeight));
A quick search on code project has found the following article. It allows for resizing of images which accepts a boolean to restrain the new image to keep the originals aspect ratio. I'm unsure of what the quality is like as no screenshots were provided. See the article here