Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CefSharp.Wpf/CefSharp.Wpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<AppDesigner Include="Properties\" />
<Compile Include="ChromiumWebBrowser.cs" />
<Compile Include="PaintEventArgs.cs" />
<Compile Include="Rendering\AbstractRenderHandler.cs" />
<Compile Include="Rendering\Experimental\IncreaseBufferInteropRenderHandler.cs" />
<Compile Include="Rendering\Experimental\ByteArrayWritableBitmapRenderHandler.cs" />
<Compile Include="Rendering\InteropBitmapRenderHandler.cs" />
Expand Down
143 changes: 143 additions & 0 deletions CefSharp.Wpf/Rendering/AbstractRenderHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright © 2019 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using Rect = CefSharp.Structs.Rect;

namespace CefSharp.Wpf.Rendering
{
/// <summary>
/// Implements the basics of a <see cref="IRenderHandler"/>
/// </summary>
/// <seealso cref="CefSharp.Wpf.IRenderHandler" />
public abstract class AbstractRenderHandler : IDisposable, IRenderHandler
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
protected static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

protected static readonly PixelFormat PixelFormat = PixelFormats.Pbgra32;
protected static int BytesPerPixel = PixelFormat.BitsPerPixel / 8;

protected object lockObject = new object();

protected Size viewSize;
protected Size popupSize;
protected DispatcherPriority dispatcherPriority;

protected MemoryMappedFile viewMemoryMappedFile;
protected MemoryMappedFile popupMemoryMappedFile;
protected MemoryMappedViewAccessor viewMemoryMappedViewAccessor;
protected MemoryMappedViewAccessor popupMemoryMappedViewAccessor;

/// <summary>
/// The value for disposal, if it's 1 (one) then this instance is either disposed
/// or in the process of getting disposed
/// </summary>
private int disposeSignaled;

/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
/// <value><see langword="true"/> if this instance is disposed; otherwise, <see langword="true"/>.</value>
public bool IsDisposed
{
get
{
return Interlocked.CompareExchange(ref disposeSignaled, 1, 1) == 1;
}
}

/// <summary>
/// Releases all resources used by the <see cref="AbstractRenderHandler"/> object
/// </summary>
public void Dispose()
{
Dispose(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If obj does not have a finalizer, the call to the SuppressFinalize method has no effect.

https://docs.microsoft.com/en-us/dotnet/api/system.gc.suppressfinalize?view=netframework-4.7.2

So looks like it's a NO OP, not sure why it's in the example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for when a derived class has a Finalizer

GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources for the <see cref="AbstractRenderHandler"/>
/// </summary>
/// <param name="disposing"><see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (Interlocked.CompareExchange(ref disposeSignaled, 1, 0) != 0)
{
return;
}

if (!disposing)
{
return;
}

ReleaseMemoryMappedView(ref popupMemoryMappedFile, ref popupMemoryMappedViewAccessor);
ReleaseMemoryMappedView(ref viewMemoryMappedFile, ref viewMemoryMappedViewAccessor);
}

protected void ReleaseMemoryMappedView(ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor stream)
{
if (stream != null)
{
stream.Dispose();
stream = null;
}

if (mappedFile != null)
{
mappedFile.Dispose();
mappedFile = null;
}
}

/// <summary>
/// Called when an element has been rendered to the shared texture handle.
/// This method is only called when <see cref="IWindowInfo.SharedTextureEnabled"/> is set to true
/// </summary>
/// <param name="isPopup">indicates whether the element is the view or the popup widget.</param>
/// <param name="dirtyRect">contains the set of rectangles in pixel coordinates that need to be repainted</param>
/// <param name="sharedHandle">is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method.</param>
public virtual void OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle)
{
// NOT USED
}

/// <summary>
/// Called when an element should be painted. (Invoked from CefRenderHandler.OnPaint)
/// This method is only called when <see cref="IWindowInfo.SharedTextureEnabled"/> is set to false.
/// </summary>
/// <param name="isPopup">indicates whether the element is the view or the popup widget.</param>
/// <param name="dirtyRect">contains the set of rectangles in pixel coordinates that need to be repainted</param>
/// <param name="buffer">The bitmap will be will be width * height *4 bytes in size and represents a BGRA image with an upper-left origin</param>
/// <param name="width">width</param>
/// <param name="height">height</param>
/// <param name="image">image used as parent for rendered bitmap</param>
public virtual void OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image)
{
if (image.Dispatcher.HasShutdownStarted)
{
return;
}

if (isPopup)
{
CreateOrUpdateBitmap(isPopup, dirtyRect, buffer, width, height, image, ref popupSize, ref popupMemoryMappedFile, ref popupMemoryMappedViewAccessor);
}
else
{
CreateOrUpdateBitmap(isPopup, dirtyRect, buffer, width, height, image, ref viewSize, ref viewMemoryMappedFile, ref viewMemoryMappedViewAccessor);
}
}

protected abstract void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image, ref Size currentSize, ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor viewAccessor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

using Rect = CefSharp.Structs.Rect;

namespace CefSharp.Wpf.Rendering.Experimental
Expand All @@ -21,18 +20,11 @@ namespace CefSharp.Wpf.Rendering.Experimental
/// wise.
/// </summary>
/// <seealso cref="CefSharp.Wpf.IRenderHandler" />
public class ByteArrayWritableBitmapRenderHandler : IRenderHandler
public class ByteArrayWritableBitmapRenderHandler : AbstractRenderHandler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AbstractRenderHandler contains methods/properties that aren't used in this particular instance, so doesn't quite fit having it as a base class.

{
/// <summary>
/// The pixel format
/// </summary>
private static readonly PixelFormat PixelFormat = PixelFormats.Prgba64;
private static int BytesPerPixel = PixelFormat.BitsPerPixel / 8;

private double dpiX;
private double dpiY;
private bool invalidateDirtyRect;
private DispatcherPriority dispatcherPriority;

/// <summary>
/// Initializes a new instance of the <see cref="WritableBitmapRenderHandler"/> class.
Expand All @@ -49,25 +41,8 @@ public ByteArrayWritableBitmapRenderHandler(double dpiX, double dpiY, bool inval
this.dispatcherPriority = dispatcherPriority;
}

/// <summary>
/// Called when an element has been rendered to the shared texture handle.
/// This method is only called when <see cref="IWindowInfo.SharedTextureEnabled"/> is set to true
/// </summary>
/// <param name="isPopup">indicates whether the element is the view or the popup widget.</param>
/// <param name="dirtyRect">contains the set of rectangles in pixel coordinates that need to be repainted</param>
/// <param name="sharedHandle">is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method.</param>
void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle)
protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image, ref Size currentSize, ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor viewAccessor)
{
//NOT USED
}

void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image)
{
if (image.Dispatcher.HasShutdownStarted)
{
return;
}

int pixels = width * height;
int numberOfBytes = pixels * BytesPerPixel;
var stride = width * BytesPerPixel;
Expand Down Expand Up @@ -117,10 +92,5 @@ void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int wid
}
}), dispatcherPriority);
}

void IDisposable.Dispose()
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using Rect = CefSharp.Structs.Rect;

Expand All @@ -20,28 +18,8 @@ namespace CefSharp.Wpf.Rendering.Experimental
/// when the size increases
/// </summary>
/// <seealso cref="CefSharp.Wpf.IRenderHandler" />
public class IncreaseBufferInteropRenderHandler : IRenderHandler
public class IncreaseBufferInteropRenderHandler : AbstractRenderHandler
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

/// <summary>
/// The pixel format
/// </summary>
private static readonly PixelFormat PixelFormat = PixelFormats.Pbgra32;
private static int BytesPerPixel = PixelFormat.BitsPerPixel / 8;

private object lockObject = new object();

private Size viewSize;
private Size popupSize;
private DispatcherPriority dispatcherPriority;

private MemoryMappedFile viewMemoryMappedFile;
private MemoryMappedFile popupMemoryMappedFile;
private MemoryMappedViewAccessor viewMemoryMappedViewAccessor;
private MemoryMappedViewAccessor popupMemoryMappedViewAccessor;

/// <summary>
/// Initializes a new instance of the <see cref="InteropBitmapRenderHandler"/> class.
/// </summary>
Expand All @@ -51,46 +29,8 @@ public IncreaseBufferInteropRenderHandler(DispatcherPriority dispatcherPriority
this.dispatcherPriority = dispatcherPriority;
}

/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
ReleaseMemoryMappedView(ref popupMemoryMappedFile, ref popupMemoryMappedViewAccessor);
ReleaseMemoryMappedView(ref viewMemoryMappedFile, ref viewMemoryMappedViewAccessor);
}

/// <summary>
/// Called when an element has been rendered to the shared texture handle.
/// This method is only called when <see cref="IWindowInfo.SharedTextureEnabled"/> is set to true
/// </summary>
/// <param name="isPopup">indicates whether the element is the view or the popup widget.</param>
/// <param name="dirtyRect">contains the set of rectangles in pixel coordinates that need to be repainted</param>
/// <param name="sharedHandle">is the handle for a D3D11 Texture2D that can be accessed via ID3D11Device using the OpenSharedResource method.</param>
void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle)
{
//NOT USED
}

void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image)
{
if (isPopup)
{
CreateOrUpdateBitmap(isPopup, dirtyRect, buffer, width, height, image, ref popupSize, ref popupMemoryMappedFile, ref popupMemoryMappedViewAccessor);
}
else
{
CreateOrUpdateBitmap(isPopup, dirtyRect, buffer, width, height, image, ref viewSize, ref viewMemoryMappedFile, ref viewMemoryMappedViewAccessor);
}
}

private void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image, ref Size currentSize, ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor viewAccessor)
protected override void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image, ref Size currentSize, ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor viewAccessor)
{
if (image.Dispatcher.HasShutdownStarted)
{
return;
}

var createNewBitmap = false;

lock (lockObject)
Expand Down Expand Up @@ -146,33 +86,15 @@ private void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, i
var bitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(backBufferHandle.DangerousGetHandle(), width, height, PixelFormat, stride, 0);
image.Source = bitmap;
}
else
else if (image.Source != null)
{
if (image.Source != null)
{
var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height);
var bitmap = (InteropBitmap)image.Source;
bitmap.Invalidate(sourceRect);
}
var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height);
var bitmap = (InteropBitmap)image.Source;
bitmap.Invalidate(sourceRect);
}
}
}), dispatcherPriority);
}
}

private void ReleaseMemoryMappedView(ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor stream)
{
if (stream != null)
{
stream.Dispose();
stream = null;
}

if (mappedFile != null)
{
mappedFile.Dispose();
mappedFile = null;
}
}
}
}
Loading