Reduce Image resolution in WPF image control - wpf

I have a image control in WPF. I need to reduce the image size control width and height. But when i Do that , the image is not looking good. The data loss is more.
So i thought to reduce the image resolution instead of just changing the image control width and height.
can any one help me how to change the image resolution of the binded image in WPF image control
[I mean image is already binded to image control now I have to the change the resolution only]

In .NET 4 they changed the default image scaling to a low quality one...so you can use BitmapScalingMode to switch back to the higher quality one:
<Image RenderOptions.BitmapScalingMode="HighQuality"
Source="myimage.png"
Width="100"
Height="100" />
http://10rem.net/blog/2010/05/01/crappy-image-resizing-in-wpf-try-renderoptionsbitmapscalingmode
You can also combine the above with other options like the Decode options if your source image is a huge image (this just reduces memory usage in your application).
http://www.shujaat.net/2010/08/wpf-saving-application-memory-by.html
other options to prevent "blurryness" is to put UseLayoutRounding="True" on your root element (i.e. Window)....it's recommended to use this in .NET 4 rather than SnapToDevicePixels:
When should I use SnapsToDevicePixels in WPF 4.0?
http://blogs.msdn.com/b/text/archive/2009/08/27/layout-rounding.aspx

You can use the DecodePixelWidth property like this:
<Image Stretch="Fill">
<Image.Source>
<BitmapImage CacheOption="OnLoad" DecodePixelWidth="2500" UriSource="Images/image.jpg"/>
</Image.Source>
</Image>

1) Have a try with a ViewBox : put your image within a ViewBox.
2) Sometimes the rendering engine looses quality because of pixel alignement issues, especially on low-resolution device. Have a look to SnapsToDevicePixels property, and try to set it to true in the containing control AND / OR in the ViewBox.
3) Obviously you could write a Control that perform a resolution change but it is quite some work.

Try this.
Image img = new Image();
var buffer = System.IO.File.ReadAllBytes(_filePath);
MemoryStream ms = new MemoryStream(buffer);
BitmapImage src = new BitmapImage();
src.BeginInit();
src.StreamSource = ms;
src.DecodePixelHeight = 200;//Your wanted image height
src.DecodePixelWidth = 300; //Your wanted image width
src.EndInit();
img.Source = src;

Related

Silverlight Embedded ImageBrush not rendering when saved as WriteableBitmap

I have an Silverlight application that would create a canvas, save the canvas as a WriteableBitmap and send the WriteableBitmap as a MemoryStream to the server side in order to embed the image inside of an email.
Everything works fine until I get a new requirement that I have to show certain images (Image brush with URL pointing to some image files, to avoid confusion, let's call it GreenIconBrush) inside of the canvas. Now, the canvas was rendering fine on the Silverlight side, however, in the email, everything showed up except the GreenIconBrush.
The canvas consists of an ItemsControl which uses DataTemplate. Depending on the data, I will load different ImageBrush which are defined as follows:
Resource:
<ImageBursh x:key="GreenIconBrush" ImageSource="Images/TrafficLightGreen.png"/>
Xaml:
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Border Background="Binding ScoreByIconColor, Converter={StaticResource ScoreColorToBrushColorConverter}">
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Converter:
<Converts:ValueMapperConverter x:Key="ScoreColorToBrushConverter">
<Converts:ValueMapperConverter.Map>
<Converts:MappedItem key="green" Value="{StaticResource GreenIconBrush}" />
</Converts:ValueMapperConverter.Map>
</Converts:ValueMapperConverter>
Here is the code that renders the image on the canvas and create WriteableBitmap and call the web service to send email
ScoreCard target = new ScoreCard();
setupScoreCard(target, data);
target.UpdateLayout();
canvas.Children.Add(target);
canvas.UpdateLayout();
WriteableBitmap bitmap = new WriteableBitmap(canvas, null);
// bitmap.Invalidate(); <- tried this, does not help
MemoryStream stream = (MemoryStream)SaveToStream(bitmap);
stream.Seek(0, SeekOrigin.Begin);
data.ScoreCard = ReadToEnd(stream);
wcfService.SendEmailAsync(data);
On the web service side:
MailMessage msg = new MailMessage();
// setup to, from, SmtpClient, etc.
msg.IsBodyHtml = true;
AlternateView view = AlternateView.CreateAlternateViewFromString(emailTemplate.ToString(), null, MediaTypeNames.Text.Html);
MemoryStream fs = new MemoryStream();
fs.Write(data.ScoreCard, 0, data.ScoreCard.Length);
fs.Seek(0, SeekOrigin.Begin);
LinkedResource pic = new LinkedResource(fs, new System.Net.Mine.ContentType("image/png");
view.LinkedResources.Add(pic);
msg.AlternateViews.Add(view);
smtp.Send(msg);
I suspect the issue is related to the ImageBrush was not Rendered when I save the canvas to WriteableBitmap but I tried InvalidateMeasure(), InvalidateArrange() on both target and canvas and I even tried to apply transformation and Invalidate the WriteableBitmap but nothing worked.
Please help...
Sincerely,
Charlie Chang
Well, the problem is that the embedded PNG file does not get rendered on the canvas and become part of the writeablebitmap when I send it to the server side to be send in the email.
After wasting many hours trying to make this work, I decided just to recreate the images in Xaml (convert image to Xaml - vector was not a good idea either for you lose too much quality) and everything works fine now.
Charlie

Windows Phone Binding Image Location

I am trying to place a collection of images at certain places on a canvas through Binding.
For some reason, the images are displaying but are NOT at the locations which I have specified.
C#
_roomView.Room = new Room
{
Items = new List<Item> {
new Item {ImageUri = "/Escape;component/Images/Items/a.jpg", XPosition = 190, YPosition = 50},
new Item {ImageUri = "/Escape;component/Images/Items/b.png", XPosition = 390, YPosition = 100},
new Item {ImageUri = "/Escape;component/Images/Items/b.png", XPosition = 490, YPosition = 600}}
};
listBoxItems.ItemsSource = _roomView.Room.Items;
XML
<Canvas>
<Image Source="{Binding ImageUri}" Stretch="None" Canvas.Left="{Binding Room.Items.XPosition}" Canvas.Top="{Binding Room.Items.YPosition}"/>
</Canvas>
XPosition and YPosition are of type int. I have tried changing them to double but the images still aren't being displayed where I want them to be. They only get displayed at the top left of the screen - on top of each other.
Can anyone see the problem?
You're using the pack URI format which is only valid if your images have a Build Action of "Embedded Resource". Even then, I'm not sure this URI format is actually supported for Image.Source and if the URI has to be Relative or Absolute. Check the images Build Action (Right click --> Properties on those files) and try changing the UriKind.
Either way, unless you have a very good reason it's best to keep media assets as:
Build Action = Content and use the normal path URIs. Adding images to DLLs as Embedded Resources is bad for startup performance since it takes longer to load your bigger DLLs into the AppDomain.
It's also bad from a memory usage perspective since the DLLs loaded into memory are now bigger.
Bottom line:
<Image Source="myPicture.png" /> format is much better for performance than
<Image Source="myAssemblyName;component/myPicture.png" /> syntax.
Your C# code has collection of images.
Your XAML only shows a single image. If the XAML is in the listbox data template, you'll get several canvases, with 1 image each.
To display a collection of images on the single Canvas, you could use e.g. <ItemsControl>, set ItemsPanel to a Canvas, and in the data template you specify a single image with source, canvas.left and canvas.top.
If you're trying to do what I've described with a ListBox instead of ItemsControl, it wont work because it has one more layer of UI elements between ItemsPanel and item, namely the item container.
P.S. Fastest way to troubleshoot such GUI problems - XAML Spy, I've bought myself a personal license. Evaluation is 21 days long, fully functional.

How to zoom image inside rectangle WPF

I am trying to zoom in and out image inside Rectangle control. But while doing so my entire Rectangle is getting zoomed in instead of just image inside that. for doing so I am using ScaleTransform and TranslateTranform on Rectangle. I should do the same on image instead of Rectangle but I dont know how? Could anyone please help me out.
XAML :
<Rectangle x:Name="LiveViewWindow" Fill="#FFF4F4F5" HorizontalAlignment="Right"
ClipToBounds="True" />
Code:
InteropBitmap m_LiveViewBitmapSource =Imaging.CreateBitmapSourceFromMemorySection(
section, width, height, PixelFormats.Bgr32, width*4, 0) as InteropBitmap;
ImageBrush m_BackgroundFrame = new ImageBrush(m_LiveViewBitmapSource);
RenderOptions.SetBitmapScalingMode(m_BackgroundFrame, BitmapScalingMode.LowQuality);
LiveViewWindow.Fill = m_BackgroundFrame;
n then for I am using Invalidate() property to render InteropBitmap to Rectangle.
You may set the Transform property of the ImageBrush. Get more information in Brush Transformation Overview.

Saving UserControl as a JPG - result is "squished"

I'm having a strange issue with saving a UserControl as a JPG. Basically what I want to do is create a Live Tile that I can update using a Background Agent (as well as from within the app itself.)
I've followed the steps in this blog post which work great for when I create the tile; I have a custom UserControl with some TextBlocks on it which gets saved as a JPG into IsolatedStorage, exactly as the post says.
Creating the tile is no problem - the UserControl is rendered nicely as it should be. However, when I try to update the tile (using the exact same method - saving a new control as a JPG, then using that JPG as the BackgroundImage), things break down.
The resulting image that gets placed on the tile (and saved in IsolatedStorage) looks like this: squished tile (pulled from IsolatedStorage using the Isolated Storage Explorer Tool)
The background is black, and all the text runs down the side of the image (and overlaps each other) - the expected result is the background being the phone's accent colour, and the text appearing horizontal near the top.
The code used to generate and save the image is exactly the same in both instances - I've abstracted it out into a static method that returns StandardTileData. The only difference is where it is called from: in the working case where the tile is created, it's called from a page within the main app; in the non-working case (where the tile is updated), it's called from a page that can only be accessed by deep-linking from the tile itself.
Any thoughts? I'm guessing something's going wrong with the rendering of the Control to a JPG, since the actual image comes out this way.
The snippet of code that generates the image is here:
StandardTileData tileData = new StandardTileData();
// Create the Control that we'll render into an image.
TileImage image = new TileImage(textA, textB);
image.Measure(new Size(173, 173));
image.Arrange(new Rect(0, 0, 173, 173));
// Render and save it as a JPG.
WriteableBitmap bitmap = new WriteableBitmap(173, 173);
bitmap.Render(image, null);
bitmap.Invalidate();
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
String imageFileName = "/Shared/ShellContent/tile" + locName + ".jpg";
using (IsolatedStorageFileStream stream = storage.CreateFile(imageFileName))
{
bitmap.SaveJpeg(stream, 173, 173, 0, 100);
}
tileData.BackgroundImage = new Uri("isostore:" + imageFileName, UriKind.Absolute);
return tileData;
The XAML for the control I'm trying to convert is here:
<UserControl x:Class="Fourcast.TileImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="173" d:DesignWidth="173" FontStretch="Normal" Height="173" Width="173">
<Border Background="{StaticResource PhoneAccentBrush}">
<StackPanel>
<TextBlock HorizontalAlignment="Stretch"
TextWrapping="Wrap" VerticalAlignment="Stretch" Style="{StaticResource PhoneTextLargeStyle}" x:Name="Temperature"></TextBlock>
<TextBlock x:Name="Condition" HorizontalAlignment="Stretch"
TextWrapping="Wrap" VerticalAlignment="Stretch" Style="{StaticResource PhoneTextNormalStyle}">
</TextBlock>
</StackPanel>
</Border>
</UserControl>
Update: after some investigation with the debugger, it appears the calls to Measure and Arrange don't seem to do anything when the method is called from the class that's updating the tile. When the tile is being created, however, these calls function as expected (the ActualWidth and ActualHeight of the control get changed to 173).
Not sure what exactly is going on here, but here's what I ended up doing to workaround the issue.
I switched from trying to render a Control to defining a StackPanel in code, adding elements to it, then rendering that to an image. Oddly enough, though, the Arrange and Measure methods don't do anything unless another element has had them applied to it, and UpdateLayout called.
The full resulting code (minus the contents of the TextBlocks and writing to an image) is below.
StandardTileData tileData = new
// None of this actually does anything, but for some reason the StackPanel
// won't render properly without it.
Border image = new Border();
image.Measure(new Size(173, 173));
image.Arrange(new Rect(0, 0, 173, 173));
image.UpdateLayout();
// End of function-less code.
StackPanel stpContent = new StackPanel();
TextBlock txbTemperature = new TextBlock();
txbTemperature.Text = temperature;
txbTemperature.Style = (Style)Application.Current.Resources["PhoneTextLargeStyle"];
stpContent.Children.Add(txbTemperature);
TextBlock txbCondition = new TextBlock();
txbCondition.Text = condition;
txbCondition.Style = (Style)Application.Current.Resources["PhoneTextNormalStyle"];
stpContent.Children.Add(txbCondition);
stpContent.Measure(new Size(173, 173));
stpContent.Arrange(new Rect(0, 0, 173, 173));
The way to get the background of the image to use the phone's accent color is to make it transparent. This will require that you save a PNG rather than a JPG.
The ouput image looks like everything is wrapping more thna it needs to. You probably need to adjust the widths of the elements you're including in your control. Without the XAML it's hard to be more specific.
I also get this strange behaviour - squished tile (using Ree7 Tile toolkit),
Try call update tile into Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
CustomTile tile = GetTile(card);
StandardTileData tileData = tile.GetShellTileData();
shellTile.Update(tileData);
});
Working for me

Why does WPF make working with images so much more difficult?

I used to be able to do so very much with just the Bitmap and Graphics objects. Now that I've been using WPF the only thing I seem to be able to do is load an image and show it and make it dance around the stupid screen. Why did they get rid of these very useful tools. Are they trying to stupefy .Net?
All I want to do is load an image from a file and cut it into two parts. This was easy with .Net 2.0 and System.Drawing. But with WPF, I'm hitting a brick wall without using some very low level code. I've tried working with WriteableBitmap. But it doesn't seem to really be what I'm wanting. Is there no way to wrap a DrawingContext around a BitmapImage or something?
Please tell me that WPF is more than HTML for applications. I am REALLY frustrated!!
Edits:
Also, how on earth does one save an image to a file?
If you want to cut the image in two parts, why not use the CroppedBitmap class?
Consider the following XAML. One source BitmapImage shared by two CroppedBitmaps, each showing different parts of the source.
<Window.Resources>
<BitmapImage x:Key="bmp" UriSource="SomeBitmap.jpg" />
</Window.Resources>
<StackPanel>
<Image>
<Image.Source>
<CroppedBitmap Source="{StaticResource ResourceKey=bmp}">
<CroppedBitmap.SourceRect>
<Int32Rect X="0" Y="0" Width="100" Height="100" />
</CroppedBitmap.SourceRect>
</CroppedBitmap>
</Image.Source>
</Image>
<Image>
<Image.Source>
<CroppedBitmap Source="{StaticResource ResourceKey=bmp}">
<CroppedBitmap.SourceRect>
<Int32Rect X="100" Y="150" Width="50" Height="50" />
</CroppedBitmap.SourceRect>
</CroppedBitmap>
</Image.Source>
</Image>
</StackPanel>
Update: to do something similar in code:
var bitmapImage = new BitmapImage(new Uri(...));
var sourceRect = new Int32Rect(10, 10, 50, 50);
var croppedBitmap = new CroppedBitmap(bitmapImage, sourceRect);
Well there is this http://www.nerdparadise.com/tech/csharp/wpfimageediting/
or perhaps you could add a reference to System.Drawing to your project and then do the editing the way you are comfortable with.
You are probably best off using TransformedBitmap. Load your Bitmap as a BitmapSource, then set the Transform property to however you want the image to transform. You have several different transformation options here. This allows you to rotate, screw, matrix, etc. transformations. If you want to apply more than one, use a TransformGroup and apply several transformations at once.
You can also use BitmapFrame.Create(...) to work with the transformed image more.
Some Pseudo code:
var image = new BitmapSource(...); //Your image
var transformBitmap = new TransformedBitmap(image);
var transformBitmap.Transform = ..//Set your transform;
//optionally:
var frame = BitmapFrame.Create(transformBitmap);

Resources