Is there a package that does screen capture in xamarin.forms ?
I need also to capture google maps screen shots
Check out this blog post by Daniel Hindrikes.
I'm going to assume that you use a PCL for your shared code.
You will need to create an interface in your PCL. He calls it IScreenshotManager. The declaration looks like this:
public interface IScreenshotManager
{
Task<byte[]> CaptureAsync();
}
Now all platforms will have their own implementation for it.
For iOS;
public class ScreenshotManager : IScreenshotManager
{
public async System.Threading.Tasks.Task<byte[]> CaptureAsync()
{
var view = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
UIGraphics.BeginImageContext(view.Frame.Size);
view.DrawViewHierarchy(view.Frame, true);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
using(var imageData = image.AsPNG())
{
var bytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
return bytes;
}
}
}
For Android:
public class ScreenshotManager : IScreenshotManager
{
public static Activity Activity { get; set; }
public async System.Threading.Tasks.Task<byte[]> CaptureAsync()
{
if(Activity == null)
{
throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
}
var view = Activity.Window.DecorView;
view.DrawingCacheEnabled = true;
Bitmap bitmap = view.GetDrawingCache(true);
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
}
And for Windows Phone:
public class ScreenshotManager : IScreenshotManager
{
public async Task<byte[]> CaptureAsync()
{
var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
var screenImage = new WriteableBitmap((int)rootFrame.ActualWidth, (int)rootFrame.ActualHeight);
screenImage.Render(rootFrame, new MatrixTransform());
screenImage.Invalidate();
using (var stream = new MemoryStream())
{
screenImage.SaveJpeg(stream, screenImage.PixelWidth, screenImage.PixelHeight, 0, 100);
var bytes = stream.ToArray();
return bytes;
}
}
}
Don't forget to register your platform specific implementations with the attribute which registers it with the Dependency Service, like this:
[assembly: Xamarin.Forms.Dependency (typeof (ScreenshotManager))]
It goes above the namespace declaration.
Now from your shared code you would be able to get the byte[] of a screenshot with a call like this:
var screenshotBytes = DependencyService.Get<IScreenshotManager>().CaptureAsync();
You probably want to check if DependencyService.Get<IScreenshotManager>() isn't null before using it.
After that you can turn your byte[] into an image and do whatever you like with it!
Implementation for UWP
public async Task<byte[]> CaptureAsync()
{
//create and capture Window
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(Window.Current.Content);
var pixelpuffer = await renderTargetBitmap.GetPixelsAsync();
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
IRandomAccessStream stream = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight, logicalDpi, logicalDpi, pixelpuffer.ToArray());
await encoder.FlushAsync();
byte[] resultingBuffer = new byte[stream.Size];
await stream.ReadAsync(resultingBuffer.AsBuffer(), (uint)resultingBuffer.Length, InputStreamOptions.None);
return resultingBuffer;
}
Related
I am working on a web based OHIF Dicom viewer. I am using clear canvas as my PACs server. So I developed one broker application in .net core which works like WADO-RS and supply information to OHIF viewer from clear canvas. In my broker application I am passing metadata to OHIF viewer in json format by using FO-dicom json converter which converts dcm file into Json string.
My code for sending metadata:
var files = Directory.GetFiles(directory);
List<JObject> lstjo = new List<JObject>();
JObject jo;
foreach (var file in files)
{
var dicomDirectory = DicomFile.Open(file, FileReadOption.ReadAll);
if (dicomDirectory.Dataset.InternalTransferSyntax.UID.UID != DicomTransferSyntax.ImplicitVRLittleEndian.UID.UID)
{
var transcoder = new DicomTranscoder(dicomDirectory.Dataset.InternalTransferSyntax, DicomTransferSyntax.ImplicitVRLittleEndian);
dicomDirectory = transcoder.Transcode(dicomDirectory);
}
JsonDicomConverter dicomConverter = new JsonDicomConverter();
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
JsonWriter writer = new JsonTextWriter(sw);
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
dicomConverter.WriteJson(writer, dicomDirectory.Dataset, serializer);
jo = JObject.Parse(sb.ToString());
// jo.Property("7FE00010").Remove();
retJsonstring += jo.ToString() + ",";
}
if (retJsonstring.Length > 6)
{
retJsonstring = retJsonstring.Substring(0, retJsonstring.Length - 1) + "]";
}
else
{
retJsonstring += "]";
}
retJsonstring = retJsonstring.Replace("\r", "").Replace("\n", "");
}
return retJsonstring;
During passing metadata there is no issue in Ohif viewer. After then OHIF viewer sending WADORS request for frames to display. My broker application also send responding for that request in multipart.
My code for sending Multipart response :
{
var dicomFile = DicomFile.Open(path, FileReadOption.ReadAll);
string transfersyntax = dicomFile.Dataset.InternalTransferSyntax.UID.UID;
MemoryStream streamContent = new MemoryStream();
if (transfersyntax != DicomTransferSyntax.ImplicitVRLittleEndian.UID.UID)
{
var transcoder = new DicomTranscoder(dicomFile.Dataset.InternalTransferSyntax, DicomTransferSyntax.ImplicitVRLittleEndian);
dicomFile = transcoder.Transcode(dicomFile);
}
dicomFile.Save(streamContent);
DicomImage img = new DicomImage(dicomFile.Dataset, 0);
streamContent.Seek(0, SeekOrigin.Begin);
string boundary = Guid.NewGuid().ToString();
MultipartContent multipartContent = new MultipartContent();
//newFile.Save(multipartContent.Stream);
multipartContent.Stream = streamContent;// File.OpenRead(path);
multipartContent.ContentType = "application/octet-stream";
multipartContent.transfersyntax = dicomFile.Dataset.InternalTransferSyntax.UID.UID;
multipartContent.FileName = "";
multiContentResult = new MultipartResult("related", boundary) { multipartContent };
return multiContentResult;
}
Mulitpart class and MulticontentResult Class:
public class MultipartContent
{
public string ContentType { get; set; }
public string FileName { get; set; }
public Stream Stream { get; set; }
public string transfersyntax { get; set; }
}
public class MultipartResult : Collection<MultipartContent>, IActionResult
{
private readonly System.Net.Http.MultipartContent content;
public MultipartResult(string subtype = "byteranges", string boundary = null)
{
if (boundary == null)
{
this.content = new System.Net.Http.MultipartContent(subtype);
}
else
{
this.content = new System.Net.Http.MultipartContent(subtype, boundary);
}
}
public async Task ExecuteResultAsync(ActionContext context)
{
foreach (var item in this)
{
if (item.Stream != null)
{
var content = new StreamContent(item.Stream);
if (item.ContentType != null)
{
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(item.ContentType);
content.Headers.ContentType.Parameters.Add(new System.Net.Http.Headers.NameValueHeaderValue("transfer-syntax", item.transfersyntax));
}
this.content.Add(content);
}
}
context.HttpContext.Response.ContentLength = content.Headers.ContentLength;
context.HttpContext.Response.ContentType = content.Headers.ContentType.ToString();
await content.CopyToAsync(context.HttpContext.Response.Body);
}
}
After sending WAROrs response , I getting error in OHIF viewer in RangeError: offset is out of bounds in stackviewport.js during set pixeldata flat32array to scaledata flat32array like below Image
So I inspect in browser after then I come know that Pixel data size and scaledata size is different like below image..
To verify my dcm file I checked with ohif viewer by directly opened those file in https://v3-demo.ohif.org/local. It is opening properly.
So what are possible reason for this issue ? how to rectify?
In my app I need to pass images from one page to another page image view to display. I am taking a photo from camera and do some stuffs, then I want to send that images to the second page.
if (await isCamAvailable())
{
MediaFile photo1 = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions() { Directory = "NewBusiness", Name = "image1.jpg", PhotoSize = PhotoSize.MaxWidthHeight, MaxWidthHeight = 1024, CompressionQuality = 95 });
if (photo1 != null)
{
PhotoImage1.Source = ImageSource.FromStream(() => { return photo1.GetStream(); });
countList.Remove("a");
countList.Add("a");
}
}
Then I am added it to a string array by doing
private List<string> sendImgList = new List<string>();
sendImgList.Add(createImgByteString(photo1.GetStream()));
private string createImgByteString(Stream data)
{
var bytes = new byte[data.Length];
return Convert.ToBase64String(bytes);
}
Then from second page (for testing i just added only one image)
foreach (string ss in imgList) {
byte[] Base64Stream = Convert.FromBase64String(ss);
imgView.Source = ImageSource.FromStream(() => new MemoryStream(Base64Stream));
}
I followed this example. But image not showing.
https://forums.xamarin.com/discussion/139360/how-to-transfer-images-from-one-page-to-another
Also getting this in logcat..
[0:] ImageLoaderSourceHandler: Image data was invalid: Xamarin.Forms.StreamImageSource05-29 14:22:43.758 W/monodroid-assembly( 8737): typemap: unable to find mapping to a Java type from managed type 'System.Byte, mscorlib'
It seems that you used the Media.Plugin . Why don't you pass the ImageSource directly?
If you do want to convert it to byte array , check the following code
public byte[] GetImageStreamAsBytes(Stream input)
{
var buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
var imgDate = GetImageStreamAsBytes(photo1.GetStream());
It would be better to pass the byte array directly .
The best mode to pass parameter in pages is with Prism.
https://prismlibrary.com/docs/xamarin-forms/navigation/passing-parameters.html
>
_navigationService.NavigateAsync(new Uri("MainPage", new NavigationParameters
{
{ "key_parameter", image }
})));
And on other page:
>
public override void OnNavigatedTo(INavigationParameters parameters)
{
image = (Image)parameters["key_parameter"];
}
I have the following two methods that handles taking photos from a camera and picking photos from a library. They're both similar methods as at the end of each method, I get an ImageSource back from the Stream and I pass it onto another page which has an ImageSource binding ready to be set. These two method work perfectly. The next step now is to save the Image in SQLite so I can show the images in a ListView later on. My question for the XamGods (Xamarin Pros =), what is the best way to save image in SQLite in 2019? I have been in the forums for hours and I still don't have a tunnel vision on what I want to do. I can either
Convert Stream into an array of bytes to save in Sqlite.
Convert ImageSource into an array of bytes (messy/buggy).
Somehow retrieve the actual Image selected/taken and convert that into an array of bytes into SQLite
I'm sorry if my question is general, but Xamarin does not provide a clear-cut solution on how to save images in SQLite and you can only find bits and pieces of solutions throughout the forums listed below.
How to save and retrieve Image from Sqlite
Load Image from byte[] array.
Creating a byte array from a stream
Thank you in advance!
private async Task OnAddPhotoFromCameraSelected()
{
Console.WriteLine("OnAddPhotoFromCameraSelected");
var photo = await Plugin.Media.CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions() { });
var stream = photo.GetStream();
photo.Dispose();
if (stream != null)
{
ImageSource cameraPhotoImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", cameraPhotoImage);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
private async Task OnAddPhotoFromLibrarySelected()
{
Console.WriteLine("OnAddPhotoFromLibrarySelected");
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
ImageSource selectedImage = ImageSource.FromStream(() => stream);
var parms = new NavigationParameters();
parms.Add("image", selectedImage);
parms.Add("stream", stream);
var result = await NavigationService.NavigateAsync("/AddInspectionPhotoPage?", parameters: parms);
if (!result.Success)
{
throw result.Exception;
}
}
}
As Jason said that you can save image path into sqlite database, but if you still want to save byte[] into sqlite database, you need to convert stream into byte[] firstly:
private byte[] GetImageBytes(Stream stream)
{
byte[] ImageBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
ImageBytes = memoryStream.ToArray();
}
return ImageBytes;
}
Then load byte[] from sqlite, converting into stream.
public Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
For simple sample, you can take a look:
Insert byte[] in sqlite:
private void insertdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
Image image = new Image();
image.Content = ConvertStreamtoByte();
var result = con.Insert(image);
sl.Children.Add(new Label() { Text = result > 0 ? "insert successful insert" : "fail insert" });
}
}
Loading image from sqlite:
private void getdata()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "sqlite1.db3");
using (var con = new SQLiteConnection(path))
{
var image= con.Query<Image>("SELECT content FROM Image ;").FirstOrDefault();
if(image!=null)
{
byte[] b = image.Content;
Stream ms = new MemoryStream(b);
image1.Source = ImageSource.FromStream(() => ms);
}
}
}
Model:
public class Image
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string FileName { get; set; }
public byte[] Content { get; set; }
}
I'm new to Xamarin.Forms and I'm making a Listview that needs to update every time I insert new information in the database, so far I can display the info of my list and add it via a PHP file but I can't make it refresh automatically.
namespace Proyect
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Alarms : ContentPage
{
public Alarms ()
{
InitializeComponent();
AlarmsList.ItemTemplate = new DataTemplate(typeof(Cells.AlarmsCell)); //Template of the Alarms
this.LoadAlarms();
}
private async void LoadAlarms()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("Http://192.168.0.13");
string url = string.Format("/Proyect/alarmscode.php?");
var response = await client.GetAsync(url);
var result = response.Content.ReadAsStringAsync().Result;
var jsonalarms = JsonConvert.DeserializeObject<ObservableCollection<GetAlarms>>(result);
AlarmsList.ItemsSource = jsonalarms;
}
catch (Exception e)
{
await DisplayAlert("ERROR", e + "", "OK");
return;
}
}
}
}
Can you try to keep the same ObservableCollection and update its content instead of setting a new ObservableCollection every time?
namespace Proyect
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Alarms : ContentPage
{
private ObservableCollection<GetAlarms> _itemsSource = null;
public Alarms()
{
InitializeComponent();
AlarmsList.ItemTemplate = new DataTemplate(typeof(Cells.AlarmsCell)); //Template of the Alarms
_itemsSource = new ObservableCollection<GetAlarms>();
AlarmsList.ItemsSource = _itemsSource;
this.LoadAlarms();
}
private async void LoadAlarms()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("Http://192.168.0.13");
string url = string.Format("/Proyect/alarmscode.php?");
var response = await client.GetAsync(url);
var result = response.Content.ReadAsStringAsync().Result;
var jsonalarms = JsonConvert.DeserializeObject<ObservableCollection<GetAlarms>>(result);
_itemsSource.Clear();
foreach (var alarm in jsonalarms)
{
_itemsSource.Add(alarm);
}
}
catch (Exception e)
{
await DisplayAlert("ERROR", e + "", "OK");
return;
}
}
}
}
Device.StartTimer (new TimeSpan (0, 0, 10), () => {
// do something every 10 seconds
return true; // runs again, or false to stop
});
I have used http://www.strathweb.com/2013/01/asynchronously-streaming-video-with-asp-net-web-api/ this technique before and worked perfect for async video streaming.
But for ASP.NET Core this way is not working as expected.
By Video streaming class is:
public class VideoStream
{
private readonly string _filename;
public VideoStream(string filename)
{
_filename = filename;
}
public async Task WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var video = File.Open(_filename, FileMode.Open, FileAccess.Read))
{
var length = (int)video.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = video.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
length -= bytesRead;
}
}
}
catch (Exception)
{ return; }
finally
{
outputStream.Flush();
outputStream.Dispose();
}
}
}
and I have the following Action for video streaming requests:
[HttpGet]
[Route("[action]")]
public IActionResult GetVideo(int id)
{
var fileName = GetVideoFileName(id);
var video = new VideoStream(fileName);
var response = new HttpResponseMessage
{
Content = new PushStreamContent(video.WriteToStream, new MediaTypeHeaderValue("video/mp4"))
};
var objectResult = new ObjectResult(response);
objectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("video/mp4"));
return objectResult;
}
Since by default Asp.Net Core doesn't have built-in Media Formatter for video/mp4 I have created the following custom Media Formatter
public class VideoOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterCanWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
return true;
}
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
var response = context.HttpContext.Response;
response.ContentType = "video/mp4";
How to impelemnt ???
}
}
and added the following line to Startup.cs
services.AddMvc(options =>
{
options.OutputFormatters.Add(new VideoOutputFormatter());
});
It actually calls my custom formatter.
I doesn't know how to implement this custom media formatter for video/mp4.
Anyone can help me ?
Looking at the source code for Asp.NET Core really helped me find the answer to this one. They have a StreamOutputFormatter class that's really close to what you want to use. I only had to modify it to look for PushStreamContent and it worked like a charm.
Here's my complete VideoOutputFormatter:
public class VideoOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterCanWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (context.Object is PushStreamContent)
return true;
return false;
}
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
using (var stream = ((PushStreamContent)context.Object))
{
var response = context.HttpContext.Response;
if (context.ContentType != null)
{
response.ContentType = context.ContentType.ToString();
}
await stream.CopyToAsync(response.Body);
}
}
}
Instead of wrapping the HttpResponseMessage in the ObjectResult in your controller, you'll want to just shove the PushStreamContent object into the ObjectResult instead. You still need to set the MediaTypeHeaderValue on the ObjectResult.