I am loading an image using the the PhotoChooserTask on Windows Phone 7. After loading the photo, I want to be able to immediately resize the photo whilst keeping it aspect ratio and this is without displaying the image in the UI, then save the to the IsolatedStorage.
So far, I have something like this:
private void SaveToIsolatedStorage(Stream imageStream, string fileName)
{
using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var fileStream = myIsolatedStorage.CreateFile(fileName))
{
var wbBarcodeImage = new WriteableBitmap(100, 100);
wbBarcodeImage.SetSource(imageStream);
wbBarcodeImage.Resize(100, 100, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
wbBarcodeImage.SaveJpeg(fileStream, 100, 100, 0, 85);
}
}
}
It's resizing the image, but I am unable to figure out how to keep the aspect ratio of the image.
You can query the image properties for height and width and determine aspect ration. If you then know either height or width, you can calculate the other value. Your need to add querying these properties to your code and then a little math.
Related
I'm drawing an SKBitmap on SKCanvas, then drawing some text over the canvas, then saving the image to disk. The text is not saved to the file on disk, and I only see the original bitmap. What am I missing ?
SKBitmap pngImage = SKBitmap.Decode(msBitmap.ToArray());
mycanvas.DrawBitmap(pngImage, 0, 0);
mycanvas.DrawText("Text", 10, 10, myBrush);
using (var stream = File.OpenWrite(myfileName))
{
SKData d = SKImage.FromBitmap(pngImage).Encode(SKEncodedImageFormat.Png, 100);
d.SaveTo(stream);
}
You are drawing both the bitmap and the text to the canvas (mycanvas) - but when you save, you are saving just the image data (pngImage). You need to get what is drawn on the canvas, and save that.
You might try the Snapshot() method, available on the surface (which owns the SKCanvas object).
I'm trying to create a level editor using Windows Forms for my monogame project and need to draw small pixel based images to a picture box with no quality loss when scaled. In monogame when I need to do this I can just set the draw type to PointClamp and then each pixel is drawn as is instead of being pixelated when zoomed; I was hoping for something like this via a picturebox. Right now it looks like this But I'd prefer a more crisp and clean image like this (The second is how it'll appear in monogame). I haven't uploaded any code for this, but just assume I grabbed an image from the filestream and used the bitmap constructor to scale it up (don't think that's relevent but I'll just put it out there).
Image croppedImage, image = tileMap.tileBox.Image;
var brush = new SolidBrush(Color.Black);
try { croppedImage = CropImage(image, tileMap.highlightedRect); } catch {
return; // If crop target is outside bounds of image then return
}
float scale = Math.Min(higlightedTileBox.Width / croppedImage.Width, higlightedTileBox.Height / image.Height);
var scaleWidth = (int)(higlightedTileBox.Width * scale);
var scaleHeight = (int)(higlightedTileBox.Height * scale);
try { higlightedTileBox.Image = new Bitmap(croppedImage, new Size(scaleWidth, scaleHeight)); } catch {
return; // Image couldn't be scaled or highlighted tileBox couldn't be set to desired image
}
CropImage:
private static Image CropImage(Bitmap img, Rectangle cropArea) {
return img.Clone(cropArea, img.PixelFormat);
}
private static Image CropImage(Image img, Rectangle cropArea) {
return CropImage(new Bitmap(img), cropArea);
}
The code above is my current method in it's entirety. tileMap is a form and tilebox is the picturebox within that form.image is the full spritesheet texture before being cropped to what the user has highlighted. After being cropped I attempt to set the current picturebox (highlightedTileBox's) image to a scaled up version of the cropped image.
So I got a solution by trying around a bit.
It looks like scaling images directly by size is using some sort of interpolation.
To try different interpolation modes supported by Winforms, I created a little demo.
As you can see, every label contains the name of the InterpolationMode and is followed by its resulting image. The original bitmap I used is the small one at the top.
From your question, it looks like you would like to achieve something like NearestNeighbour.
Following code scales bmp and the result is stored in bmp2. Try if that's what you want. Consider building a proper implementation if you're using this as solution (disposing unused bitmaps etc.).
I hope it helps.
Bitmap bmp = new Bitmap("test.bmp");
Bitmap bmp2;
Graphics g = Graphics.FromImage(bmp2=new Bitmap(bmp.Width * 2, bmp.Height * 2));
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, bmp.Width * 2, bmp.Height * 2);
g.Dispose();
I've searched a bit around the discussions\forums/StackOverflow/Official documentation, but i couldn't find much information about how to achieve what i'm trying. Most of the official documentation covers the command-line version of ImageMagick.
I'll describe what i'm trying to do:
I have a image loaded that i would like to paste into a larger one.
Ex: the image i loaded has 9920 width, 7085 height. I would like to place it in the middle of a larger one (10594 width, 7387 height). I do have all border calculation ready ([larger width - original width / 2] , same goes for height).
But i don't know how to do it using MagickImage. Here's the max i got:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = new MagickImage(MagickColor.FromRgb(255, 255, 255), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
}
}
Here's the max i got. In order to achieve this, i used the following post:
How to process only one part of image by ImageMagick?
And i'm not using GDI+ because i'll be always working with larger TIFF files (big resolutions), and GDI+ tends to throw exceptions (Parameter not valid, out of memory) when it can't handle everything (i loaded three images with an resolution like that, and got out of memory).
Any help will be kindly appreciate, thanks.
Pablo.
You could either Composite the image on top of a new image with the required background or you could Clone and Extent if with the required background. In the answer from #Pablo Costa there is an example for Compositing the image so here is an example on how you could extent the image:
private void drawInkzone(MagickImage loadedImage, List<string> inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation);
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI);
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);
using (MagickImage image = loadedImage.Clone())
{
MagickColor background = MagickColors.Black;
int width = (int)zoneAreaWidth_Pixels;
int height = (int)zoneAreaHeight_Pixels;
image.Extent(width, height, Gravity.Center, background);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
I managed to accomplish what i needed.
Cool that i didn't had to calculate borders.
Here's the code:
private void drawInkzone(MagickImage loadedImage, List<string>inkzoneAreaInformation, string filePath)
{
unitConversion converter = new unitConversion();
List<double> inkZoneInfo = inkZoneListFill(inkzoneAreaInformation); //Larger image information
float DPI = getImageDPI(filePath);
double zoneAreaWidth_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(4), DPI); //Width and height for the larger image are in mm , converted them to pixel
double zoneAreaHeight_Pixels = converter.mmToPixel(inkZoneInfo.ElementAt(5), DPI);//Formula (is: mm * imageDPI) / 25.4
using (MagickImage image = new MagickImage(MagickColor.FromRgb(0, 0, 0), Convert.ToInt32(zoneAreaWidth_Pixels), Convert.ToInt32(zoneAreaHeight_Pixels)))
{
//first: defining the larger image, with a white background (must be transparent, but for now its okay)
using (MagickImage original = loadedImage.Clone())
{
//Cloned the original image (already passed as parameter)
image.Composite(loadedImage, Gravity.Center);
image.Write(#"C:\DI_PLOT\whatever.png");
}
}
Hope this helps someone :)
This is my current application GUI, its still not been finalized as I am just trying to get the main functions to work (This is for self practice),
https://www.dropbox.com/s/gtsz5k4mtpkuva5/appLatest.png?dl=0
At first when I was trying to save the image it was saving the whole canvas (printCanvas) then I learnt it was possible to "crop" the rendered image, but now my image is 480 x 480.
I am just wondering, how would I know increase the resolution of the image, I would like to to be able to be printed on something like A3?
Here is my current code, for the Rendering / Cropping of the image,
private static void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
var renderTargetBitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 92, 92, PixelFormats.Pbgra32);
renderTargetBitmap.Render(visual);
var crop = new CroppedBitmap(renderTargetBitmap, new Int32Rect(610, 130, 480, 480));
var bitmapFrame = BitmapFrame.Create(renderTargetBitmap);
encoder.Frames.Add(BitmapFrame.Create(crop));
using (var fileStream = new FileStream(fileName, FileMode.OpenOrCreate))
{
encoder.Save(fileStream);
}
}
Thanks for any insight you can provide!
I have a situation where I want to convert some XAML to an image, so I created a RichTextBox and then took the image of it. Now problem is that words in image is blurred, any idea how I might be able to fix it?
public System.Drawing.Bitmap ConvertXamltoImage(string XamlString, int Width, int Height)
{
RichTextBox AdContentRichTextBox = new RichTextBox() { Width = Width, Height = Height };
AdContentRichTextBox.BorderThickness = new Thickness(0);
XmlReader _XmlReader = XmlReader.Create(new StringReader(XamlString));
AdContentRichTextBox.Document = XamlString;
var size = new Size(Width, Height);
AdContentRichTextBox.Measure(size);
AdContentRichTextBox.Arrange(new Rect(size));
RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 300, 300, PixelFormats.Pbgra32);
bmp.Render(AdContentRichTextBox);
DrawingVisual _drawingVisual = new DrawingVisual();
using (DrawingContext _drwaingContext = _drawingVisual.RenderOpen())
{
VisualBrush _visualBrush = new VisualBrush(AdContentRichTextBox);
}
PngBitmapEncoder _png = new PngBitmapEncoder();
_png.Frames.Add(BitmapFrame.Create(bmp));
System.Drawing.Bitmap _tempBitmap = null;
using (Stream _fileStream = new MemoryStream())
{
_png.Save(_fileStream);
_tempBitmap = new System.Drawing.Bitmap(_fileStream);
_fileStream.Flush();
}
return _tempBitmap;
}
Hmmmm..there could be lots of things all interacting here:
1st
"Grayscale fall back - if ClearType is disabled or one is rendering text in certain situations where the ClearType algorithm cannot be run, WPF will use a grayscale rendering algorithm to antialias the rendered text."
Rendering Text to a RenderTargetBitmap seems to be one of those situations....(the renderer switches from a hardware to a software path).
2nd
In addition NET 4 switched the default scaling algorithm from high-quality (Fant) to low-quality (Bi-Linear).....now that shouldn't come into play here as it doesn't look like you are scaling the bitmap in any way...but you never know what's going on inside. It's possible to switch the scaler back to the higher quality one.
http://www.olsonsoft.com/blogs/stefanolson/post/Workaround-for-low-quality-bitmap-resizing-in-WPF-4.aspx
3rd
You may need to take into account the parent container of the RichTextBox...see last link below, mentions it can distort the font rendering.
Problems with rendering text as bitmaps using WPF
Some ideas on how to work around this are:
render the RichTextBox at a higher resolution e.g. 600dpi, and then scale down the bitmap (probably will make no difference)
capture the screen....difficult or not practical if your visual is offscreen/obscured, etc.
See related links:
http://windowsclient.net/wpf/white-papers/wpftextclarity.aspx
WPF RenderTargetBitmap downscaling text ClearType to GreyScale
WPF RenderTargetBitmap downscaling TextRenderMode to GreyScale
WPF text rendering inconsistencies