How to fix a opacity bug with DrawToBitmap on WebBrowser Control?

questions :

According to following link and my console application the method DrawToBitmap doesn’t respect opacity.

Proof link:

My HTML code :

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

My C# console code :

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

So I’m looking for alternative .NET built-in solution (no CEF, Awesomium, or any extension please) just a built-in feature of .NET to fix the bug or alternative solution to take screenshot of a web URL in my console application.

If I make WebBrowser window visible to my client and use CopyFromScreen the opacity is respected and HIDDEN TEXT isn’t showing, how ever I don’t want to make WebBrowser window visible to desktop screen.

I’m looking for a built-in solution to take a screenshot from posted URL in the question without HIDDEN TEXT. In other words a solution to respect opacity.

EDIT1: All pixels in my Bitmap class (.NET class not BMP format) has alpha value of 255. So the problem is NOT with file format. I have tried PNG and any other .NET supported format.

Complete source code (Console Template, Need to add references to System.Drawing and System.Windows.Forms

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

    static void Main(string[] args)

        w.DocumentCompleted += w_DocumentCompleted;
        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");

        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





Answer :

I’m looking for a built-in solution to take a screenshot from posted
URL in the question without HIDDEN TEXT. In other words a solution to
respect opacity.

The following code does just that: respects CSS opacity. Amongst other things, it uses a Metafile object and OleDraw API to render the web page’s image.

The test HTML:

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

The output:


The code (console app):

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

    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)
                // 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) =>

                        webBrowser.DocumentText = HTML;
                        return pageLoadedTcs.Task;

                    // 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);
                                // save the metafile as bitmap
                                metafile.Save(FILE_NAME, ImageFormat.Png);

                    // dispose of webBrowser
                    apartment.Invoke(() => webBrowser.Dispose());
                    webBrowser = null;
            catch (Exception ex)

        // 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
        static void SetFeatureBrowserFeature(string feature, uint value)
            if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
            var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
            Registry.SetValue(@"HKEY_CURRENT_USERSoftwareMicrosoftInternet ExplorerMainFeatureControl" + feature,
                appName, value, RegistryValueKind.DWord);

    // MessageLoopApartment
    // more info:

    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

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

            _thread.IsBackground = true;
            _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
                    () => Application.ExitThread(),

                _thread = null;

        /// <summary>Task.Factory.StartNew wrappers</summary>
        public void Invoke(Action 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;





Facebook Comments

Post a comment