I've asked question on proper scaling without rim on right and bottom. link
I've checked and size of bitmap is 32 by 32 pixels.
When i try to draw it on pictureBox with drawImage(Image, int, int) it draws it little bit bigger than size of bitmap like 36 by 36 or 40 by 40. Not sure.
But when i add width and height to drawImage it draws it 32 by 32. Is this supposed to happend.
Edit:
internal void draw(Graphics g, int x, int y)
{
int s = Game.scale;
Bitmap resized = (s > 1) ? ResizeImage(image, width*s, height*s) : image;
g.DrawImage(resized, x * width * s, y * height * s);
}
private static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Feature, not a bug. If you don't specify the target size then DrawImage() pays attention to the physical size of the image. The size it had when it was created, in inches. Something you can see back in the debugger, look at the bitmap's HorizontalResolution and VerticalResolution properties.
With the diagnostic that it is probably 120 dots-per-inch, programmers commonly run their video adapter at 125% today. So the drawn image becomes 1.25 * 32 = 40 pixels.
Keep in mind that this kind of rescaling tends to be important, such an image can easily turn into but a fleck of dust on an upscale 4K monitor. They are getting pretty affordable today. Whether you want this or not depends on how the rest of your UI rescales. Check out this post about dpiAwareness in a Winforms app.
Related
I've been using this method to resize uploaded .JPG images to a max width, but it's resulting in images being larger in kb than the source. What am I doing wrong? Is there something else I need to do when saving the new image?
I've tried all sorts of combinations of PixelFormat, e.g. PixelFormat.Format16bppRgb555
E.g: source image is a .JPG 1900w, trying to resize to 1200w...
- Source file is 563KB,
- resized file is 926KB or larger, even 1.9MB
public static void ResizeToMaxWidth(string fileName, int maxWidth)
{
Image image = Image.FromFile(fileName);
if (image.Width > maxWidth)
{
double ratio = ((double)image.Width / (double)image.Height);
int newHeight = (int)Math.Round(Double.Parse((maxWidth / ratio).ToString()));
Bitmap resizedImage = new Bitmap(maxWidth, newHeight);
Graphics graphics = Graphics.FromImage(resizedImage);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
Rectangle rectDestination = new Rectangle(0, 0, maxWidth, newHeight);
graphics.DrawImage(image, rectDestination, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
graphics.Dispose();
image.Dispose();
resizedImage.Save(fileName);
resizedImage.Dispose();
}
image.Dispose();
}
You need to specify that you want to save it as the jpeg format:
resizedImage.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
Otherwise it will default to saving as BMP / PNG (I can't remember which).
I'm trying to write a very fast method for creating thumbnails. It will crop and resize an image at the same time. The result image will always be square-like. So having that in mind we can now look at my code :) It is working (as far as I can see):
public static void CreateAndSaveThumbnail(string path, string output, int desiredSize)
{
using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(path)))
using (Bitmap src = new Bitmap(ms))
{
Rectangle crop = src.Width > src.Height ?
new Rectangle((src.Width - src.Height) / 2, 0, src.Height, src.Height) :
new Rectangle(0, 0, src.Width, src.Width);
int size = Math.Min(desiredSize, crop.Width);
using (Bitmap cropped = src.Clone(crop, src.PixelFormat))
using (Bitmap resized = new Bitmap(size, size))
using (Graphics g = Graphics.FromImage(resized))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(cropped, 0, 0, size, size);
resized.Save(output, ImageFormat.Jpeg);
}
}
}
So how the code above works? It crops the middle part for landscape images and top part for portrait images. So this is a standard behavior. Then it resizes the cropped image (it is already a square).
Is this code OK? Could it be faster? How? Any optimizations possible?
Your routine works great but for one slight problem, at least in my desired application. I wanted the routine to crop portrait bitmaps the same as you do for landscape bitmaps; by cropping off half of the excess from the top and half of excess from the bitmap. So I simply duplicated the landscape crop code to the portrait crop code and adjusted as necessary resulting in this:
Rectangle crop = src.Width > src.Height ?
new Rectangle((src.Width - src.Height) / 2, 0, src.Height, src.Height) :
new Rectangle(0, (src.Height - src.Width) / 2, src.Width, src.Width);
I am trying to resize an image (bitmap) in C# without stretching the image.
Say the image is 100x100 pixels.
I am looking to make it 100x110 pixels, and leave a white gap at the bottom of the image where it added the extra pixels.
I have done this, but cannot find a way to specify the pixel format. I need it to be 8bppindexed. I've attached an example to show the before and after image.
Here is the code I have so far.
string visit2 = "C:\\users\\moorez\\desktop\\visit2.bmp";
Bitmap orig = new Bitmap(visit2);
int width = orig.Width;
int height = orig.Height;
int newHeight = height + 2;
Bitmap newImage = orig.Clone(new Rectangle(0, 0, width, height), System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
newImage.Save("C:\\users\\moorez\\desktop\\visit3.bmp");
Bitmap test = new Bitmap(width, newHeight);
Graphics g = Graphics.FromImage(test);
g.DrawImage(newImage, new Point(0, 0));
test.Save("C:\\users\\moorez\\desktop\\visit4.bmp");
You can try this
Bitmap bmp = new Bitmap(newImage.Width, newHeight);
Graphics g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.DrawImageUnscaled(newImage, 0, 0, newImage.Width, newHeight);
bmp.Save(#"C:\\users\\moorez\\desktop\\visit3.bmp", ImageFormat.Jpeg);
To reproduce this issue, please create a 2x2 pixel black image in Microsoft Paint, saved as D:\small.png. Then create a new WinForms app in Visual Studio, with a no-margin PictureBox. Then use the following code:
void f6(Graphics g)
{
var img = Image.FromFile(#"d:\small3.png");
var srcRect = new Rectangle(0, 0, img.Width, img.Height);
int factor = 400;
var destRect = new Rectangle(0, 0, img.Width * factor, img.Height * factor);
g.DrawRectangle(new Pen(Color.Blue), destRect);
g.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
f6(e.Graphics);
}
I expect the entire rectangle inside the blue margins be black while the output is as follows:
Why is this happening?
ok, thanks. i din't know about interpolation. now, let's change the code as following:
void f6(Graphics g)
{
var img = Image.FromFile(#"d:\small3.png");
var srcRect = new Rectangle(0, 0, img.Width, img.Height);
int factor = 200;
var destRect = new Rectangle(0, 0, img.Width * factor, img.Height * factor);
g.FillRectangle(new SolidBrush(Color.DarkCyan), pictureBox1.ClientRectangle);
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawRectangle(new Pen(Color.Blue), destRect);
g.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
}
it produces the following result:
Result3
which is still unacceptable.
i've tried 60x60 images too. the problem is not because it's a 2x2 image. they produce the same effect. the problem is that why GDI+ decides not to fill the entire destRect with the entire srcRect?!
the original problem is that i've a big image tiled in smaller ones. i need adjacent tiles neither overlap nor seam exist between them. in C++, StretchBlt works properly. but it doesn't produce a smooth stretched image.
GDI+'s definition of the source rectangle is a bit odd.
(0,0) in the source image is actually the center of the upper-left pixel in the image. (width-1,height-1) is the center of the lower-right pixel in the image.
That means that the upper-left pixel is the rectangle from (-0.5,-0.5) to (0.5,0.5), and the lower-right pixel is the rectangle from (width-1.5,height-1.5) to (width-0.5,height-0.5). Thus, your source rectangle is actually outside the image by 0.5 pixels to the right and bottom.
So, you actually need a source rectangle of (-0.5, -0.5, img.Width, img.Height).
I guess you can also try setting PixelOffsetMode as Hans suggests. That would actually make sense of the behavior, but I wouldn't have expected it to apply to source rectangles.
Looks like the image was resized using bicubic interpolation. The normal Bicubic sizing algorithm normally requires 16 pixels to interpolate one, but you only have 4 in the image, so the remaining 12 pixels are never set, staying white.
Try changing the resizing method to nearest-neighbor. This is done by setting the InterpolationMode property of your Graphics object g to InterpolationMode.NearestNeighbor:
void f6(Graphics g)
{
var img = Image.FromFile(#"d:\small3.png");
var srcRect = new Rectangle(0, 0, img.Width, img.Height);
int factor = 400;
var destRect = new Rectangle(0, 0, img.Width * factor, img.Height * factor);
g.ImterpolatonMode = InterpolationMode.NearestNeighbor;
g.DrawRectangle(new Pen(Color.Blue), destRect);
g.DrawImage(img, destRect, srcRect, GraphicsUnit.Pixel);
}
i am using this code to take a jpg image and save it as a thumbnail but it seems very slow ..
Image thumbNail = image.GetThumbnailImage(width, height, null, new IntPtr());
is there any faster way to generate a large amount of thumbnails from a directory of images?
Try Draw Image - Re Edited
public Image ResizeImage(Image openImage, int NewWidth, int NewHeight) {
var openBitmap = new Bitmap(openImage);
var newBitmap = new Bitmap(NewWidth, NewHeight);
using (Graphics g = Graphics.FromImage(openBitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(newBitmap, new Rectangle(0, 0, NewWidth, NewHeight));
}
openBitmap.Dispose(); //Clear The Old Large Bitmap From Memory
return (Image)newBitmap;
}
Typical 3-4mb Image Takes Between 4-8ms
1) By far the fastest and most reliable way to create .Jpg thumbnails is to use partial decompression.
Jpg's have a unique aspect in that it's possible to extract a 1/8 size or 1/16 size (or any power of 2 size) copy of the original without decompressing or sampling the entire original image.
Ever notice how programs like Picassa or Windows Explorer seem to create thumbnails super fast? This is how they do it (when they are not already cached).
This functionality is easily available in any library based on the Independent JPEG Group library code, and most of them are. For example ImageMagick which has a .NET layer available.
2) You can further increase speed by using a hardware accelerated library like libjpeg turbo, although it may require interop.
3) Some explanation of this special .jpg feature is here.
Try it:
public bool GenerateThumbNail(string fileName, string thumbNailFileName,
ImageFormat format, int height, int width)
{
try
{
using (Image img = Image.FromFile(fileName))
{
Image thumbNail = new Bitmap(width, height, img.PixelFormat);
Graphics g = Graphics.FromImage(thumbNail);
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rect = new Rectangle(0, 0, width, height);
g.DrawImage(img, rect);
thumbNail.Save(thumbNailFileName, format);
}
return true;
}
catch (Exception)
{
return false;
}
}
It uses DrawImage too.