ultralight-ux / Ultralight

Lightweight, high-performance HTML renderer for game and app developers.
https://ultralig.ht
4.66k stars 195 forks source link

Issue with CPU Renderer when using white text on a transparent background #358

Open wobbier opened 3 years ago

wobbier commented 3 years ago

So the issue I'm experiencing is when using white text on a transparent background while using the C++ CPU renderer.

I've tried 1.2 and this 1.3 commit.

The HTML is just a simple div with text and 'body { background: transparent; color: white; }' for the CSS

The raw output of bitmap->WritePNG(path);

WritePNG-Output

I have a GitHub dark mode on so adding the raw image on top of a white background for clarity:

WritePNG-WITHBG

The final pass of my game view:

Game

1zun4secondary commented 3 years ago

We have the same issue with our SVG logo on CPU renderer. image

adamjs commented 3 years ago

Hello!

Sorry it took so long to get back to you-- the reason for the black-fringe is because Bitmaps are actually encoded with premultiplied alpha, see the following note about color-space in the in-header documentation:

  /// Blue Green Red Alpha channels, 32-bits per pixel.
  ///
  /// Encoding: 8-bits per channel, unsigned normalized.
  ///
  /// Color-space: sRGB gamma with premultiplied linear alpha channel.
  ///
  kBitmapFormat_BGRA8_UNORM_SRGB,

We use premultiplied alpha throughout Ultralight for a number of reasons, you can read up a bit more here: https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre

When displaying premultipled alpha bitmaps in your engine you'll need to make sure your blend function is set correctly.

For OpenGL: glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

For D3D11: See AppCore's D3D11 blend state here.

This dive into alpha compositing did identify a bug in Bitmap::WritePNG-- the PNG format expects straight alpha (eg, RGB channels should not be pre-multiplied with the alpha channel). Rather than modifying Bitmap::WritePNG (which is primarily used for debug to inspect the state of the bitmap), I've decided to add a new function Bitmap::ConvertToStraightAlpha which can be called before calling Bitmap::WritePNG to encode transparency in a format that most image viewers expect.

To verify the results are accurate, here is a render of the following HTML with the CPU renderer, written to disk via Bitmap::ConvertToStraightAlpha() and Bitmap::WritePNG:

    <html>
      <head>
        <style type="text/css">
          body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            color: white;
            font-family: Arial;
            /* Linear gradient from seafoam green to fully transparent */
            background: linear-gradient(rgba(51, 253, 184, 1.0), rgba(51, 253, 184, 0.0));
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 4em;
          }
        </style>
      </head>
      <body>
          <h1>Hello World!</h1>
      </body>
    </html>

result