I have an app in C# WF, where user move and zoom image. I want to draw ellipse on Image control because ellipse remains in place, and don't moves when I modify the image.
View:
<Grid>
<Canvas Name="cavRoot" Opacity="1">
<Image Name="highresmap4" Source="highresmap4.png" Canvas.Left="0" Canvas.Top="0" Width="1473" Height="770">
</Image>
</Canvas>
</Grid>
Model:
class draw
{
public static void circle(double x, double y, int width, int height, Canvas cv)
{
Ellipse circle = new Ellipse()
{
Width = width,
Height = height,
Stroke = Brushes.Red,
StrokeThickness = 6
};
cv.Children.Add(circle);
circle.SetValue(Canvas.LeftProperty, (double)x);
circle.SetValue(Canvas.TopProperty, (double)y);
}
}
ViewModel:
draw.circle(x, y, 10, 10, cavRoot);
You could put the Image in a Grid and use the Margin property of the Ellipse to specify its position within the Image:
public static void circle(double x, double y, int width, int height, Panel cv)
{
Ellipse circle = new Ellipse()
{
Width = width,
Height = height,
Stroke = Brushes.Red,
StrokeThickness = 6,
Margin = new Thickness(x, y, 0, 0)
};
cv.Children.Add(circle);
}
XAML:
<Grid x:Name="theGrid">
<Image Name="highresmap4" Source="highresmap4.png" Width="1473" Height="770" />
</Grid>
Related
I post this before and it was remove for being a duplicate. It is not. My problem is different then what that other people is doing. He is not doing zoom nor pan, and does not have a boarder.
I am using Stretch="Fill" to place my entire picture in the borders of an Image box. I am using a Border so that I can do Zoom and Pan. I am using the Canvas to draw rectangles around giving click areas. I want to map the left mouse click coordinates of the Canvas with zoom and pan back to the original image. here is my XAML code :
`
<Border x:Name="VideoPlayerBorder" ClipToBounds="True" Background="Gray" >
<Canvas x:Name="CanvasGridScreen" MouseLeftButtonDown="VideoPlayerSource_OnMouseLeftButtonDown" >
<Image x:Name="VideoPlayerSource" Opacity="1" RenderTransformOrigin="0.5,0.5" MouseLeftButtonUp="VideoPlayerSource_OnMouseLeftButtonUp" MouseWheel="VideoPlayerSource_OnMouseWheel" MouseMove="VideoPlayerSource_OnMouseMove" Width="{Binding Path=ActualWidth, ElementName=CanvasGridScreen}" Height="{Binding Path=ActualHeight, ElementName=CanvasGridScreen}" Stretch="Fill" >
</Image>
</Canvas>
`
here is my C# code:
`private void VideoPlayerSource_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
VideoPlayerSource.CaptureMouse();
var tt = (TranslateTransform)((TransformGroup)VideoPlayerSource.RenderTransform).Children.First(tr => tr is TranslateTransform);
start = e.GetPosition(VideoPlayerBorder);
origin = new Point(tt.X, tt.Y);
_stIR = start;
_stIR2 = start;
addRemoveItems(sender, e);
}
private void addRemoveItems(object sender, MouseButtonEventArgs e)
{
// this is the event that will check if we clicked on a rectangle or if we clicked on the canvas
// if we clicked on a rectangle then it will do the following
if (e.OriginalSource is Rectangle)
{
// if the click source is a rectangle then we will create a new rectangle
// and link it to the rectangle that sent the click event
Rectangle activeRec = (Rectangle)e.OriginalSource; // create the link between the sender rectangle
CanvasGridScreen.Children.Remove(activeRec); // find the rectangle and remove it from the canvas
}
// if we clicked on the canvas then we do the following
else
{
// generate a random colour and save it inside the custom brush variable
Custombrush = new SolidColorBrush(Color.FromRgb((byte)r.Next(1, 255),
(byte)r.Next(1, 255), (byte)r.Next(1, 233)));
// create a re rectangle and give it the following properties
// height and width 50 pixels
// border thickness 3 pixels, fill colour set to the custom brush created above
// border colour set to black
Rectangle newRec = new Rectangle
{
Width = 50,
Height = 50,
StrokeThickness = 3,
Fill = Custombrush,
Stroke = Brushes.Black
};
// once the rectangle is set we need to give a X and Y position for the new object
// we will calculate the mouse click location and add it there
Canvas.SetLeft(newRec, Mouse.GetPosition(CanvasGridScreen).X); // set the left position of rectangle to mouse X
Canvas.SetTop(newRec, Mouse.GetPosition(CanvasGridScreen).Y); // set the top position of rectangle to mouse Y
CanvasGridScreen.Children.Add(newRec); // add the new rectangle to the canvas
}
}
private void VideoPlayerSource_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
TransformGroup transformGroup = (TransformGroup)VideoPlayerSource.RenderTransform;
ScaleTransform transform = (ScaleTransform)transformGroup.Children[0];
double zoom = e.Delta > 0 ? .2 : -.2;
double transformScaleX = Math.Round((transform.ScaleX + zoom), 2);
double transformScaleY = Math.Round((transform.ScaleY + zoom), 2);
if (transformScaleX <= 8.2 && transformScaleX >= 1)
{
transform.ScaleX = Math.Round(transform.ScaleX + zoom, 2);
transform.ScaleY = Math.Round(transform.ScaleY + zoom, 2);
zoomFactor2 = zoomFactor2 + zoom;
zoomFactor = zoomFactor2;
}
}
void PanMethod(MouseEventArgs e)
{
var tt = (TranslateTransform)((TransformGroup)VideoPlayerSource.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector v = start - e.GetPosition(VideoPlayerBorder);
if (zoomFactor > 1.0)
{
tt.X = origin.X - v.X;
tt.Y = origin.Y - v.Y;
}
}
is there a function that would give me this information ? is there a way of using TransformGroup or ScaleTransform to return the actual location in the picture that was clicked? again the Image with possible zoom and/or pan
Check out: https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.visual.transformtovisual
The right way to translate coordinates back to the original pre-transforms control is to use the TransformToVisual helper. It's probably a good idea to do that regardless since transforms could be applied higher up in the stack.
In your case you want to call:
GeneralTransform transform = CanvasGridScreen.TransformToVisual(VideoPlayerSource);
Point normalizedPoint = transform.Transform(new Point(0, 0));
I am building an application to find dead pixels on the screen and I get a big problem that I can not solve by myself. My code uses to draw a rectangle on the screen at the point that user touch on.
My XAML code:
<Grid x:Name="mainGrid" Background="Gray">
<Canvas x:Name="myCanvas" Background="Purple" PointerMoved="digitizerGrid_PointerMoved" PointerReleased="digitizerGrid_PointerReleased">
<Grid x:Name="digitizerGrid" Visibility="Visible"/>
</Canvas>
</Grid>
and my c# code to handle event:
private void digitizerGrid_PointerMoved(object sender, PointerRoutedEventArgs e)
{
PointerPoint pt = e.GetCurrentPoint(myCanvas);
Point currentContactPt = pt.Position;
double x2 = currentContactPt.X;
double y2 = currentContactPt.Y;
Rectangle rect = new Rectangle()
{
Width = 50,
Height = 50,
StrokeThickness = 20.0,
Fill = new SolidColorBrush(Colors.Green)
};
Canvas.SetLeft(rect, x2);
Canvas.SetTop(rect, y2);
myCanvas.Children.Add(rect);
}
When I touch and fill all the screen like this:
I get the wrong result image below with black pixel blocks:
So I can not detect where is dead pixels. What I was wrong?
Thank in advance!
I try to change writablebitmap using writepixels() method, but it doesn't change any pixels.
it has following consructor
public void createWbm(int viewportW, int viewportH)
{
writeableBitmap = new WriteableBitmap(
viewportW,
viewportH,
96,
96,
PixelFormats.Indexed8,
new BitmapPalette(Form1.form1.getColors()));
i.Source = writeableBitmap;
}
and I use this method calling leftbuttondown event, but there is not any change. Is it necessary to use two loops(outer for row of pixels and inner for columns ) to paint every pixle or it is possible use just writepixels() method? thanks
void BrushPixel(MouseEventArgs e)
{
byte[] ColorData = { 0, 0, 0, 0 }; // B G R
Int32Rect rect = new Int32Rect(
(int)(e.GetPosition(i).X),
(int)(e.GetPosition(i).Y),
1,
1);
writeableBitmap.WritePixels( rect, ColorData, 4, 0);
}
You are incorrectly using it,
at this format (8-bit) your array ColorData either represents an 1x4, 4x1 or 2x2 pixels image.
therefore rect dimensions should match either of these sizes
Keep in mind that these are indices to colors in a palette, not BGR values as you've commented.
Here's a simple example :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap(100, 100, 96, 96, PixelFormats.Indexed8, BitmapPalettes.Halftone256);
int width = 50;
int height = 50;
var pixels = new byte[width*height];
var random = new Random();
random.NextBytes(pixels);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);
Image1.Source = bitmap;
}
}
XAML :
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<Grid>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Black"
BorderThickness="1">
<Image x:Name="Image1" Stretch="None" />
</Border>
</Grid>
</Window>
Note : at this format stride parameter always equals width because a pixel length in bytes is 1.
I strongly suggest you to use WriteableBitmapEx instead, it makes WriteableBitmap manipulation way easier. Beware that it supports only PixelFormats.Pbgra32 but unless you really have a specific reason to use 8-bit I can only recommend it, it can draw many primitives such as lines, rectangles, circles etc ...
I've got this function that just draws an ellipse and places it on the given grid
public void drawEllipse(double top, double left, double height, double width, Grid grid)
{
Ellipse ellipse = new Ellipse();
ellipse.Height = height;
ellipse.Width = width;
SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Black;
ellipse.Stroke = brush;
ellipse.Fill = brush;
Canvas.SetTop(ellipse, top);
Canvas.SetLeft(ellipse, left);
grid.Children.Add(ellipse);
}
However, for some reason, it only wants to place the ellipse in the center of the grid, or (given fourth quadrant arguments) the fourth quadrant of the grid.
Am I doing something wrong?
You are adding your ellipse to a Grid control, but you're setting the Canvas.Top and Canvas.Left properties. Without the ellipse actually being on a Canvas, those two properties don't do anything. Either add a Canvas and use Canvas.Children.Add instead of Grid.Children.Add, or change your Canvas.SetTop and Canvas.SetLeft calls with calls to Grid.SetRow and Grid.SetColumn.
I have two Image controls on each other and I set the alpha channel of some pixels to zero, from the upper one(this is the colorful). But after I "zoom" (width the ScaleTransform), a "border" will be visible around the pixels that have set. Here is a screenshot:
Here is the code:
<Grid Name="grdPhotos">
<Image Stretch="None" Source="picture_grayscale.jpg" Name="photo1" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Image Stretch="None" Source="picture.jpg" Name="photo2" MouseLeftButtonDown="photo2_MouseLeftButtonDown" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
private void photo2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var photo = photo2.Source as WriteableBitmap; // A WriteableBitmap is created before from the Source BitmapImage
for (int x = 100; x < 200; x++)
{
for (int y = 100; y < 200; y++)
{
int index = Convert.ToInt32(photo.PixelWidth * y + x);
if (index > 0 && index < photo.Pixels.Length)
SetPixelAlphaChannel(ref photo.Pixels[index], 0);
}
}
var transform = new ScaleTransform { ScaleX = 2, ScaleY = 2 };
photo1.RenderTransform = photo2.RenderTransform = transform;
}
public void SetPixelAlphaChannel(ref int pixel, byte value)
{
var color = ColorFromPixel(pixel);
if (color.A == value)
return;
color.A = value;
pixel = ColorToPixel(color);
}
private Color ColorFromPixel(int pixel)
{
var argbBytes = BitConverter.GetBytes(pixel);
return new Color { A = argbBytes[3], R = argbBytes[2], G = argbBytes[1], B = argbBytes[0] };
}
private int ColorToPixel(Color color)
{
var argbBytes = new byte[] { color.B, color.G, color.R, color.A };
return BitConverter.ToInt32(argbBytes, 0);
}
Why is this? Or how can I implement a zoom functionality without this "border"? Thanks a lot.
When you zoom an image, the pixel values will be interpolated, this will result in pixels in the border are you are observing being the result of interpolating your transparent pixels with their non transparent neighbours. Unfortunately you cannot control the interpolation behaviour of render transforms. You are going to have to do this yourself, possibly via WriteableBitmap.