void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { var writeableBitmap = image.Source as WriteableBitmap; if (writeableBitmap == null || writeableBitmap.PixelWidth != width || writeableBitmap.PixelHeight != height) { image.Source = writeableBitmap = new WriteableBitmap(width, height, dpiX, dpiY, AbstractRenderHandler.PixelFormat, null); } if (writeableBitmap != null) { writeableBitmap.Lock(); NativeMethodWrapper.MemoryCopy(writeableBitmap.BackBuffer, buffer, writeableBitmap.BackBufferStride * writeableBitmap.PixelHeight); if (invalidateDirtyRect) { writeableBitmap.AddDirtyRect(new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height)); } else { writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, writeableBitmap.PixelWidth, writeableBitmap.PixelHeight)); } writeableBitmap.Unlock(); } }
/// <inheritdoc/> /// <remarks> /// NOTE: DevTools popups DO NOT trigger OnBeforePopup. /// </remarks> protected override bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { newBrowser = null; //No action so we'll go with the default behaviour. if (onPopupCreated == null) { return(false); } var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser; //We need to execute sync here so IWindowInfo.SetAsChild is called before we return false; webBrowser.InvokeSyncOnUiThreadIfRequired(new Action(() => { var control = new ChromiumHostControl { Dock = DockStyle.Fill }; control.CreateControl(); onPopupCreated?.Invoke(control, targetUrl); var rect = control.ClientRectangle; var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height); windowInfo.SetAsChild(control.Handle, windowBounds); })); return(false); }
/// <summary> /// Open DevTools using your own Control as the parent. If inspectElementAtX and/or inspectElementAtY are specified then /// the element at the specified (x,y) location will be inspected. /// For resize/moving to work correctly you will need to use the <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/> implementation. /// (Set <see cref="ChromiumWebBrowser.LifeSpanHandler"/> to an instance of <see cref="CefSharp.WinForms.Handler.LifeSpanHandler"/>) /// </summary> /// <param name="chromiumWebBrowser"><see cref="ChromiumWebBrowser"/> instance</param> /// <param name="addParentControl"> /// Action that is Invoked when the DevTools Host Control has been created and needs to be added to it's parent. /// It's important the control is added to it's intended parent at this point so the <see cref="Control.ClientRectangle"/> /// can be calculated to set the initial display size.</param> /// <param name="inspectElementAtX">x coordinate (used for inspectElement)</param> /// <param name="inspectElementAtY">y coordinate (used for inspectElement)</param> /// <returns>Returns the <see cref="Control"/> that hosts the DevTools instance if successful, otherwise returns null on error.</returns> public static Control ShowDevToolsDocked(this IChromiumWebBrowserBase chromiumWebBrowser, Action <ChromiumHostControl> addParentControl, string controlName = nameof(ChromiumHostControl) + "DevTools", DockStyle dockStyle = DockStyle.Fill, int inspectElementAtX = 0, int inspectElementAtY = 0) { if (chromiumWebBrowser.IsDisposed || addParentControl == null) { return(null); } var host = chromiumWebBrowser.GetBrowserHost(); if (host == null) { return(null); } var control = new ChromiumHostControl() { Name = controlName, Dock = dockStyle }; control.CreateControl(); //It's now time for the user to add the control to it's parent addParentControl(control); //Devtools will be a child of the ChromiumHostControl var rect = control.ClientRectangle; var windowInfo = new WindowInfo(); var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height); windowInfo.SetAsChild(control.Handle, windowBounds); host.ShowDevTools(windowInfo, inspectElementAtX, inspectElementAtY); return(control); }
private void CopyMemoryGentle(IntPtr source, IntPtr destination, Rect dirtyRect, RenderInfo info) { IntPtr newDestination = new IntPtr(destination.ToInt64() + dirtyRect.Y * info.Width * info.BytesPerPixel + dirtyRect.X * info.BytesPerPixel); IntPtr newSource = new IntPtr(source.ToInt64() + dirtyRect.Y * info.Width * info.BytesPerPixel + dirtyRect.X * info.BytesPerPixel); int length = (dirtyRect.Height - 1) * info.Width * info.BytesPerPixel + dirtyRect.Width * info.BytesPerPixel; CopyMemory(newDestination, newSource, (uint)length); }
public RenderInfo(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { IsPopup = isPopup; DirtyRect = dirtyRect; Buffer = buffer; Width = width; Height = height; Image = image; }
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; var tempBuffer = new byte[numberOfBytes]; //Copy unmanaged memory to our buffer Marshal.Copy(buffer, tempBuffer, 0, numberOfBytes); image.Dispatcher.BeginInvoke((Action)(() => { var bitmap = image.Source as WriteableBitmap; if (bitmap == null || bitmap.PixelHeight != height || bitmap.PixelWidth != width) { if (image.Source != null) { image.Source = null; GC.Collect(1); } image.Source = bitmap = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormat, null); } //Get a ptr to our temp buffer var tempBufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(tempBuffer, 0); //By default we'll only update the dirty rect, for those that run into a MILERR_WIN32ERROR Exception (#2035) //it's desirably to either upgrade to a newer .Net version (only client runtime needs to be installed, not compiled //against a newer version. Or invalidate the whole bitmap if (invalidateDirtyRect) { // Update the dirty region var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); bitmap.Lock(); bitmap.WritePixels(sourceRect, tempBufferPtr, numberOfBytes, stride, dirtyRect.X, dirtyRect.Y); bitmap.Unlock(); } else { // Update whole bitmap var sourceRect = new Int32Rect(0, 0, width, height); bitmap.Lock(); bitmap.WritePixels(sourceRect, tempBufferPtr, numberOfBytes, stride); bitmap.Unlock(); } }), dispatcherPriority); }
internal void OnBrowserRequestsPainting(CefSharp.Structs.Rect dirtyRect, IntPtr buffer, int width, int height) { if (_colorBuffer == null || _colorBuffer.Width != width || _colorBuffer.Height != height) { _colorBuffer?.Dispose(); _colorBuffer = new DeviceIndependentBitmap(width, height); } _colorBuffer.CopyFromBuffer(buffer, (uint)(width * height * 4 /*RGBA*/)); var blend = new NativeMethods.BlendFunction { BlendOp = NativeMethods.AC_SRC_OVER, BlendFlags = 0, SourceConstantAlpha = 255, AlphaFormat = NativeMethods.AC_SRC_ALPHA }; var windowPosition = new NativeMethods.Point { X = this.Left, Y = this.Top }; var surfaceSize = new NativeMethods.Size { Width = _colorBuffer.Width, Height = _colorBuffer.Height }; var surfacePosition = new NativeMethods.Point { X = 0, Y = 0 }; try { IntPtr handle = this.InvokeSyncOnUI((f) => f.Handle); //IntPtr.Zero; // this.InvokeSyncOnUIThread(() => handle = this.Handle); NativeMethods.UpdateLayeredWindow( handle, IntPtr.Zero, ref windowPosition, ref surfaceSize, _colorBuffer.DeviceContext, ref surfacePosition, 0, ref blend, NativeMethods.ULW_ALPHA); } catch (ObjectDisposedException ex) { //can happen, when the form gets killed by the ui thread while we try to render CEF stuff on it _colorBuffer?.Dispose(); } }
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); } }
void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { if (image.Dispatcher.HasShutdownStarted) { return; } lock (lockObj) { var layer = isPopup ? popup : view; layer.OnPaint(dirtyRect, buffer, width, height, image); } }
void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { lock (lockObject) { if (IsDisposed || image.Dispatcher.HasShutdownStarted) { return; } var paintElement = isPopup ? popup : view; paintElement?.UpdateBuffer(dirtyRect, buffer, width, height, image); paintElement?.UpdateImage(lockObject); } }
/// <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); } }
internal void OnPaint(Rect dirtyRect, IntPtr sourceBuffer, int width, int height, Image image) { ImageSize = (width * height) * AbstractRenderHandler.BytesPerPixel; if (BufferSize < ImageSize) { Marshal.FreeHGlobal(Buffer); Buffer = Marshal.AllocHGlobal(ImageSize); BufferSize = ImageSize; } Width = width; Height = height; DirtyRect = dirtyRect; NativeMethodWrapper.MemoryCopy(Buffer, sourceBuffer, ImageSize); Image = image; IsDirty = true; }
internal void UpdateBuffer(Rect dirtyRect, IntPtr sourceBuffer, int width, int height, Image image) { imageSize = (width * height) * AbstractRenderHandler.BytesPerPixel; if (bufferSize < imageSize) { Marshal.FreeHGlobal(buffer); buffer = Marshal.AllocHGlobal(imageSize); bufferSize = imageSize; } this.width = width; this.height = height; this.dirtyRect = dirtyRect; NativeMethodWrapper.MemoryCopy(buffer, sourceBuffer, imageSize); this.image = image; IsDirty = true; }
public void OnPaint(CefSharp.Structs.Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { lock (_lock) { int numberOfBytes = (width * height) * (System.Windows.Media.PixelFormats.Pbgra32.BitsPerPixel / 8); if (_bufferSize < numberOfBytes) { System.Runtime.InteropServices.Marshal.FreeHGlobal(_buffer); _buffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(numberOfBytes); _bufferSize = numberOfBytes; } _width = width; _height = height; CopyMemory(_buffer, buffer, numberOfBytes); _image = image; //Invalidate } }
void IRenderHandler.OnPaint(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { //File.AppendAllText("DEBUG.txt", DateTime.Now.ToLongTimeString() + this.GetHashCode() + " ONPAINT " + isPopup + " " + buffer + " - " + width + "x" + height + " " + image.GetHashCode() + Environment.NewLine); if (image.Dispatcher.HasShutdownStarted || (dirtyRect.X == 0 && dirtyRect.Y == 0 && dirtyRect.Width == 0 && dirtyRect.Height == 0)) { return; } if (isPopup) { CurrentPopupRenderInfo = new RenderInfo(isPopup, dirtyRect, buffer, width, height, image); ReleaseMemoryMappedView(ref memoryMappedFile, ref memoryMappedViewAccessor); memoryMappedFile = MemoryMappedFile.CreateNew(null, CurrentRenderInfo.NumberOfBytes, MemoryMappedFileAccess.ReadWrite); memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor(); var data = texA.LockRectangle(0, LockFlags.ReadOnly); CopyMemoryGentle(data.DataPointer, memoryMappedViewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), CurrentRenderInfo.NumberOfBytes); texA.UnlockRectangle(0); ReleaseMemoryMappedView(ref popupMemoryMappedFile, ref popupMemoryMappedViewAccessor); popupMemoryMappedFile = MemoryMappedFile.CreateNew(null, CurrentPopupRenderInfo.NumberOfBytes, MemoryMappedFileAccess.ReadWrite); popupMemoryMappedViewAccessor = popupMemoryMappedFile.CreateViewAccessor(); CopyMemoryGentle(CurrentPopupRenderInfo.Buffer, popupMemoryMappedViewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), CurrentPopupRenderInfo.NumberOfBytes); DirectXRender(CurrentPopupRenderInfo); } else { CurrentRenderInfo.IsPopup = isPopup; CurrentRenderInfo.DirtyRect = dirtyRect; CurrentRenderInfo.Buffer = buffer; CurrentRenderInfo.Width = width; CurrentRenderInfo.Height = height; CurrentRenderInfo.Image = image; DirectXRender(CurrentRenderInfo); } }
/// <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 }
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) { bool createNewBitmap = false; lock (lockObject) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { //If the MemoryMappedFile is smaller than we need then create a larger one //If it's larger then we need then rather than going through the costly expense of //allocating a new one we'll just use the old one and only access the number of bytes we require. if (viewAccessor == null || viewAccessor.Capacity < numberOfBytes) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); } currentSize.Height = height; currentSize.Width = width; } NativeMethodWrapper.MemoryCopy(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); //Take a reference to the sourceBuffer that's used to update our WritableBitmap, //once we're on the UI thread we need to check if it's still valid var sourceBuffer = viewAccessor.SafeMemoryMappedViewHandle; image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (sourceBuffer.IsClosed || sourceBuffer.IsInvalid) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; GC.Collect(1); } image.Source = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormat, null); } var stride = width * BytesPerPixel; var noOfBytes = stride * height; var bitmap = (WriteableBitmap)image.Source; //By default we'll only update the dirty rect, for those that run into a MILERR_WIN32ERROR Exception (#2035) //it's desirably to either upgrade to a newer .Net version (only client runtime needs to be installed, not compiled //against a newer version. Or invalidate the whole bitmap if (invalidateDirtyRect) { // Update the dirty region var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride, dirtyRect.X, dirtyRect.Y); bitmap.Unlock(); } else { // Update whole bitmap var sourceRect = new Int32Rect(0, 0, width, height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride); bitmap.Unlock(); } } }), dispatcherPriority); } }
private void CreateOrUpdateBitmap(bool isPopup, Rect dirtyRect, IntPtr buffer, int width, int height, Image image, ref Size currentSize, ref MemoryMappedFile mappedFile, ref MemoryMappedViewAccessor viewAccessor) { bool createNewBitmap = false; if (image.Dispatcher.HasShutdownStarted) { return; } lock (lockObject) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); currentSize.Height = height; currentSize.Width = width; } //TODO: Performance analysis to determine which is the fastest memory copy function //NativeMethodWrapper.CopyMemoryUsingHandle(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); CopyMemory(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, (uint)numberOfBytes); //Take a reference to the sourceBuffer that's used to update our WritableBitmap, //once we're on the UI thread we need to check if it's still valid var sourceBuffer = viewAccessor.SafeMemoryMappedViewHandle; image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (sourceBuffer.IsClosed || sourceBuffer.IsInvalid) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; GC.Collect(1); } image.Source = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormat, null); } var stride = width * BytesPerPixel; var noOfBytes = stride * height; var bitmap = (WriteableBitmap)image.Source; //By default we'll only update the dirty rect, for those that run into a MILERR_WIN32ERROR Exception (#2035) //it's desirably to either upgrade to a newer .Net version (only client runtime needs to be installed, not compiled //against a newer version. Or invalidate the whole bitmap if (invalidateDirtyRect) { // Update the dirty region var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride, dirtyRect.X, dirtyRect.Y); bitmap.Unlock(); } else { // Update whole bitmap var sourceRect = new Int32Rect(0, 0, width, height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride); bitmap.Unlock(); } } }), dispatcherPriority); } }
public virtual void SetAsChild(System.IntPtr parentHandle, CefSharp.Structs.Rect windowBounds) { }
public void OnPaint(bool isPopup, CefSharp.Structs.Rect dirtyRect, IntPtr buffer, int width, int height, Image image) { var layer = isPopup ? this._popup : this._background; layer.OnPaint(dirtyRect, buffer, width, height, image); }
public void OnAcceleratedPaint(bool isPopup, CefSharp.Structs.Rect dirtyRect, IntPtr sharedHandle) { //throw new NotImplementedException(); }
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) { var createNewBitmap = false; lock (lockObject) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); currentSize.Height = height; currentSize.Width = width; } //TODO: Performance analysis to determine which is the fastest memory copy function //NativeMethodWrapper.CopyMemoryUsingHandle(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); CopyMemory(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, (uint)numberOfBytes); //Take a reference to the backBufferHandle, once we're on the UI thread we need to check if it's still valid var backBufferHandle = mappedFile.SafeMemoryMappedFileHandle; //Invoke on the WPF UI Thread image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (backBufferHandle.IsClosed || backBufferHandle.IsInvalid) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; //TODO: Is this still required in newer versions of .Net? GC.Collect(1); } var stride = width * BytesPerPixel; var bitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(backBufferHandle.DangerousGetHandle(), width, height, PixelFormat, stride, 0); image.Source = bitmap; } else if (image.Source != null) { var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); var bitmap = (InteropBitmap)image.Source; bitmap.Invalidate(sourceRect); } } }), dispatcherPriority); } }
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) { var createNewBitmap = false; lock (lockObject) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); currentSize.Height = height; currentSize.Width = width; } NativeMethodWrapper.MemoryCopy(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); //Take a reference to the backBufferHandle, once we're on the UI thread we need to check if it's still valid var backBufferHandle = mappedFile.SafeMemoryMappedFileHandle; //Invoke on the WPF UI Thread image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (backBufferHandle.IsClosed || backBufferHandle.IsInvalid) { return; } var size = isPopup ? popupSize : viewSize; //If OnPaint is called multiple times before //our BeginInvoke call we check the size matches our most recent //update, the buffer has already been overriden (frame is dropped effectively) //so we ignore this call //https://github.com/cefsharp/CefSharp/issues/3114 if (size.Width != width || size.Height != height) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; //TODO: Is this still required in newer versions of .Net? GC.Collect(1); } var stride = width * BytesPerPixel; var bitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(backBufferHandle.DangerousGetHandle(), width, height, PixelFormat, stride, 0); image.Source = bitmap; } else if (image.Source != null) { var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); var bitmap = (InteropBitmap)image.Source; bitmap.Invalidate(sourceRect); } } }), dispatcherPriority); } }
/// <inheritdoc/> /// <remarks> /// NOTE: DevTools popups DO NOT trigger OnBeforePopup. /// </remarks> protected override bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { newBrowser = null; PopupCreation userAction = onBeforePopupCreated?.Invoke(chromiumWebBrowser, browser, frame, targetUrl, targetFrameName, targetDisposition, userGesture, browserSettings) ?? PopupCreation.Continue; //Cancel popup creation if (userAction == PopupCreation.Cancel) { return(true); } if (userAction == PopupCreation.ContinueWithJavascriptDisabled) { noJavascriptAccess = true; } //No action so we'll go with the default behaviour. if (onPopupCreated == null) { return(false); } var webBrowser = (ChromiumWebBrowser)chromiumWebBrowser; //Load and Display Handlers are used to trigger the relevant events. //If they are already assigned we'll leave the user preference in place if (webBrowser.LoadHandler == null) { webBrowser.LoadHandler = new LoadHandler(); } if (webBrowser.DisplayHandler == null) { webBrowser.DisplayHandler = new DisplayHandler(); } //We need to execute sync here so IWindowInfo.SetAsChild is called before we return false; webBrowser.InvokeSyncOnUiThreadIfRequired(new Action(() => { ChromiumHostControl control = chromiumHostControlCreatedDelegate?.Invoke(); if (control == null) { control = new ChromiumHostControl { Dock = DockStyle.Fill }; } control.CreateControl(); onPopupCreated?.Invoke(control, targetUrl); var rect = control.ClientRectangle; var windowBounds = new CefSharp.Structs.Rect(rect.X, rect.Y, rect.Width, rect.Height); windowInfo.SetAsChild(control.Handle, windowBounds); })); return(false); }
/// <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 }
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) { bool createNewBitmap = false; lock (lockObject) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { //If the MemoryMappedFile is smaller than we need then create a larger one //If it's larger then we need then rather than going through the costly expense of //allocating a new one we'll just use the old one and only access the number of bytes we require. if (viewAccessor == null || viewAccessor.Capacity < numberOfBytes) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); } currentSize.Height = height; currentSize.Width = width; } NativeMethodWrapper.MemoryCopy(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); //Take a reference to the sourceBuffer that's used to update our WritableBitmap, //once we're on the UI thread we need to check if it's still valid var sourceBuffer = viewAccessor.SafeMemoryMappedViewHandle; image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (sourceBuffer.IsClosed || sourceBuffer.IsInvalid) { return; } var size = isPopup ? popupSize : viewSize; //If OnPaint is called multiple times before //our BeginInvoke call we check the size matches our most recent //update, the buffer has already been overriden (frame is dropped effectively) //so we ignore this call //https://github.com/cefsharp/CefSharp/issues/3114 if (size.Width != width || size.Height != height) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; GC.Collect(1); } image.Source = new WriteableBitmap(width, height, dpiX, dpiY, PixelFormat, null); } var stride = width * BytesPerPixel; var noOfBytes = stride * height; var bitmap = (WriteableBitmap)image.Source; //When agressively resizing the ChromiumWebBrowser sometimes //we can end up with our buffer size not matching our bitmap size //Just ignore these frames as the rendering should eventually catch up //(CEF can generate multiple frames before WPF has performed a render cycle) //https://github.com/cefsharp/CefSharp/issues/3474 if (width > bitmap.PixelWidth || height > bitmap.PixelHeight) { return; } //By default we'll only update the dirty rect, for those that run into a MILERR_WIN32ERROR Exception (#2035) //it's desirably to either upgrade to a newer .Net version (only client runtime needs to be installed, not compiled //against a newer version. Or invalidate the whole bitmap if (invalidateDirtyRect) { // Update the dirty region var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride, dirtyRect.X, dirtyRect.Y); bitmap.Unlock(); } else { // Update whole bitmap var sourceRect = new Int32Rect(0, 0, width, height); bitmap.Lock(); bitmap.WritePixels(sourceRect, sourceBuffer.DangerousGetHandle(), noOfBytes, stride); bitmap.Unlock(); } } }), dispatcherPriority); } }
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);
void IRenderHandler.OnAcceleratedPaint(bool isPopup, Rect dirtyRect, IntPtr sharedHandle) { throw new NotImplementedException(); }
private 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) { int pixels = width * height; int numberOfBytes = pixels * BytesPerPixel; createNewBitmap = mappedFile == null || currentSize.Height != height || currentSize.Width != width; if (createNewBitmap) { //If the MemoryMappedFile is smaller than we need then create a larger one //If it's larger then we need then rather than going through the costly expense of //allocating a new one we'll just use the old one and only access the number of bytes we require. if (viewAccessor == null || viewAccessor.Capacity < numberOfBytes) { ReleaseMemoryMappedView(ref mappedFile, ref viewAccessor); mappedFile = MemoryMappedFile.CreateNew(null, numberOfBytes, MemoryMappedFileAccess.ReadWrite); viewAccessor = mappedFile.CreateViewAccessor(); } currentSize.Height = height; currentSize.Width = width; } //TODO: Performance analysis to determine which is the fastest memory copy function //NativeMethodWrapper.CopyMemoryUsingHandle(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, numberOfBytes); CopyMemory(viewAccessor.SafeMemoryMappedViewHandle.DangerousGetHandle(), buffer, (uint)numberOfBytes); //Take a reference to the backBufferHandle, once we're on the UI thread we need to check if it's still valid var backBufferHandle = mappedFile.SafeMemoryMappedFileHandle; image.Dispatcher.BeginInvoke((Action)(() => { lock (lockObject) { if (backBufferHandle.IsClosed || backBufferHandle.IsInvalid) { return; } if (createNewBitmap) { if (image.Source != null) { image.Source = null; GC.Collect(1); } var stride = width * BytesPerPixel; var bitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(backBufferHandle.DangerousGetHandle(), width, height, PixelFormat, stride, 0); image.Source = bitmap; } else { if (image.Source != null) { var sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height); var bitmap = (InteropBitmap)image.Source; bitmap.Invalidate(sourceRect); } } } }), dispatcherPriority); } }
void CefSharp.Internals.IRenderWebBrowser.OnPaint(CefSharp.PaintElementType type, CefSharp.Structs.Rect dirtyRect, IntPtr buffer, int width, int height) { OnPaintDelegate.Invoke(type, dirtyRect, buffer, width, height); }