MAUI: How to draw image from byte[] on MAUI - c#

I'm try to use maui develop an application now, but I meet a problem:
MAUI document just offers the way to draw the image from a file, link is here: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/graphics/draw?view=net-maui-7.0#draw-an-image
In my scenario, I will get the yuv/rgb byte[], not the image file, but the PlatformImage just can create the IImage object from an image file that has been encoded. And ICanvas just needs to use IImage object to draw an image.
And PlatformImage isn't support Windows.
using Microsoft.Maui.Graphics.Platform;
...
IImage image;
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("GraphicsViewDemos.Resources.Images.dotnet_bot.png"))
{
image = PlatformImage.FromStream(stream);
}
if (image != null)
{
canvas.DrawImage(image, 10, 10, image.Width, image. Height);
}
So, my question is, is there any way to draw the byte[] into View?
I have tried Microsoft.Maui.Graphics.Skia, but it cannot work.
This is my codes:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Graphics.Skia;
using Render.Source;
using SkiaSharp;
namespace MauiSample
{
internal class VideoDrawable : IDrawable, IDisposable
{
private SKBitmap? _sKBitmap;
private SkiaImage? _skiaImage;
private Microsoft.Maui.Graphics.IImage? _image;
private uint _width;
private uint _height;
private byte[]? _buffer;
public void Draw(ICanvas canvas, RectF dirtyRect)
{
if (_image is null)
{
return;
}
canvas.DrawImage(_image, dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height);
}
private void EnsureBitmap(uint width, uint height)
{
bool needResize = false;
if (_width != width)
{
needResize = true;
_width = width;
}
if (_height != height)
{
needResize = true;
_height = height;
}
if (_sKBitmap is null)
{
_sKBitmap = new SKBitmap((int)width, (int)height, SKColorType.Rgb888x, SKAlphaType.Premul);
}
else if (needResize)
{
_sKBitmap.Resize(new SKImageInfo((int)width, (int)height), SKFilterQuality.None);
}
if (_skiaImage is null)
{
_skiaImage = new SkiaImage(_sKBitmap);
_image = _skiaImage;
}
else if (needResize)
{
_skiaImage.Resize(width, height);
}
if (needResize)
{
_buffer = new byte[width * height << 2];
}
}
public void DrawVideoFrame(byte[] yuvFrame, uint width, uint height)
{
EnsureBitmap(width, height);
byte[] pixels = new byte[width * height * 4];
VideoFrameConverter.YUV2RGBA(yuvFrame, pixels, width, height);
unsafe
{
fixed (byte* p = pixels)
{
_sKBitmap.SetPixels((nint)p);
}
}
}
#region IDisposable
private bool _disposed = false;
// Use C# finalizer syntax for finalization code.
// This finalizer will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide finalizer in types derived from this class.
~VideoDrawable()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(disposing: false) is optimal in terms of
// readability and maintainability.
Dispose(disposing: false);
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(disposing: true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SuppressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!_disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
_skiaImage.Dispose();
_sKBitmap.Dispose();
}
// Note disposing has been done.
_disposed = true;
}
}
#endregion
}
}

Related

Recording Video from Webcam with OpenCvSharp - Resulting File playback to fast

I am trying to record video from a webcam using OpenCvSharp
I can already record the video using the code below but the resulting .mp4 file plays way to fast (e.g. i record for 5 seconds and the result isn't even one second long).
I already played with the delay in AddCameraFrameToRecordingThread but to no avail
What can possibly be the problem? Or what other library can I use to record a video from webcam?
namespace BlackBears.Recording
{
using System;
using System.Drawing;
using System.Threading;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;
public class Recorder : IDisposable
{
private readonly VideoCaptureAPIs _videoCaptureApi = VideoCaptureAPIs.DSHOW;
private readonly ManualResetEventSlim _writerReset = new(false);
private readonly VideoCapture _videoCapture;
private VideoWriter _videoWriter;
private Thread _writerThread;
private bool IsVideoCaptureValid => _videoCapture is not null && _videoCapture.IsOpened();
public Recorder(int deviceIndex, int frameWidth, int frameHeight, double fps)
{
_videoCapture = VideoCapture.FromCamera(deviceIndex, _videoCaptureApi);
_videoCapture.Open(deviceIndex, _videoCaptureApi);
_videoCapture.FrameWidth = frameWidth;
_videoCapture.FrameHeight = frameHeight;
_videoCapture.Fps = fps;
}
/// <inheritdoc />
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
~Recorder()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
StopRecording();
_videoCapture?.Release();
_videoCapture?.Dispose();
}
}
public void StartRecording(string path)
{
if (_writerThread is not null)
return;
if (!IsVideoCaptureValid)
ThrowHelper.ThrowVideoCaptureNotReadyException();
_videoWriter = new VideoWriter(path, FourCC.XVID, _videoCapture.Fps, new Size(_videoCapture.FrameWidth, _videoCapture.FrameHeight));
_writerReset.Reset();
_writerThread = new Thread(AddCameraFrameToRecordingThread);
_writerThread.Start();
}
public void StopRecording()
{
if (_writerThread is not null)
{
_writerReset.Set();
_writerThread.Join();
_writerThread = null;
_writerReset.Reset();
}
_videoWriter?.Release();
_videoWriter?.Dispose();
_videoWriter = null;
}
private void AddCameraFrameToRecordingThread()
{
var waitTimeBetweenFrames = (int)(1_000 / _videoCapture.Fps);
using var frame = new Mat();
while (!_writerReset.Wait(waitTimeBetweenFrames))
{
if (!_videoCapture.Read(frame))
return;
_videoWriter.Write(frame);
}
}
}
}
I found a solution myself after a lot more playing around.
A single thread to capture the frames and write them was not enough.
I now created two threads, one that captures the frames from the camera and one that writes them. To have the correct timing in the resulting file the delay the write creates has to be taken into account.
I ended up with the following two functions that run in separate threads:
private void CaptureFrameLoop()
{
while (!_threadStopEvent.Wait(0))
{
_videoCapture.Read(_capturedFrame);
}
}
private void AddCameraFrameToRecordingThread()
{
var waitTimeBetweenFrames = 1_000 / _videoCapture.Fps;
var lastWrite = DateTime.Now;
while (!_threadStopEvent.Wait(0))
{
if (DateTime.Now.Subtract(lastWrite).TotalMilliseconds < waitTimeBetweenFrames)
continue;
lastWrite = DateTime.Now;
_videoWriter.Write(_capturedFrame);
}
}

Memory will not release in C# singleton mode

I have a puzzle about singleton mode freeing object memory in C# between in C++;
Here C++ Code:
#include<iostream>
using namespace std;
class Rocket
{
private:
Rocket() { speed = 100; }
~Rocket() {}
static Rocket* ms_rocket;
public:
int speed;
static Rocket*ShareRocket()
{
if (ms_rocket == NULL)
ms_rocket = new Rocket();
return ms_rocket;
}
static void Close()
{
if (ms_rocket != NULL)
delete ms_rocket;
}
};
Rocket *Rocket::ms_rocket = NULL;
int main()
{
Rocket* p = Rocket::ShareRocket();
p->speed = 100;
cout << p->speed << endl;
Rocket::Close();
cout << p->speed << endl;
getchar();
}
When I use Rocket::Close(), the memory space which ms_rocket pointed to will be freed, ms_rocket become a wild pointer, and the second "cout<age<<endl" show is not 100, but when I use C# , I also use Dispose(), but still show 100. here the C# code:
class A : IDisposable
{
public int age;
public A() { }
public void Run()
{
Console.WriteLine("Run");
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Console.WriteLine("A is release");
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
class B
{
static A a;
private B() { }
public static A Intance
{
get
{
if (a == null)
a = new A();
return a;
}
}
}
class Class1
{
public static void Main(string[] args)
{
A a = B.Intance;
a.age =100;
Console.WriteLine(a.age);
a.Dispose();
A a1 = B.Intance;
Console.WriteLine(a1.age);
Console.Read();
}
}
In C#, I think when I use Dispose(), the memory('a' object in B singleton) will be released, but in the second access, the age value should not be 100, and the static variable 'a' will become like a wild pointer.
Who can tell me why?
In C# Dispose mainly is used to release unmanaged resources as soon as they are not needed and not to free the memory occupied by object itself - it is handled by garbage collector which will free (when GC will decide that it needs to run) it only when it will become unaccessible from so called GC roots (and static variables are one of the GC roots, so B.Intance will hold the reference to this instance of A in the heap).
So first of all to free the memory taken by current instance of A you will need to set B.Instance to null (and wait for GC to run).
Also fundamentals of garbage collection in CLR can be useful.

What is the correct way to dispose of SafeMemoryMappedViewHandle when AcquirePointer is used?

I have some code which opens a memory mapped file and exposes a ReadOnlySlice<T> for a number of parsing operations. Simplified class code below:
public MemoryMappedViewAccessor Accessor { get; }
public SafeMemoryMappedViewHandle Handle { get; }
public byte* Memory;
private long _size;
public Parser(MemoryMappedFile mappedFile, long offset, long size)
{
_size = size;
Accessor = mappedFile.CreateViewAccessor(offset, _size, MemoryMappedFileAccess.Read);
Handle = Accessor.SafeMemoryMappedViewHandle;
unsafe
{
Handle.AcquirePointer(ref Memory);
}
}
public ReadOnlySlice<T> GetSpan<T>(int offset, int size)
{
return new ReadOnlySpan<T>(chunk.Memory, _size).Slice(offset, size);
}
/* other functions exposing various Slice<T> over this */
Obviously this requires my class to implement IDisposable, but I am unsure as to where the boundary lies between unsafe and safe resources. One reference I found says that SafeMemoryMappedViewHandle is a managed resource and should be disposed as such, but it doesn't mention how this changes with ReleasePointer, or whether ReleasePointer should even be called before Dispose.
The way I see it, the following two Dispose patterns are my options:
Option 1 - Treat everything as managed
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Accessor.Dispose();
Handle.ReleasePointer(); // is this even needed?
Handle.Dispose();
}
_isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
Option 2 - Treat pointer release as unmanaged
private bool _isDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Accessor.Dispose();
}
Handle.ReleasePointer();
Handle.Dispose();
_isDisposed = true;
}
}
~FileChunk()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
In both of these cases I would add a check to my parsing logic to throw an ObjectDisposedException if _isDisposed is true, to avoid UAF bugs and memory corruption.
Which of these is the correct dispose pattern when unsafe pointers are exposed from a SafeMemoryMappedViewHandle? Additionally, is it worth setting the Memory field to null during disposal?

Drawing a CachedBitmap in Winforms

I try to display a cached Bitmap in Winforms (for performance reasons). I have a problem cause I cant draw it.
The exaple in this answer https://stackoverflow.com/a/6474581/1676819 says that there should be something like
graphics.DrawCachedBitmap(bitmap, 0, 0);
I cant find it.
What I've done so far:
I added Presentationcore.dll as reference
I created a CachedBitmap
CachedBitmap tempCBm = new CachedBitmap(new BitmapImage(new Uri(#"D:\test.bmp")),BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
I tried to draw it with the standard method (causes error)
private void CustomPaint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(tempCBm, 0,0);//error
}
Can somebody tell me what am I´m doing wrong? Many thanks in advance.
CachedBitmap is not available through .NET. It is a feature of GDI+. Use Bitmap instead. If you need to optimize for performance, then you can use the unsafe context in C# for faster bitmap access.
A nice tutorial is available here: http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp.
It is possible to use cached bitmap - but for some reason it is not available in standard C# api. You can circumvent around that however - make a managed C++ library that will encapsulate methods you want to expose to C#.
See my github repo - https://github.com/svejdo1/CachedBitmap
C++ utility class to expose cached bitmap
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
namespace CachedBitmapUtility {
public ref class BitmapUtility {
public:
static void* CreateCachedBitmapPtr(void* bitmapPtr, void* graphicsHdc) {
Graphics graphics((HDC)graphicsHdc);
CachedBitmap* result = new CachedBitmap((Bitmap*)bitmapPtr, &graphics);
return result;
}
static void DisposeCachedBitmap(void* cachedBitmapPtr) {
delete (CachedBitmap*)cachedBitmapPtr;
}
static void DisposeBitmap(void* bitmapPtr) {
delete (Bitmap*)bitmapPtr;
}
static void* GetBitmapPtrFromHICON(void* hicon) {
return (void*)Bitmap::FromHICON((HICON)hicon);
}
static void DrawCachedBitmap(void* hdc, void* cachedBitmapPtr, int x, int y) {
Graphics graphics((HDC)hdc);
graphics.DrawCachedBitmap((CachedBitmap*)cachedBitmapPtr, x, y);
}
};
}
Example usage from WinForm application:
public partial class MainForm : Form {
IntPtr m_BitmapPtr;
IntPtr m_CachedBitmapPtr = IntPtr.Zero;
public MainForm() {
InitializeComponent();
Bitmap bitmap;
using (var stream = typeof(MainForm).Assembly.GetManifestResourceStream("FormApplication.character.png")) {
bitmap = (Bitmap)Bitmap.FromStream(stream);
}
unsafe {
m_BitmapPtr = (IntPtr)BitmapUtility.GetBitmapPtrFromHICON((void*)bitmap.GetHicon());
}
}
protected override void OnClosed(EventArgs e) {
// TODO: refactor - dispose should happen in Dispose event
unsafe {
BitmapUtility.DisposeBitmap((void*)m_BitmapPtr);
BitmapUtility.DisposeCachedBitmap((void*)m_CachedBitmapPtr);
}
}
protected override void OnPaint(PaintEventArgs e) {
var graphics = e.Graphics;
IntPtr hdc;
if (m_CachedBitmapPtr == IntPtr.Zero) {
hdc = graphics.GetHdc();
unsafe {
m_CachedBitmapPtr = (IntPtr)BitmapUtility.CreateCachedBitmapPtr((void*)m_BitmapPtr, (void*)hdc);
}
graphics.ReleaseHdc(hdc);
}
hdc = graphics.GetHdc();
unsafe {
BitmapUtility.DrawCachedBitmap((void*)hdc, (void*)m_CachedBitmapPtr, 0, 0);
}
graphics.ReleaseHdc(hdc);
}
}

Why doesn't FastBitmap get garbage collected?

So I've finally located a problem I have with growing memory consumption. It's the class below, which for some reason doesn't get garbage collected. What would be the problem?
The idea of the FastBitmap class is to lock the bitmap data of a bitmap image once to avoid locking/unlocking on each call to GetPixel/SetPixel.
public unsafe class FastBitmap
{
private Bitmap subject;
private int subject_width;
private BitmapData bitmap_data = null;
private Byte* p_base = null;
public FastBitmap(Bitmap subject_bitmap)
{
this.subject = subject_bitmap;
try
{
LockBitmap();
}
catch (Exception ex)
{
throw ex;
}
}
public void Release()
{
try
{
UnlockBitmap();
}
catch (Exception ex)
{
throw ex;
}
}
public Bitmap Bitmap
{
get { return subject; }
}
public void LockBitmap()
{
GraphicsUnit unit = GraphicsUnit.Pixel;
RectangleF boundsF = subject.GetBounds(ref unit);
Rectangle bounds = new Rectangle((int)boundsF.X, (int)boundsF.Y, (int)boundsF.Width, (int)boundsF.Height);
subject_width = (int)boundsF.Width * sizeof(int);
if (subject_width % 4 != 0)
{
subject_width = 4 * (subject_width / 4 + 1);
}
bitmap_data = subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
p_base = (Byte*)bitmap_data.Scan0.ToPointer();
}
private void UnlockBitmap()
{
if (bitmap_data == null) return;
subject.UnlockBits(bitmap_data); bitmap_data = null; p_base = null;
}
}
EDIT
Here's how it does get properly collected..
public unsafe class FastBitmap : IDisposable
{
private Bitmap subject;
private int subject_width;
private BitmapData bitmap_data = null;
private Byte* p_base = null;
public FastBitmap(Bitmap subject_bitmap)
{
this.subject = subject_bitmap;
try
{
LockBitmap();
}
catch (Exception ex)
{
throw ex;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
UnlockBitmap();
Bitmap.Dispose();
}
subject = null;
bitmap_data = null;
p_base = null;
disposed = true;
}
}
~FastBitmap()
{
Dispose(false);
}
public Bitmap Bitmap
{
get { return subject; }
}
public void LockBitmap()
{
GraphicsUnit unit = GraphicsUnit.Pixel;
RectangleF boundsF = subject.GetBounds(ref unit);
Rectangle bounds = new Rectangle((int)boundsF.X, (int)boundsF.Y, (int)boundsF.Width, (int)boundsF.Height);
subject_width = (int)boundsF.Width * sizeof(int);
if (subject_width % 4 != 0)
{
subject_width = 4 * (subject_width / 4 + 1);
}
bitmap_data = subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
p_base = (Byte*)bitmap_data.Scan0.ToPointer();
}
public void UnlockBitmap()
{
if (bitmap_data == null) return;
subject.UnlockBits(bitmap_data); bitmap_data = null; p_base = null;
}
}
A couple points:
Your class hold access to pinned data. The garbage collector works by moving structures around in memory. So long as the bitmap has locked its bits, the garbage collector can't do anything with it.
Once you have Released the FastBitmap, I'm afraid that GDI+ may still be hanging onto the bits of data. GDI+ is a native library that does not interact with the garbage collector.
You need to release (dispose of) the GDI+ Bitmap too. Just call subject.Dispose() in Release.
As Mitchel mentioned, it would be nice to make your FastBitmap implement IDisposable and rename Release to dispose. This will allow you to use using statements in your code to make sure that the data is freed deterministically.
At a first glance i would say that you want to look into implementing the IDisposable interface on the class so that you can be sure to free up resources that are being used by the class.
If this class isn't being Garbage Collected, then something else still has a reference to it. While the internal data may be what is keeping it locked, I'd look elsewhere first.

Categories