I have a series of rectangles in which the user can add images to, by dragging the images in.
The image is then scaled down in proportion and the rectangle is then filled with an ImageBrush.
I need for the user to be able to manipulate the image within the rectangle to fit their needs. Like any photo collage app does.
My question is: How can I show the full, unmasked image on top of the rectangle so that the user can manipulate it to their needs? I don't know where to start with this one.
private async void Mask_Drop(object sender, DragEventArgs e)
{
Rectangle maskSq = e.OriginalSource as Rectangle;
var maskW = maskSq.Width.ToSingle();
var maskH = maskSq.Height.ToSingle();
double maskX = Canvas.GetLeft(maskSq);
double maskY = Canvas.GetTop(maskSq);
// Image sizes for bounding to mask
float boundH = Convert.ToSingle(size.Height);
float boundW = Convert.ToSingle(size.Width);
maskSq.Fill = new ImageBrush
{
ImageSource = new BitmapImage(new Uri("ms-appdata:///local/" + SelectedImage.Name, UriKind.Absolute)),
Stretch = Stretch.UniformToFill
};
}
private void Tap_Collage(object sender, TappedRoutedEventArgs e)
{
//Gets the full image from ImageBrush
ImageBrush brush = (ImageBrush)(((Rectangle)sender).Fill);
Rectangle rect = sender as Rectangle;
//Mask sure rectangle does not drag, just the image brush
rect.CanDrag = false;
rect.StrokeThickness = 6;
//Drag Image Functionality
rect.ManipulationDelta += ImageManipulation.Resize_ImageEdit;
ImageManipulation.ImageEdit_Drag = new TranslateTransform();
brush.Transform = ImageManipulation.ImageEdit_Drag;
//Zoom Image Functionality
ImageManipulation.ImageEdit_Zoom = new ScaleTransform();
brush.RelativeTransform = ImageManipulation.ImageEdit_Zoom;
}
Class
public static class ImageManipulation
{
public static TranslateTransform ImageEdit_Drag;
public static ScaleTransform ImageEdit_Zoom;
public static RotateTransform ImageEdit_Rotate;
public static void Resize_ImageEdit(object sender, ManipulationDeltaRoutedEventArgs e)
{
ImageEdit_Drag.X += e.Delta.Translation.X;
ImageEdit_Drag.Y += e.Delta.Translation.Y;
ImageEdit_Zoom.ScaleX *= e.Delta.Scale;
ImageEdit_Zoom.ScaleY *= e.Delta.Scale;
if (ImageEdit_Zoom.ScaleX < 1.0)
{
ImageEdit_Zoom.ScaleX = 1.0;
ImageEdit_Zoom.ScaleY = 1.0;
}
ImageEdit_Rotate.Angle += e.Delta.Rotation;
}
}
XAML
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:CollageGrid">
<Rectangle Width="{Binding CollageW}" Height="{Binding CollageH}" AllowDrop="True" CanDrag="True" Fill="Transparent"
Drop="Mask_Drop"
DragOver="Mask_DragOver"
ManipulationMode="TranslateX, TranslateY" Stroke="Black" StrokeThickness="2" DragEnter="Mask_DragEnter" DragLeave="Mask_DragLeave" Tapped="Tap_Collage">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding CollageX}" Y="{Binding CollageY}"/>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
Example of what I'm looking to acheive:
How it looks currently:
You can try to replace the Rectangle with the Polyline to draw the Rectangle so that you can access the Image which is on the bottom of the Rectangle.
<Grid>
<Image Source="Assets/image1.jpg" Width="800"
Height="400" Tapped="Image_Tapped" />
<Polyline Stroke="Black" HorizontalAlignment="Center"
VerticalAlignment="Center"
StrokeThickness="4" Tapped="Polyline_Tapped"
Points="0,0,0,200,200,200,200,0,0,0" />
</Grid>
---Update---
UniformToFill will cause the image is resized to fill the destination dimensions while it preserves its native aspect ratio. If the aspect ratio of the destination rectangle differs from the source, the source content is clipped to fit in the destination dimensions. So it is not suitable for your requirement. You should manually scale the Image to make image fit one of your Rectangle's border and keep other part out of the Rectangle.
It seems you put the image as the Rectangle's background image brush, there are no other place to display the image out of the Rectangle. So I think, we may take a considerition for a new scenario.
As my pervious reply, using Image control to display the image and Polylines to draw a Rectangle above the Image so that we can operate the Image which is below the Rectangle using the manipulation events to fit the rectangle, meanwhile we can also use the community toolkit BackdropBlurBrush on the xaml layout to Blur the outer image.
Related
I have a ContentDialog where I have an image and another image overlayed to place a watermark on it:
<StackPanel Orientation="Vertical">
<Grid>
<Image x:Name="ImageOriginal" Source="{x:Bind OriginalImage, Mode=OneWay}" MinHeight="120" MinWidth="160"/>
<Image x:Name="ImageOverlay" Source="{x:Bind OverlayImage, Mode=OneWay}" MinHeight="120" MinWidth="160" PointerPressed="ImageOverlay_PointerPressed" />
</Grid>
...
So the goal was, to place a text (centered horizontally and vertically) on the clicked position.
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
var point = e.GetCurrentPoint(ImageOverlay);
var pointPosition = new Windows.Foundation.Point((int)point.Position.X, (int)point.Position.Y);
Position = new PointF((float)pointPosition.X, (float)pointPosition.Y);
UpdateOverlayImage();
}
(PointF) is a floating point point of ImageSharp. With the code above my text placement is always a bit off, like 50 pixels in each direction.
I also tried it with the following code, but this just makes the text placed wrongly in the opposite direction:
GeneralTransform transform = ImageOverlay.TransformToVisual(Window.Current.Content);
Windows.Foundation.Point coordinatePointToWindow = transform.TransformPoint(pointPosition);
Update:
Someone wanted to know, how I draw the text,s o here it is, this code is inside UpdateOverlayImage():
var watermarkedImage = OverlayImageBase.Clone(ctx => ctx.ApplyWatermark(font, tbWatermarkText.Text, color, Position));
OverlayImage = watermarkedImage.ToBitmap();
And the ApplyWatermark extension method looks like this:
public static IImageProcessingContext ApplyWatermark(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
PointF position)
{
var textGraphicOptions = new TextGraphicsOptions(true)
{
ColorBlendingMode = SixLabors.ImageSharp.PixelFormats.PixelColorBlendingMode.Normal,
HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment.Center,
VerticalAlignment = SixLabors.Fonts.VerticalAlignment.Center,
};
return processingContext.DrawText(textGraphicOptions, text, font, color, position);
}
Any ideas why my coordinates are off?
I found out what was wrong. As the displayed image inside the ContentDialog is scaled, the coordinates of the drawn text cannot be directly the same coordintes as received from the pointer pressed event (of course).
So we have to calculate the relative to the size of the scaling. This is how I could solve it:
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint ptrPt = e.GetCurrentPoint(ImageOverlay);
// As the image is scaled we have to caluclate the position inside the image based on the position of the pointer pressed
float imagePressedX = (float)(OriginalImage.PixelWidth / ImageOverlay.ActualWidth * ptrPt.Position.X);
float imagePressedY = (float)(OriginalImage.PixelHeight / ImageOverlay.ActualHeight * ptrPt.Position.Y); ;
Position = new PointF(imagePressedX, imagePressedY);
UpdateOverlayImage();
}
I need a WPF Control with 300000000 Width and Height, on which I will draw Rectangles, based on some input. I am drawing the Rectangle on the OnRender of the control. For testing purpose I have put the control inside a ScrollViewer and am drawing Rectangles on each pixels from (0,0) to (1000,1000). If the Width & Height is 1000000, the Rectangles are drawn properly, shown below, (the Rectangles are plotted on all the pixels from (0,0) to (1000,1000), so it looks like the on in the below link)
https://social.msdn.microsoft.com/Forums/getfile/950012
But If I increase the size to 3000000, few lines are missing in the drawing like below,
https://social.msdn.microsoft.com/Forums/getfile/950018
My Xaml:
<ScrollViewer x:Name="MainScrollViewer"
Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<waferControl:WaferMapControl x:Name="WaferControl"
Grid.Row="2"
Grid.ColumnSpan="2"
Width="30000000"
Height="30000000"
Margin="10"
ColumnCount="{Binding WaferInfo.ColumnCount}"
RowCount="{Binding WaferInfo.RowCount}"
SelectedCells="{Binding SelectedCellCollection}"
Visibility="Collapsed" />
</ScrollViewer>
I am using the following code in OnRender of WaferMapControl.
private void RenderBackgroud(IEnumerable lists`enter code here`)
{
using (DrawingContext dc = _backgroundDv.RenderOpen())
{
SolidColorBrush brush = Brushes.Black;
if (brush.CanFreeze)
brush.Freeze();
foreach (GridCellInfo cellInfo in lists)
{
double length = cellInfo.GridRange;
var point = new Point(
cellInfo.RowIndex + 1,
ActualHeight - cellInfo.ColumnIndex - length);
RenderBackgroud(dc, point, brush, length);
}
}
}
private void RenderBackgroud(DrawingContext dc, Point point, Brush brush, double length)
{
var rect = new Rect(point, new Size(length, length));
dc.DrawRectangle(brush, null, rect);
}
The cellInfo contains the x,y coordinates. The point calculation is done to draw from the bottom left. The length is 1 in my sample.
Can someone help me out, what is the issue here. Let me know if you require anything from my side.
I am attempting to take a 'snapshot' of a videobrush display on a click event, but for some reason I cannot display this snapshot correctly.
double height = Application.Current.Host.Content.ActualHeight;
//Capture the screen
private void freeze_Click(object sender, EventArgs e)
{
// Takes the preview buffer pixel array from camera and returns
// an array of pixels representing the camera's preview buffer
int[] previewBuffer = GetPreviewBuffer();
//Process the preview image
int[] imageData = PhotofunDataContext.SelectedEffect.Effect.ProcessImage(previewBuffer);
//Copy to WriteableBitmap
//previewWriteableBitmap = new WriteableBitmap((int)camera.PreviewResolution.Width, (int)camera.PreviewResolution.Height);
WriteableBitmap freezeWriteableBitmap = new WriteableBitmap((int)camera.PreviewResolution.Width, (int)camera.PreviewResolution.Height);
imageData.CopyTo(freezeWriteableBitmap.Pixels, 0);
freezeWriteableBitmap.Invalidate();
//Display that on the screen
FreezeImage.Source = freezeWriteableBitmap;
//height of device
FreezeImage.Width = height;
//width to maintain 4:3 aspect ratio
FreezeImage.Height = height * (.75);
FreezeImage.Opacity = 1;
}
}
//Get the preview buffer
private int[] GetPreviewBuffer()
{
int[] pixelData = new int[(int)(camera.PreviewResolution.Width * camera.PreviewResolution.Height)];
//int[] pixelData = new int[(int)width * (int)height];
camera.GetPreviewBufferArgb32(pixelData);
return pixelData;
}
The above code is meant to take the snapshot of the videobrush and display it over the currently active videobrush as a freeze frame.
<Rectangle x:Name="videoRectangle" Width="1" Height="1" HorizontalAlignment="Center">
<Rectangle.Fill>
<VideoBrush x:Name="viewfinderBrush" >
<VideoBrush.RelativeTransform>
<CompositeTransform x:Name="viewfinderTransform" Rotation="90"
CenterX="0.5" CenterY="0.5"/>
</VideoBrush.RelativeTransform>
</VideoBrush>
</Rectangle.Fill>
</Rectangle>
<Image x:Name="FreezeImage" Opacity="0" HorizontalAlignment="Center" Stretch="Fill" Canvas.ZIndex="900">
<Image.Projection>
<PlaneProjection RotationZ="-90"/>
</Image.Projection>
</Image>
The FreezeImage is now overlayed the active videobrush, but the image dimensions do not match the phone dimensions. I would like to fit the FreezeImage, without stretching, to the entire phone screen, in Portrait mode.
I attempt to set FreezeImage.Width = height; and FreezeImage.Height = height * (.75); to maintain the camera's natural 4:3 aspect ratio when the image is sized to the phone screen, but this does not overwrite the previewbuffer and FreezeWriteableBitmap dimensions (640x480).
I also attempting hard coding the width(=1064) and height(=800) dimensions for the previewbuffer and FreezeWriteableBitmap but then the image is not displayed at all(its like the writeableBitmap will not accept any other value other than the camera's dimensions).
After playing around with the camera Initialization, I found that regardless of what I set the resolution to, the writeableBitmaps always default to 640x480 which is where I think the problem must be because it seems that this aspect ratio is what is actualy displayed on the phone screen. How can I fix this implementation so that the resulting freezeImage height matches the phone screen height and the width is adjusted so no stretching is performed?
Note: The height value when setting FreezeImage.Width = height; and FreezeImage.Height = height * (.75); is found by setting double height = Application.Current.Host.Content.ActualHeight; to account for future WP7 devices with other dimensions besides the current standard of 480x800.
I'm creating app for image scan with WIA. But my images has a lot of unused space if scanned document size is not big. I need to crop that unused space like in Paint with Cross cursor and rectangle. How to do that in WPF?
Code of image cropping is:
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea,bmpImage.PixelFormat);
return (Image)(bmpCrop);
}
Put the Image control in a Canvas and add an invisible rectangle to the canvas as well.
On left mouse button down set the top left of the rectangle at the mouse coordinates and make it visible.
On mouse move (and mouse button still down), set the size of the rectangle, so the opposite of the first corner moves with the mouse.
Example:
<Window x:Class="TestWpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="MainWindow"
Height="350"
Width="525">
<Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown"
MouseMove="Canvas_MouseMove"
Background="AntiqueWhite">
<Image Width="500"
Height="300" />
<Rectangle x:Name="selectionRectangle"
StrokeThickness="1"
Stroke="LightBlue"
Fill="#220000FF"
Visibility="Collapsed" />
</Canvas>
</Window>
And the code behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TestWpfApplication
{
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var mousePosition = e.GetPosition(sender as UIElement);
Canvas.SetLeft(selectionRectangle, mousePosition.X);
Canvas.SetTop(selectionRectangle, mousePosition.Y);
selectionRectangle.Visibility = System.Windows.Visibility.Visible;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mousePosition = e.GetPosition(sender as UIElement);
selectionRectangle.Width = mousePosition.X - Canvas.GetLeft(selectionRectangle);
selectionRectangle.Height = mousePosition.Y - Canvas.GetTop(selectionRectangle);
}
}
}
}
Note that the Canvas will only respond to a click when it has a color.
Also: you'll need to handle the situation where the user drags the selection right to left and/or bottom to top because that will introduce negative sizes for the width and height of the rectangle. But I'll leave that to you.
Thanks for this code. My actual issues realted to CROP iamge show in another iamge control. How ist possible in WPF. Please fallow this link.
http://social.msdn.microsoft.com/Forums/en-US/302bb1c8-e272-48b1-982d-12cf2aefe8a3/how-to-crop-image-show-in-another-image-control-?forum=wpf
Thank
I have an image in a scrollviewer.The image has Pinch in and out feature implemented on it.
But while scrolling the zoomed image,the aspect ratio changes and images becomes distorted.
Following the xaml:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Name="scroller" >
<Image Name="image_new" Visibility="Visible" CacheMode="BitmapCache" >
<Image.RenderTransform >
<CompositeTransform x:Name="transform"/>
</Image.RenderTransform >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Flick="OnFlick" PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta" DoubleTap="Onimage_doubletap" Tap="Onimage_singletap" />
</toolkit:GestureService.GestureListener>
</Image>
</ScrollViewer>
And in the .cs file the methods are :
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
Point point0 = e.GetPosition(image_new, 0);
Point point1 = e.GetPosition(image_new, 1);
Point midpoint = new Point((point0.X + point1.X) / 2, (point0.Y + point1.Y) / 2);
image_new.RenderTransformOrigin = new Point(midpoint.X / image_new.ActualWidth, midpoint.Y / image_new.ActualHeight);
initialScale = transform.ScaleX;
}
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
transform.ScaleX = Math.Max(Math.Min(initialScale * e.DistanceRatio, 3.0), 0.5);
transform.ScaleY = Math.Max(Math.Min(initialScale * e.DistanceRatio, 3.0), 0.5);
}
I think the problem here is that you are changing the RenderTransformOrigin for each pinch gesture, which is resulting in the distortion. I would try leaving the RenderTransformOrigin fixed at 0.5,0.5 to ensure that you get an even scale.
I assume you were moving the origin to try to zoom into/out of the part of the image that the user had started the gesture on. To achieve this, I think you will need to enable the user to pan around the image once zoomed in.
One other point, the scale factor is always the same, so you shoudl just calculate it once, and then assign it to both ScaleX and ScaleY.