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.
Related
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.
I am taking a photo with the camera, using it as the background for an InkCanvas, and letting the user draw overtop.
When the user saves their drawing, I am scaling the drawing to match the size of the image in the background. I can not resize the original image.
However, while the length and position of the strokes are being scaled, the widths are not.
Scrolling through StackOverflow I have found people saying that you also need to set the scale on the InkStroke.DrawingAttributes.PenTipTransform as well as the InkStroke.PointTransform, but when I try to set the value to the scale, the value doesn't change.
Origional Values
New Values
Any information on how I can set this value, or another way to o the same thing would be greatly appreciated.
Thanks
UI Code
<!-- Canvas -->
<Image Grid.Row="1" x:Name="imgImage" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" SizeChanged="imgImage_SizeChanged"/>
<InkCanvas Grid.Row="1" x:Name="inkCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" PointerExited="inkCanvas_PointerExited" Visibility="Collapsed">
<InkCanvas.RenderTransform>
<ScaleTransform x:Name="inkCanvasScaleTransform" />
</InkCanvas.RenderTransform>
</InkCanvas>
EDIT - More information
The image taken by the camera has the resolution 3264x244, the user can then draw on the image with the canvas, but I can't show the entire image on the screen, so it gets scaled down to fit the size of the canvas. The canvas (in this particular case), has a resolution of 1012x759.
When the image is saved, the location of the draw gets scaled up to match the full size of the image. but the width of the stroke does not.
P.S. the scale transform in the XAML is currently not used.
SAVE CODE
StorageFile inputFile = null;
inputFile = await StorageFile.GetFileFromPathAsync(_image.fullPath);
height = (int)inkCanvas.ActualHeight;
width = (int)inkCanvas.ActualWidth;
double scalex = 0;
double scaley = 0;
double offsety = 0;
double offsetx = 0;
double widthscale = 1;
if (inputFile != null)
{
var prop = await inputFile.Properties.GetImagePropertiesAsync();
offsetx = (double)prop.Width - width;
offsety = (double)prop.Height - height;
scalex = ((double)prop.Width - width) / width;
scaley = ((double)prop.Height - height) / height;
width = (int)prop.Width;
height = (int)prop.Height;
widthscale = 1 + ((scalex * scaley) / 2);
}
var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
List<InkStroke> ExpandedStrokes = new List<InkStroke>();
foreach (var stroke in strokes)
{
InkStroke strk = stroke.Clone();
Matrix3x2 scaleMatrix = Matrix3x2.CreateScale(1 + (float)scalex, 1 + (float)scaley);
strk.PointTransform = scaleMatrix;
strk.DrawingAttributes.PenTipTransform = scaleMatrix;
ExpandedStrokes.Add(strk);
}
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget renderTarget = new CanvasRenderTarget(device, width, height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Colors.White);
if (inputFile != null)
{
using (CanvasBitmap image = await CanvasBitmap.LoadAsync(device, inputFile.Path, 96))
{
ds.DrawImage(image);
}
}
ds.DrawInk(ExpandedStrokes);
}
using (var fileStream = await inputFile.OpenAsync(FileAccessMode.ReadWrite))
{
await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Jpeg, 1f);
}
The scale for the PenTip may not be correct, but was just trying to get it making a change.
EXAMPLE IMAGES
Before Scaling (1012x759)
After Scaling (3264x2448)
The InkStroke.PointTransform is for resizing the length of the strokes, it will not scale the strokes's thickness. If you want to display the strokes with corresponding width, you can use the InkCanvas.RenderTransform and set it's ScaleX and ScaleY, But it will not affect the real effect of the Stroke's thickness, it just adjusts the InkCanvas's size to make the Strokes seem to be scaled. There seems no a property to change the real thickness of strokes that have be drawn on the images.
Actually, when you take a photo and put it in the Image control as a background image, then the users can draw on the image, you maybe give the user a choice to select the Pen with different configuration to draw. After users finished drawing, they will save the image with strokes, this should be the effect they want. That is to say, we use the pen to draw on a small image, we should just make sure the stroke on the right position, if the image become larger, the strokes should change automatically, I don't think we should change the stroke scale again.
I want to change some pixels of an image. The image build action is set to "Resource". I'm not possible to get a Bitmap object, so I can make my changes with the for loop below.
Bitmap bmp = new Bitmap("pack://application:,,,/ovl_kommen.png");
//Change Black pixels to color
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (bmp.GetPixel(x,y) == System.Drawing.Color.Black)
{
bmp.SetPixel(x, y, System.Drawing.Color.Blue);
}
}
}
My next problem is, to view the modified bitmap to an user. I have a WPF image control img_kommen which I want to set the bitmap as source.
img_kommen.Source = bmp
Instead of manipulating pixel values, better simply use a filled Rectangle and apply your icon image as OpacityMask, because
This property only uses whatever the alpha channel value is for the supplied Brush. The other channels of the Brush's rendered content (Red, Green, or Blue) are ignored
Example:
<Rectangle x:Name="icon" Fill="Black" Width="100" Height="100">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="ovl_kommen.png" Stretch="Uniform"/>
</Rectangle.OpacityMask>
</Rectangle>
Then set the icon color in code behind like:
icon.Fill = Brushes.Red;
I have a webbrowser which is in portrait mode. I am loading the webbrowser using
this.webBrowserControl.Source = new Uri("http://google.com");
When the content is loaded the view looks like this
there is rotate button provided, so if user is in portrait mode and user presses rotate button then I am rotating the webview using transform and setting its height and width so that it can occupy the whole screen
pageWidth = 480;
pageHeight = 800;
RotateTransform transform = new RotateTransform();
transform.Angle = 90;
this.webbrowser.RenderTransform = transform;
webbrowser.Width = pageHeight;
webbrowser.Height = pageWidth;
The webbrowser is rotated but the contents looks like zoomed or stretched, it looks like this
Any idea how not to zoom the content??
Edit:
Change Zoom percentage:
// figure out the ratio you want to reduce by
string zoom_js = "document.body.style.zoom=\"50%\";";
try
{
this.myBrowser.IsScriptEnabled = true;
this.myBrowser.InvokeScript("eval", zoom_js);
}
catch(Exception ex)
{
}
It is not zooming anything. It just that you are rotating it then stretching (resizing) the container.
Given your resolution, you have to have keep the aspect ratio if you don't want any stretching to be done.
<phone:WebBrowser x:Name="myBrowser" RenderTransformOrigin="0.5,0.5" Height="480" Width="480" Margin="0" Padding="0">
<phone:WebBrowser.RenderTransform>
<CompositeTransform Rotation="90"/>
</phone:WebBrowser.RenderTransform>
</phone:WebBrowser>
</Border>
Now that won't be stretched but it also not cover the entire screen. Now, imagine stretching the container like you did. The result will be your image you posted.
It seems like variations of this question have been asked before, but not this specifically. Also, it seems that BitmapImages are different from straight Bitmaps. So here we go:
I've got a BitmapImage x:Name="HeightMapImage" that is pointing to an Image x:Name="image" that is inside ContentPresenter x:Name="contentPresenter" that is in Viewbox x:Name="viewBox". I want to draw both semi-transparently and non-transparently at specific X,Y coordinates on the HeightMapImage.
The reason for this set-up is that the BitmapImage is being scrolled and zoomed. When I draw on the BitmapImage at X,Y I want that to automatically scroll and zoom, too.
I'm a very old geek having written for many machines in many different GDIs over the years. This seems like a 'get the handle' to some graphic device context problem and once I've got it I can merrily draw away.
Your help is greatly appreciated.
Somebody wanted to see the code. Here's the XAML:
<Viewbox x:Name="viewBox" Margin="0,0,0,0">
<ContentPresenter x:Name="contentPresenter" Width="350" Height="350" >
<ContentPresenter.Content>
<Image x:Name="image" Width="350" Height="350">
<Image.Source>
<BitmapImage x:Name="HeightMapImage" UriSource="DinoIslandLogo.bmp" />
</Image.Source>
</Image>
</ContentPresenter.Content>
</ContentPresenter>
</Viewbox>
And here's a screen capture that somebody wanted:
And here's the code that gets the user's selected bitmap and loads and displays it:
string selectedFileName = dlg.FileName;
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(selectedFileName);
bitmap.EndInit();
image.Source = bitmap;
Does this need to be rewritten for a Writeable Bitmap?
You may use a WriteableBitmap instead of (or actually in addition to) a BitmapImage. First create your BitmapImage as usual (but with less code):
var selectedFileName = dlg.FileName;
var bitmap = new BitmapImage(new Uri(selectedFileName));
Then create a WritableBitmap from the BitmapImage and assign that to the Image control:
var writeableBitmap = new WriteableBitmap(bitmap);
image.Source = writeableBitmap;
Now you may modify the WriteableBitmap in order to draw your overlay data. The following code snippet shows how to get and modify a pixel in the bitmap:
if (writeableBitmap.Format.BitsPerPixel == 32)
{
var x = 10;
var y = 20;
var pixelRect = new Int32Rect(x, y, 1, 1);
var pixelBuffer = new byte[4];
writeableBitmap.CopyPixels(pixelRect, pixelBuffer, 4, 0);
// modify pixelBuffer and write it back
writeableBitmap.WritePixels(pixelRect, pixelBuffer, 4, 0);
}
EDIT: A suggestion for a SetPixel method that takes the overlay color alpha value into account. Please note that this method assumes that the bitmap's pixel format is Bgr32.
public void SetPixel(WriteableBitmap wb, int x, int y, Color color)
{
var pixelRect = new Int32Rect(x, y, 1, 1);
var pixelBuffer = new byte[4];
wb.CopyPixels(pixelRect, pixelBuffer, 4, 0);
pixelBuffer[0] = (byte)(pixelBuffer[0] * (1F - color.ScA) + color.B * color.ScA);
pixelBuffer[1] = (byte)(pixelBuffer[1] * (1F - color.ScA) + color.G * color.ScA);
pixelBuffer[2] = (byte)(pixelBuffer[2] * (1F - color.ScA) + color.R * color.ScA);
wb.WritePixels(pixelRect, pixelBuffer, 4, 0);
}
Please note also that it is a lot more efficient to set a larger number of pixels in one go. Ideally you would set all overlay pixels at once. You would copy all pixel values into one large array, calculate their new RGB values as shown above, and then write them all back at once.