RenderTransform RotateTransform Blurry Image - c#

I have the following custom UserControl that represents a card in my application:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyApp" xmlns:Properties="clr-namespace:MyApp.Properties" Core:Class="MyApp.Card" Height="176" RenderTransformOrigin="0.5,0.5" Width="83" UseLayoutRounding="True">
<UserControl.LayoutTransform>
<TransformGroup>
<RotateTransform/>
<ScaleTransform/>
</TransformGroup>
</UserControl.LayoutTransform>
<UserControl.RenderTransform>
<TransformGroup>
<RotateTransform/>
<ScaleTransform/>
</TransformGroup>
</UserControl.RenderTransform>
<UserControl.Resources>
<Namespace:ImagesConverter Core:Key="ImagesConverter"/>
</UserControl.Resources>
<Canvas Core:Name="Layout">
<Image Core:Name="Image" HorizontalAlignment="Left" Source="{Binding Source={Core:Static Properties:Resources.Cards}, Converter={StaticResource ImagesConverter}}" Stretch="None" VerticalAlignment="Top">
<Image.Clip>
<RectangleGeometry Core:Name="Clipping" Rect="0,0,83,176"/>
</Image.Clip>
<Image.RenderTransform>
<TranslateTransform Core:Name="Translation" X="0" Y="0"/>
</Image.RenderTransform>
</Image>
<Rectangle Core:Name="Highlight" Canvas.Left="-2" Canvas.Top="-2" Height="180" Opacity="0.7" Stroke="#FFFFF500" StrokeThickness="3" Visibility="Collapsed" Width="87"/>
</Canvas>
</UserControl>
As you can see... I have a bit PNG image containing all the card faces and then, when I create a new card passing Suit and Rank enum values in constructor, I calculate the correct clipping rectagle and translation for the image.
Everything works like a charm... except when I try to animate my card with a Storyboard that requires a 90° rotation. Here is my code (the Storyboard is defined in MainWindow.Resources):
<DoubleAnimation BeginTime="00:00:00.4" Duration="00:00:00.2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)" To="90"/>
And here is the result:
I can't understand what's going on... but the images gets somehow stretched and becomes very blurry as you can see. I tried using UseLayoutRounding="True" in my card control and also SnapsToDevicePixels="True" in my MainWindow as suggested somewhere else... but it's not working!
Of course... if I rotate the card using LayoutTransform instead of RenderTransform everything works perfectly and the card is not blurry... but I can't make the card rotate around it's center and my animation requires a 90° rotation from the center. Animating Canvas.Top for half of the card height together with layout rotation looks to me like a very unbrilliant solution... and it also makes my animation looks very bad.
Can you suggest me a solution please?
[EDIT] I tried to use RenderOptions.BitmapScalingMode="NearestNeighbor" and RenderOptions.EdgeMode="Aliased"... but it becomes even worse:

The problem was that the underlying image, and so the layout, had an odd value as Width (83). RenderTransformOrigin, even if set to "0.5,0.5", was probably rounding up or down that value resulting in a very bad rendering.
Changing both the image and layout Width to a even value (82), totally resolved the problem.

You could try changeing the RenderOptions on the Image and see if you can find a setting that works, I have added an example of settings I use for this exact situation in my application
RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Example:
<Image RenderOptions.BitmapScalingMode="NearestNeighbor"
RenderOptions.EdgeMode="Aliased"
Core:Name="Image" HorizontalAlignment="Left" Source="{Binding Source={Core:Static Properties:Resources.Cards}, Converter={StaticResource ImagesConverter}}" Stretch="None" VerticalAlignment="Top">
<Image.Clip>
<RectangleGeometry Core:Name="Clipping" Rect="0,0,83,176"/>
</Image.Clip>
<Image.RenderTransform>
<TranslateTransform Core:Name="Translation" X="0" Y="0"/>
</Image.RenderTransform>
</Image>

You indicated that using LayoutTransform works perfectly without blurring the image. Have you tried using it and specifying the center as the pivot of rotation?
Something like:
<Image ...>
<Image.LayoutTransform>
<RotateTransform CenterX="0.5" CenterY="0.5" Angle="90"/>
</Image.LayoutTransform>
</Image>

As #Tommaso Belluzzo said, this is cause from RenderTransformOrigin="0.5,0.5"
Your answer above really solved my question.
Allow me to show my code as following:
System.Drawing.Bitmap bm = null;
//RotateTransform(angle) with RenderTransformOrigin="0.5,0.5" might cause blurry if the width or height is odd,
using (System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(path))
{
int width = bitmap.Width;
int height = bitmap.Height;
if (0 != width % 2) width ++;
if (0 != height % 2) height ++;
bm = new System.Drawing.Bitmap(width, height);
bm.SetResolution(96, 96);
using (System.Drawing.Graphics g = System.Drawing. Graphics.FromImage(bm))
{
//I don't want to do anything which may modify(resize) the original image.
g.Clear(System.Drawing.Color.White);
g.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height);
}
//bm.Save(directory+#"\tmp.bmp", ImageFormat.Bmp);
}
//.....(show bm)

Related

Using SkiaSharp to create crossed out circle filled with color

I am new to working with skiasharp with avalonia. I looked at the documentation and examples of how to make some basic shapes, like circles, rectangles, etc. Can anyone give advice or suggestion on how to make a crossed circle filled with color? I think it would be best to do UserControl, something like in wpf.
How you package this up (UserControl) really depends on the use case and intention of the UI element.
<Viewbox x:Name="Vol2"
Width="25"
Height="25">
<Canvas Width="100" Height="100">
<Ellipse x:Name="MyCirlce" Width="100" Height="100" Fill="Black">
</Ellipse>
<Rectangle Height="50" Fill="White" Width="15" Canvas.Top="50" Canvas.Left="42.5" RenderTransformOrigin="0,0">
<Rectangle.RenderTransform>
<RotateTransform Angle="45"></RotateTransform>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</Viewbox>
A ViewBox scales it's content, like an SVG.
We then plop a Canvas in there and add our Ellipse. We then add a Rectangle and position in (Left, Right) and then add a RotateTransform.
This should get you going.

Image cropping windows phone

I want to crop an image in wp7 such that only some part of it appears in an imagebox (or just the 'image' control). I used image.clip, but it actually retains the whole image, and just whitens the 'to-be-cropped' portion. How can i crop the image so that the cropped image is the resultant image?
Note: I am looking for a way in xaml
This is working for me just fine with Light and Dark themes. I'm showing a square image inside an ellipse clip and adding a border around it; it's not showing through.
<Grid>
<Ellipse
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="78"
Height="78"
StrokeThickness="5"
Stroke=""/>
<Image
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="UniformToFill"
Source="{Binding MediumPhotoUrl}"
Width="64"
Height="64" >
<Image.Clip>
<EllipseGeometry RadiusX="32" RadiusY="32" Center="32,32"/>
</Image.Clip>
</Image>
</Grid>
Are you sure you're doing clip correctly?
If you know the size of the initial image, you can calculate the required transforms, for example:
<Image Width="100" Height="100">
<Image.Clip>
<!-- Image after clipping takes only a small area in the center -->
<RectangleGeometry Rect="25,25,50,50"/>
</Image.Clip>
<Image.RenderTransform>
<!-- Transforms Rect(25,25,50,50) into Rect(0,0,100,100) -->
<TransformGroup>
<TranslateTransform X="-25" Y="-25"/>
<ScaleTransform ScaleX="2" ScaleY="2"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
I don't know a way of doing it automatically (without code behind).

DropShadowEffect on Rectangle with slight transparency - how to make shadow not show inside fill?

I have a DropShadowEffect on a Rectangle with a fill that's slightly transparent. The problem is, the drop shadow also shows up inside the fill, which is what I don't want. Anyways to solve this? I tried this, but it doesn't work for me :/
This is very tricky.
Okay, I don't know the exact answer. But here is what will give you almost the desired effect. Try it.
This is a sample Grid with yellow background. I've drawn two intersecting rectangles of 100x100 dimension on it. You may need to customize the size according to your need. One rectangle is gray rectangle (to show shadow), and the other is a red rectangle (the actual semi-transparent rectangle that you want to display). The shadow depth has been hard coded as 5 pixels here. Please customize it at:
RectangleGeometry Rect="5,5,100,100"
RectangleGeometry Rect="0,0,95,95"
So, the grid looks like:
<Grid Background="Yellow">
<!-- A rectangle for shadow -->
<Rectangle Fill="Gray" Width="100" Height="100" Opacity=".7">
<Rectangle.Clip>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="5,5,100,100"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="0,0,95,95"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Rectangle.Clip>
<Rectangle.Effect>
<!-- For nice soft shadow effect -->
<BlurEffect Radius="5" />
</Rectangle.Effect>
</Rectangle>
<!-- Actual rectangle which is translucent -->
<Rectangle Fill="Red" Width="100" Height="100" Opacity=".6" >
<Rectangle.Clip>
<RectangleGeometry Rect="0,0,95,95"/>
</Rectangle.Clip>
</Rectangle>
</Grid>
Update (8-Nov-11):
You can replace the hard-coded width and height by binding them to parent's width and height. Check this SO topic for multiple binding which you will need. More study material on binding: here.
An example of how the XAML snippet will look like is:
<Rectangle Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"
Height="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualHeight}">
</Rectangle>
Since I'm not the expert on data binding, you need to research on your own from here. I feel that you will need your own value-converters for assigning special width ad height (ActualWidth - ShadowDepth kind of stuff (ShadowDepth being 5 pixels here)).

Plane projection and scale causing bluring in silverlight

Ok,
So I've tried to make an application which relies on images being scaled by an individual factor. These images are then able to be turned over, but the use of an animation working on the ProjectionPlane rotation.
The problem comes around when an image is both scaled and rotated. For some reason it starts bluring, where a non scaled image doesn't blur.
Also, if you look at the example image below (top is scaled and rotated, bottom is rotated) the projection of the top one doesn't even seem right. Its too horizontal.
http://img43.imageshack.us/img43/5923/testimages.png http://img43.imageshack.us/img43/5923/testimages.png
This this the code for the test app:
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Canvas x:Name="LayoutRoot" Background="White">
<Border Canvas.Top="25" Canvas.Left="50">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="3" ScaleY="3" />
</TransformGroup>
</Border.RenderTransform>
<Border.Projection>
<PlaneProjection RotationY="45"/>
</Border.Projection>
<Image Source="bw-test-pattern.jpg" Width="50" Height="40"/>
</Border>
<Border Canvas.Top="150" Canvas.Left="50">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup>
</Border.RenderTransform>
<Border.Projection>
<PlaneProjection RotationY="45"/>
</Border.Projection>
<Image Source="bw-test-pattern.jpg" Width="150" Height="120"/>
</Border>
</Canvas>
</UserControl>
So if anyone could possible shed any light on why this may be happening, I'd very much appreciate it. Suggestions also welcome! :)
** Update **
Just to clarify, if the projection plane rotation is 0, the image becomes un-blurred, so its only during the rotation that the image is blurred.
The top image's width is set to 50 and the height to 40. So it is downscaled. Afterwards you scale it up to the right size 150, 120. I guess Silverlight scales the image down and doesn't store the original size due to performance optmization. Leave the Scale out and set the right width and height for the first image.
It looks like the top image is being filtered as it is being drawn. From your code you have:
<Image Source="bw-test-pattern.jpg" Width="50" Height="40"/>
for the top image and
<Image Source="bw-test-pattern.jpg" Width="150" Height="120"/>
for the bottom one. You have different image sizes so the top one might be being upscaled and therefore blurred as it interpolates the missing pixels.
I'm not familiar with silverlight so I don't know how you'd control the filtering options, but setting the top line above to the same as the bottom one might fix it.

Center text horizontally and vertically in Silverlight

I am working on a C# Silverlight application that needs some text rotated 90 degrees CCW and for it to be center within an image that is 100px x 221px. The rotation part was easy to do in the design view, but centering the text has been a nightmare. (I am using a 16px font that cant be changed or resized)
My text is dynamic and can be one or two lines. When it is two lines long I can center it fine...but if It is only one line long, I cannot center it. The only way to center it so far is to resize the box and move it to the right.
Is there an easy way of doing this?
alt text http://www.freeimagehosting.net/uploads/f0435a8c65.png
The answer turned out to be simple:
<Grid x:Name="LayoutRoot">
<Image x:Name="Background" Source="Background.png" Stretch="Fill"/>
<TextBlock x:Name="Title"
Margin="-19.75,68.25,-21.25,67.806" Text="Here is some text to fill this up"
Foreground="#FF00A33D"
FontSize="22" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto"
TextAlignment="Center" TextWrapping="Wrap"
>
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-90"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
Then in the cs file set the alignment when you change the text
Title.VerticalAlignment = VerticalAlignment.Bottom;

Categories