4
votes

I'm drawing bitmaps in Direct2D. The bitmaps utilize transparency (alpha channel).

The blending looks wrong.

As a test I loaded a pure black png image with 50% transparency and drew it over a white background. The result is pixels with a Red, Green, and Blue value of 127 (0x7F7F7F). This suggests Direct2D's blend is ignoring gamma and treating the color values as if they were linear.

(Bitmaps use the regular sRGB color space and are at 32 bits per pixel, 8-bits each for Red, Green, Blue and Alpha). They are loaded in GUID_WICPixelFormat32bppPBGRA format.

In sRGB, a blend halfway between black and white is 186 (0xBABABA). This is the ideal result I want.

Can Direct2D display transparency with respect for gamma? How would I do that? Any help appreciated.

Direct2D vs Gamma-correct blend

2
I think you have it backwards. In sRGB, 50% will LOOK like halfway between black and white (in brightness, kinda). Does it look too bright or too dark? This may be due to the way it's displayed.a stray cat
My understanding is sRGB colorspace defines a nonlinear transformation between the intensity of pixels and the actual number stored. i.e. the gamma curve. For example a 8-bit color value ranges from 0-255, but due to the gamma curve a value of 127 (halfway) is displayed at approx 20% brightness. So a gamma-correct 50% blend of black and white should have a pixel value of 186 (73% full scale). So in Direct2d, my blend looks too dark.Jeff McClintock
@astraycat if you view alternating black and white pixels and compare them to a solid block, the BABABA block should be the same brightness on a properly calibrated monitor. Even on an uncalibrated monitor, BABABA should be closer than 7F7F7F.Mark Ransom

2 Answers

2
votes

Blending is properly done in linear colorspace, so the process for blending sRGB pixels should be

  1. convert to linear
  2. blend
  3. convert back to sRGB.

Note that with black or white pixels, steps (1) and (3) are no-ops and can be omitted.

See the alpha channel processing section of the PNG specification. In particular, note this:

The equation for computing a composited sample value is

output = alpha * foreground + (1-alpha) * background

where the alpha value and the input and output sample values are expressed as fractions in the range 0 to 1. This computation should be performed with intensity samples (not gamma-encoded samples)

The section contains sample C code for alpha-channel processing

At the time this question was posed, HWND Render targets (drawing to the screen) did not support linear pixel formats; however, Direct-2D HwndRender targets have now been superseded by interface ID2D1DeviceContext. These are created via IDXGIFactory2::CreateSwapChainForHwnd() and support more pixel formats like DXGI_FORMAT_B8G8R8A8_UNORM_SRGB which automatically performs the correct color-space conversions when blending (information provided by @Jeff McClintock).

0
votes

A Direct-2D 1.0 HwndRenderTarget performs blending calculations directly on pixel values. This results in errors compositing standard sRGB images utilizing an alpha-channel. The error is because Direct 2D is treating gamma-compressed intensity values as if they were linear intensity values.

Ignoring gamma results in poor quality compositing, geometry antialiasing, image-resizing and text rendering.

There is a workaround, which is to 'pre-warp' a bitmap's alpha values to compensate for the errors introduced by the blending calculation.

example: Image on left is correct compositing by performing blending in linear colorspace, center image is Direct 2D (shadow too dark and 'contrasty'), image on right is Direct2D after pre-warping the alpha channel.

Direct 2D Renders alpha blends to dark and 'contrasty'

see: https://bel.fi/alankila/lcd/alpcor.html

UPDATE!: Newer versions of Direct-2D (1.1) Supports SRGB back-buffers which perform blending correctly. Use IDXGIFactory2::CreateSwapChainForHwnd() to use the improved color-depth options.