how do you use Direct2D to render ClearType text on a transparent background?

2.4k Views Asked by At

can anyone tell me why ClearType is picking up the transparency of the background in the simple SharpDX sample below? And how to stop it from doing that.

it renders two lines of solid black text into a semi-transparent bitmap. the first line is rendered with Grayscale, and the second is rendered with ClearType.

EDIT: it has been suggested that the problem is that the surface is pre-multiplied. but that doesn't explain the difference between the two Grayscale/ClearType renderings.

result (win7):

enter image description here

public static void Main()
{
    var pixelFormat = SharpDX.WIC.PixelFormat.Format32bppPBGRA;

    using (var wicFactory = new ImagingFactory())
    using (var dddFactory = new SharpDX.Direct2D1.Factory())
    using (var dwFactory = new SharpDX.DirectWrite.Factory())
    using (var textFormat = new TextFormat(dwFactory, "Arial", FontWeight.Bold, FontStyle.Normal, 48))
    using (var textLayout = new TextLayout(dwFactory, "argle-bargle", textFormat, float.PositiveInfinity, float.PositiveInfinity))
    {
        var width = (int) Math.Ceiling(textLayout.Metrics.Width);
        var height = (int) Math.Ceiling(textLayout.Metrics.Height);

        using (var wicBitmap = new SharpDX.WIC.Bitmap(
            wicFactory,
            width, height * 2,
            pixelFormat,
            BitmapCreateCacheOption.CacheOnLoad))
        {
            var renderTargetProperties = new RenderTargetProperties(new SharpDX.Direct2D1.PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied));
            Color4 textColor = new Color4(0f, 0f, 0f, 1f);

            using (var renderTarget = new WicRenderTarget(dddFactory, wicBitmap, renderTargetProperties))
            using (var textBrush = new SolidColorBrush(renderTarget, textColor))
            {
                renderTarget.BeginDraw();
                renderTarget.Clear(new Color4(1f, 1f, 0f, .25f));

                renderTarget.TextAntialiasMode = TextAntialiasMode.Grayscale;
                renderTarget.DrawTextLayout(new Vector2(0, 0), textLayout, textBrush);

                renderTarget.TextAntialiasMode = TextAntialiasMode.Cleartype;
                renderTarget.DrawTextLayout(new Vector2(0, height), textLayout, textBrush);

                renderTarget.EndDraw();
            }

            var path = Path.Combine(Path.GetTempPath(), "test.png");
            using (var stream = File.OpenWrite(path))
            {
                using (var encoder = new PngBitmapEncoder(wicFactory, stream))
                using (var frameEncoder = new BitmapFrameEncode(encoder))
                {
                    frameEncoder.Initialize();
                    frameEncoder.WriteSource(wicBitmap);
                    frameEncoder.Commit();
                    encoder.Commit();
                }
            }
        }
    }
}
1

There are 1 best solutions below

1
Peter Kostov On

This unexpected result is a "normal" behavior.

As it stands in MSDN:

If you specify an alpha mode other than D2D1_ALPHA_MODE_IGNORE for a render target, the text antialiasing mode automatically changes from D2D1_TEXT_ANTIALIAS_MODE CLEARTYPE to D2D1_TEXT_ANTIALIAS_MODE GRAYSCALE.

So, your first assignment of the renderTarget.TextAntialiasMode to Grayscale is pointless, because Grayscale is its initially setted value.

Another quote from MSDN:

You can use the SetTextAntialiasMode method to change the text antialias mode back to D2D1_TEXT_ANTIALIAS_MODE CLEARTYPE, but rendering ClearType text to a transparent surface can create unpredictable results. If you want to render ClearType text to an transparent render target, we recommend that you use one of the following two techniques.

  • Use the PushAxisAlignedClip method to clip the render target to the area where the text will be rendered, then call the Clear method and specify an opaque color, then render your text.
  • Use DrawRectangle to draw an opaque rectangle behind the area where the text will be rendered.

Here is the full article: D2D1_ALPHA_MODE enumeration

So, if I have to summarize:

  1. Don't set D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE to a render target with alpha mode different from D2D1_ALPHA_MODE_IGNORE
  2. Follow the techniques from above.