Open Obelison opened 1 month ago
So the general approach should work. See https://github.com/AvaloniaUI/Avalonia/blob/ae44889370c56a5f7b163f72b32a4030d28a15a5/tests/Avalonia.RenderTests/Media/BitmapTests.cs#L115-L121 and https://github.com/stevemonaco/AvaloniaDemos/blob/master/RealTimeBitmapAdapter/ImageAdapters/WriteableBitmapAdapter.cs#L63
If this WriteableBitmap
is already assigned to an Image.Source
before the mutation, then you need to manually call theImage.InvalidateVisual();
so it refreshes. This is different from WPF.
Otherwise, provide a minimal repro. There may be some bad assumptions, such as assuming the stride for the WriteableBitmap
and your pixel buffer are the same...though you'd probably have a crash if that were the case.
There probably should be some public API here, but I prefer working directly with SkiaSharp when directly wrapping an existing pixel buffer. See https://github.com/stevemonaco/AvaloniaDemos/blob/master/SkiaBitmapAdapter/Views/SkiaBitmapView.axaml.cs#L90 for an example with SKBitmap
.
Many thanks for this info @stevemonaco, very helpful.
Actually, I am trying to display a stream of images grabbed from a camera in real-time. So with my specific application, I am trying to overwrite the image with another image with the same parameters (from the same camera). It turns out that a simpler way of doing the blit under these circumstances is with a more-or-less one-liner:
using (var locked = ((WriteableBitmap)bitmap).Lock()) { Unsafe.CopyBlock(locked.Address.ToPointer(), buffer.ImageAddress.ToPointer(), buffer.ImageSize); }
In my application, 'image' is a property in a ViewModel that's bound to an Image control. I am not calling InvalidateVisual() on the Image, so I am guessing this is the problem. Am I correct in assuming that I should post the call to InvalidateVisual() into the main/UI thread?
So the general approach should work. See
and https://github.com/stevemonaco/AvaloniaDemos/blob/master/RealTimeBitmapAdapter/ImageAdapters/WriteableBitmapAdapter.cs#L63 If this
WriteableBitmap
is already assigned to anImage.Source
before the mutation, then you need to manually calltheImage.InvalidateVisual();
so it refreshes. This is different from WPF.Otherwise, provide a minimal repro. There may be some bad assumptions, such as assuming the stride for the
WriteableBitmap
and your pixel buffer are the same...though you'd probably have a crash if that were the case.There probably should be some public API here, but I prefer working directly with SkiaSharp when directly wrapping an existing pixel buffer. See https://github.com/stevemonaco/AvaloniaDemos/blob/master/SkiaBitmapAdapter/Views/SkiaBitmapView.axaml.cs#L90 for an example with
SKBitmap
.
Am I correct in assuming that I should post the call to InvalidateVisual() into the main/UI thread?
Dispatching this to the UI thread appears to be necessary so you don't invalidate while a render pass is currently underway.
In some apps, I have an Action
property on the VM that the View sets like: ViewModel.OnImageModified = () => _image.InvalidateVisual();
. An event/callback/message would work too. In realtime apps, I'm usually triggering updates via the View anyways (via TopLevel.RequestAnimationFrame
). So those are some alternate approaches to using the Dispatcher
from within the ViewModel.
Is your feature request related to a problem? Please describe.
I would like to be able to write pixels directly into the (back?) buffer of a Bitmap, but cannot find a way of doing this using the published Avalonia interface.
Describe the solution you'd like
I mistakenly thought that the CopyPixels method in Avalonia.Media.Imaging writes pixels from a buffer into the Bitmap, but looking under the hood at the code, I see that it is copying the Bitmap pixels to the given buffer. My request is to effectively do the 'reverse' of the CopyPixels method, where the pixels in the buffer are copied (blit'ed) into the Bitmap. It could effectively be the same parameters, eg:
... and almost the same code as in the underlying CopyPixelsCore method of Bitmap, except that we replace sourceRect with destinationRect throughout, and replace line 214 and 215 with: var dstAddress = fb.Address + fb.RowBytes (destinationRect.Y + y) + offsetX; var srcAddress = buffer + stride y;
Is this something that could be done?
Describe alternatives you've considered
where buffer is an object that contains the (RGBA8888) pixels (bytes), and the ImageAddress, ImageHeight, & ImageWidth properties. This fails to write anything into the Bitmap, which may be a local issue with other parts of my code, so having a correct implementation within Avalonia would really help. However, I do think it would be useful for the community to have this addition to the Bitmap class and its children.
Additional context
No response