読者です 読者をやめる 読者になる 読者になる

みかづきメモ

学習したことのメモとか、日記とか、備忘録。

CefSharp.Wpf でもスクショしたい

C# WPF

Chromium Embedded Framework (CEF) を C# から使えるようにしたプロジェクトのうちの1つに、
CefSharp というものがあります。

WinForms, WPF, OffScreen 用にそれぞれパッケージが用意されており、 NuGet からも導入できます。
そのうち、 OffScreen には、描画部分をスクショする機能があるのですが、
WinForms 及び WPF プロジェクトには提供されていないので、自前実装しました。


基本的には、 OffScreen の実装を WPF 向けに移植しただけです。

/// <summary>
///     Extend CefSharp ChromiumWebBrowser for Snapshot
/// </summary>
internal class ChromiumWebBrowser2 : ChromiumWebBrowser
{
    private readonly object _lockObj = new object();
    private ImageFormat _format;
    private bool _isTakingSnapshot;
    private string _path;

    public ChromiumWebBrowser2()
    {
        _isTakingSnapshot = false;
        Rendering += OnRendering;
    }

    public void TakingSnapshot(string path, ImageFormat format)
    {
        if (!WebBrowser.IsBrowserInitialized || WebBrowser.IsLoading || _isTakingSnapshot)
            return;

        lock (_lockObj)
        {
            _path = path;
            _format = format;
            _isTakingSnapshot = true;
        }
    }

    // InvokeRenderAsync
    private void OnRendering(object sender, RenderingEventArgs e)
    {
        if (_isTakingSnapshot)
            Snapshot(e.BitmapInfo);
    }

    private void Snapshot(BitmapInfo bitmapInfo)
    {
        var stride = bitmapInfo.Width * bitmapInfo.BytesPerPixel;
        lock (_lockObj)
        {
            var snapshot = (InteropBitmap) Imaging.CreateBitmapSourceFromMemorySection(bitmapInfo.FileMappingHandle,
                                                                                       bitmapInfo.Width,
                                                                                       bitmapInfo.Height,
                                                                                       PixelFormats.Bgra32, stride,
                                                                                       0);
            using (var stream = new FileStream(_path, FileMode.Create))
            {
                var encoder = _format.GetBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(snapshot));
                encoder.Save(stream);
            }
        }
        _isTakingSnapshot = false;
    }
}

ちなみに ImageFormat はこれ。

public enum ImageFormat
{
    Bmp,

    Png,

    Jpeg
}

public static class ImageFormatExt
{
    public static string GetImageExtension(this ImageFormat obj) => obj.ToString().ToLower();

    public static BitmapEncoder GetBitmapEncoder(this ImageFormat obj)
    {
        switch (obj)
        {
            case ImageFormat.Bmp:
                return new BmpBitmapEncoder();

            case ImageFormat.Jpeg:
                return new JpegBitmapEncoder();
 
            case ImageFormat.Png:
                return new PngBitmapEncoder();

            default:
                throw new ArgumentOutOfRangeException(nameof(obj), obj, null);
        }
    }
}

使い方は、 ChromiumWebBrowser の代わりに ChromiumWebBrowser2 を使って、
任意のタイミングで TakingSnapshot を呼べば OK 。