【问题标题】:How to fix a opacity bug with DrawToBitmap on WebBrowser Control?如何使用 WebBrowser 控件上的 DrawToBitmap 修复不透明度错误?
【发布时间】:2014-03-08 22:55:53
【问题描述】:

根据以下链接和我的控制台应用程序,DrawToBitmap 方法不尊重不透明度。

证明链接: http://social.msdn.microsoft.com/Forums/vstudio/en-US/e9704309-0c52-442d-80e0-2f8393dcd313/webbrowser-opacity-problem-

我的 HTML 代码:http://fiddle.jshell.net/L37TC/

<div id="fader" style="background-color: #ff0000">ffff</div>
<div style="background-color: blue;  opacity:0;filter:alpha(opacity=0);">HIDDEN TEXT!</div>
SomeText 

我的 C# 控制台代码:

var bmp = new  Bitmap(640,480, PixelFormat::Format32bppArgb)
var web = (System.Windows.Forms.Control)sender;
web.DrawToBitmap(bmp, Rectangle(0, 0, 640,480));

所以我正在寻找替代的 .NET 内置解决方案(没有 CEF、Awesomium 或任何扩展)只是 .NET 的内置功能修复错误或替代解决方案以在我的控制台应用程序中截取 Web URL。

如果我让WebBrowser 窗口对我的客户端可见并使用CopyFromScreen,则不透明度得到尊重并且HIDDEN TEXT 没有显示,但我不想让WebBrowser 窗口对桌面屏幕可见。

我正在寻找一个内置的解决方案来从问题中发布的 URL 中截取屏幕截图,而不使用 HIDDEN TEXT。换句话说,一个尊重opacity的解决方案。

EDIT1:我的Bitmap class(.NET 类不是 BMP 格式)中的所有像素的 alpha 值为 255。所以问题不在于文件格式。我已经尝试过 PNG 和任何其他 .NET 支持的格式。

完整的源代码(控制台模板,需要添加对System.DrawingSystem.Windows.Forms的引用

class Program
{
    static System.Windows.Forms.WebBrowser w = new System.Windows.Forms.WebBrowser();

    [STAThread]
    static void Main(string[] args)
    {

        w.Navigate("http://fiddle.jshell.net/L37TC/show/");
        w.DocumentCompleted += w_DocumentCompleted;
        System.Windows.Forms.Application.Run();
        while (true) Console.Read();

    }

    static void w_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
    {

        var bmp = new System.Drawing.Bitmap(w.Width, w.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        ((System.Windows.Forms.Control)w).DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, w.Width, w.Height));
        for (int i = 0; i < w.Width; i++) for (int j = 0; j < w.Height; j++) if (bmp.GetPixel(i, j).A != 255)
                {
                    Console.WriteLine("Alpha != 255");
                    return;
                }

        Console.WriteLine("All pixels have alpha value of 255");
        bmp.Save(@"d:\ss.png", System.Drawing.Imaging.ImageFormat.Png);
        // HIDDEN TEXT has opcity of 0 but it's appearing in image



    }
}

【问题讨论】:

  • 您可以将 webbrowser 窗口置于可见但在用户看不到的区域中,例如在 x= - browser.width, y= - browser.height(类似的东西)中跨度>
  • 不确定 CopyFromScreen 是否适用于负值...但是我仍然不想要可见窗口
  • 你有完整的例子吗?当我将您的 HTML 代码与开箱即用的 WebBrowser 控件一起使用时,它根本不显示不透明度(可能是 IE 版本兼容性问题)
  • @SimonMourier 我已经添加了,谢谢
  • 在运行您的代码时,我得到了与@SimonMourier 相同的响应。 (W8.1 IE11 .Net4)

标签: c# .net vb.net webbrowser-control


【解决方案1】:

我正在寻找一个内置的解决方案来截取已发布的屏幕截图 问题中的 URL 没有隐藏文本。换句话说,一个解决方案 尊重不透明度。

以下代码就是这样做的:尊重 CSS 不透明度。除其他外,它使用Metafile 对象和OleDraw API 来呈现网页的图像。

测试 HTML:

<!DOCTYPE html>
<body style='background-color: grey'>
    <div style='background-color: blue; opacity: 0.2; color: yellow'>This is a text</div>
</body>

输出:

代码(控制台应用):

using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Console_21697048
{
    // http://stackoverflow.com/q/21697048/1768303

    class Program
    {
        const string HTML = "<!DOCTYPE html><body style='background-color: grey'><div style='background-color: blue; opacity: 0.2; color: yellow'>This is a text</div></body>";
        const string FILE_NAME = "webpage.png";
        readonly static Size IMAGE_SIZE = new Size(320, 200); 

        // Main
        static void Main(string[] args)
        {
            try
            {
                // enable HTML5 etc (assuming we're running IE9+)
                SetFeatureBrowserFeature("FEATURE_BROWSER_EMULATION", 9000);
                // force software rendering
                SetFeatureBrowserFeature("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI", 1);
                SetFeatureBrowserFeature("FEATURE_GPU_RENDERING", 0);

                using (var apartment = new MessageLoopApartment())
                {
                    // create WebBrowser on a seprate thread with its own message loop
                    var webBrowser = apartment.Invoke(() => new WebBrowser());

                    // navigate and wait for the result 
                    apartment.Invoke(() =>
                    {
                        var pageLoadedTcs = new TaskCompletionSource<bool>();
                        webBrowser.DocumentCompleted += (s, e) =>
                            pageLoadedTcs.TrySetResult(true);

                        webBrowser.DocumentText = HTML;
                        return pageLoadedTcs.Task;
                    }).Wait();

                    // save the picture
                    apartment.Invoke(() =>
                    {
                        webBrowser.Size = IMAGE_SIZE;
                        var rectangle = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);

                        // get reference DC
                        using (var screenGraphics = webBrowser.CreateGraphics())
                        {
                            var screenHdc = screenGraphics.GetHdc();
                            // create a metafile
                            using (var metafile = new Metafile(screenHdc, rectangle, MetafileFrameUnit.Pixel))
                            {
                                using (var graphics = Graphics.FromImage(metafile))
                                {
                                    var hdc = graphics.GetHdc();
                                    var rect = new Rectangle(0, 0, 320, 50);
                                    OleDraw(webBrowser.ActiveXInstance, DVASPECT_CONTENT, hdc, ref rectangle);
                                    graphics.ReleaseHdc(hdc);
                                }
                                // save the metafile as bitmap
                                metafile.Save(FILE_NAME, ImageFormat.Png);
                            }
                            screenGraphics.ReleaseHdc(screenHdc);
                        }
                    });

                    // dispose of webBrowser
                    apartment.Invoke(() => webBrowser.Dispose());
                    webBrowser = null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        // interop
        const uint DVASPECT_CONTENT = 1;

        [DllImport("ole32.dll", PreserveSig = false)]
        static extern void OleDraw(
            [MarshalAs(UnmanagedType.IUnknown)] object pUnk,
            uint dwAspect,
            IntPtr hdcDraw,
            [In] ref System.Drawing.Rectangle lprcBounds);

        // WebBrowser Feature Control
        // http://msdn.microsoft.com/en-us/library/ie/ee330733(v=vs.85).aspx
        static void SetFeatureBrowserFeature(string feature, uint value)
        {
            if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
                return;
            var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
            Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\" + feature,
                appName, value, RegistryValueKind.DWord);
        }
    }


    // MessageLoopApartment
    // more info: http://stackoverflow.com/a/21808747/1768303

    public class MessageLoopApartment : IDisposable
    {
        Thread _thread; // the STA thread

        TaskScheduler _taskScheduler; // the STA thread's task scheduler

        public TaskScheduler TaskScheduler { get { return _taskScheduler; } }

        /// <summary>MessageLoopApartment constructor</summary>
        public MessageLoopApartment()
        {
            var tcs = new TaskCompletionSource<TaskScheduler>();

            // start an STA thread and gets a task scheduler
            _thread = new Thread(startArg =>
            {
                EventHandler idleHandler = null;

                idleHandler = (s, e) =>
                {
                    // handle Application.Idle just once
                    Application.Idle -= idleHandler;
                    // return the task scheduler
                    tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
                };

                // handle Application.Idle just once
                // to make sure we're inside the message loop
                // and SynchronizationContext has been correctly installed
                Application.Idle += idleHandler;
                Application.Run();
            });

            _thread.SetApartmentState(ApartmentState.STA);
            _thread.IsBackground = true;
            _thread.Start();
            _taskScheduler = tcs.Task.Result;
        }

        /// <summary>shutdown the STA thread</summary>
        public void Dispose()
        {
            if (_taskScheduler != null)
            {
                var taskScheduler = _taskScheduler;
                _taskScheduler = null;

                // execute Application.ExitThread() on the STA thread
                Task.Factory.StartNew(
                    () => Application.ExitThread(),
                    CancellationToken.None,
                    TaskCreationOptions.None,
                    taskScheduler).Wait();

                _thread.Join();
                _thread = null;
            }
        }

        /// <summary>Task.Factory.StartNew wrappers</summary>
        public void Invoke(Action action)
        {
            Task.Factory.StartNew(action, 
                CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Wait();
        }

        public TResult Invoke<TResult>(Func<TResult> action)
        {
            return Task.Factory.StartNew(action,
                CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
        }
    }
}

【讨论】:

  • 谢谢,这正是我正在寻找的答案!它对其他控件也很有用。
猜你喜欢
  • 1970-01-01
  • 2011-04-14
  • 2013-04-18
  • 2011-02-16
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-31
相关资源
最近更新 更多