|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
using System.Drawing.Text;
namespace System.Windows.Forms;
/// <summary>
/// This class provides API for drawing GDI text.
/// </summary>
public static class TextRenderer
{
#if DEBUG
// In various cases the DC may have already been modified, and we don't pass TextFormatFlags.PreserveGraphicsClipping
// or TextFormatFlags.PreserveGraphicsTranslateTransform flags, that set off the asserts in GetApplyStateFlags
// method. This flags allows us to skip those assert for the cases we know we don't need these flags.
internal const TextFormatFlags SkipAssertFlag = (TextFormatFlags)0x4000_0000;
#endif
internal static FONT_QUALITY DefaultQuality { get; } = GetDefaultFontQuality();
internal static Size MaxSize { get; } = new(int.MaxValue, int.MaxValue);
public static void DrawText(IDeviceContext dc, string? text, Font? font, Point pt, Color foreColor)
=> DrawTextInternal(dc, text, font, pt, foreColor, Color.Empty);
/// <summary>
/// Draws the specified text at the specified location using the specified device context, font, and color.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="pt">The <see cref="Point"/> that represents the upper-left corner of the drawn text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static void DrawText(IDeviceContext dc, ReadOnlySpan<char> text, Font font, Point pt, Color foreColor)
=> DrawTextInternal(dc, text, font, pt, foreColor, Color.Empty);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
Color backColor)
=> DrawTextInternal(dc, text, font, pt, foreColor, backColor);
/// <summary>
/// Draws the specified text at the specified location, using the specified device context, font, color, and back color.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="pt">The <see cref="Point"/> that represents the upper-left corner of the drawn text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="backColor">The <see cref="Color"/> to apply to the background area of the drawn text.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font font,
Point pt,
Color foreColor,
Color backColor)
=> DrawTextInternal(dc, text, font, pt, foreColor, backColor);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, pt, foreColor, Color.Empty, flags);
/// <summary>
/// Draws the specified text at the specified location using the specified device context, font, color, and
/// formatting instructions.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="pt">The <see cref="Point"/> that represents the upper-left corner of the drawn text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="flags">A bitwise combination of the <see cref="TextFormatFlags"/> values.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Point pt,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(
dc,
text,
font,
pt,
foreColor,
Color.Empty,
BlockModifyString(flags));
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Point pt,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, pt, foreColor, backColor, flags);
/// <summary>
/// Draws the specified text at the specified location using the specified device context, font, color, back
/// color, and formatting instructions.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="pt">The <see cref="Point"/> that represents the upper-left corner of the drawn text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="backColor">The <see cref="Color"/> to apply to the background area of the drawn text.</param>
/// <param name="flags">A bitwise combination of the <see cref="TextFormatFlags"/> values.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Point pt,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(
dc,
text,
font,
pt,
foreColor,
backColor,
BlockModifyString(flags));
public static void DrawText(IDeviceContext dc, string? text, Font? font, Rectangle bounds, Color foreColor)
=> DrawTextInternal(dc, text, font, bounds, foreColor, Color.Empty);
/// <summary>
/// Draws the specified text within the specified bounds, using the specified device context, font, and color.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="bounds">The <see cref="Rectangle"/> that represents the bounds of the text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor)
=> DrawTextInternal(dc, text, font, bounds, foreColor, Color.Empty);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor)
=> DrawTextInternal(dc, text, font, bounds, foreColor, backColor);
/// <summary>
/// Draws the specified text within the specified bounds using the specified device context, font, color, and
/// back color.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="bounds">The <see cref="Rectangle"/> that represents the bounds of the text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="backColor">The <see cref="Color"/> to apply to the background area of the drawn text.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor)
=> DrawTextInternal(dc, text, font, bounds, foreColor, backColor);
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, bounds, foreColor, Color.Empty, flags);
/// <summary>
/// Draws the specified text within the specified bounds using the specified device context, font, color, and
/// formatting instructions.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="bounds">The <see cref="Rectangle"/> that represents the bounds of the text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="flags">A bitwise combination of the <see cref="TextFormatFlags"/> values.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(
dc,
text,
font,
bounds,
foreColor,
Color.Empty,
BlockModifyString(flags));
public static void DrawText(
IDeviceContext dc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(dc, text, font, bounds, foreColor, backColor, flags);
/// <summary>
/// Draws the specified text within the specified bounds using the specified device context, font, color,
/// back color, and formatting instructions.
/// </summary>
/// <param name="dc">The device context in which to draw the text.</param>
/// <param name="text">The text to draw.</param>
/// <param name="font">The <see cref="Font"/> to apply to the drawn text.</param>
/// <param name="bounds">The <see cref="Rectangle"/> that represents the bounds of the text.</param>
/// <param name="foreColor">The <see cref="Color"/> to apply to the drawn text.</param>
/// <param name="backColor">The <see cref="Color"/> to apply to the background area of the drawn text.</param>
/// <param name="flags">A bitwise combination of the <see cref="TextFormatFlags"/> values.</param>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static void DrawText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor,
TextFormatFlags flags)
=> DrawTextInternal(
dc,
text,
font,
bounds,
foreColor,
backColor,
BlockModifyString(flags));
private static void DrawTextInternal(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Point pt,
Color foreColor,
Color backColor,
TextFormatFlags flags = TextFormatFlags.Default)
=> DrawTextInternal(dc, text, font, new Rectangle(pt, MaxSize), foreColor, backColor, flags);
internal static void DrawTextInternal(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor,
TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter)
{
ArgumentNullException.ThrowIfNull(dc);
// Avoid creating the HDC, etc if we're not going to do any drawing
if (text.IsEmpty || foreColor == Color.Transparent)
return;
// This MUST come before retrieving the HDC, which locks the Graphics object
FONT_QUALITY quality = FontQualityFromTextRenderingHint(dc);
using DeviceContextHdcScope hdc = dc.ToHdcScope(GetApplyStateFlags(dc, flags));
DrawTextInternal(hdc, text, font, bounds, foreColor, quality, backColor, flags);
}
internal static void DrawTextInternal(
PaintEventArgs e,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
TextFormatFlags flags)
=> DrawTextInternal(e, text, font, bounds, foreColor, Color.Empty, flags);
internal static void DrawTextInternal(
PaintEventArgs e,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
Color backColor,
TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter)
{
HDC hdc = e.HDC;
if (hdc.IsNull)
{
// This MUST come before retrieving the HDC, which locks the Graphics object
FONT_QUALITY quality = FontQualityFromTextRenderingHint(e.GraphicsInternal);
using DeviceContextHdcScope graphicsHdc = new(e.GraphicsInternal, applyGraphicsState: false);
DrawTextInternal(graphicsHdc, text, font, bounds, foreColor, quality, backColor, flags);
}
else
{
DrawTextInternal(hdc, text, font, bounds, foreColor, DefaultQuality, backColor, flags);
}
}
internal static void DrawTextInternal(
HDC hdc,
string? text,
Font? font,
Rectangle bounds,
Color foreColor,
FONT_QUALITY fontQuality,
TextFormatFlags flags)
=> DrawTextInternal(hdc, text, font, bounds, foreColor, fontQuality, Color.Empty, flags);
private static void DrawTextInternal(
HDC hdc,
ReadOnlySpan<char> text,
Font? font,
Rectangle bounds,
Color foreColor,
FONT_QUALITY fontQuality,
Color backColor,
TextFormatFlags flags)
{
using var hfont = GetFontOrHdcHFONT(font, fontQuality, hdc);
hdc.DrawText(text, hfont, bounds, foreColor, flags, backColor);
}
private static TextFormatFlags BlockModifyString(TextFormatFlags flags)
{
#pragma warning disable CS0618 // Type or member is obsolete - ModifyString is obsolete
if (flags.HasFlag(TextFormatFlags.ModifyString))
{
throw new ArgumentOutOfRangeException(nameof(flags), SR.TextFormatFlagsModifyStringNotAllowed);
}
#pragma warning restore CS0618
return flags;
}
public static Size MeasureText(string? text, Font? font)
=> MeasureTextInternal(text, font, MaxSize);
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with the specified font.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="font">The <see cref="Font"/> to apply to the measured text.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of text drawn on a single line with the specified font. You can
/// manipulate how the text is drawn by using one of the
/// <see cref="DrawText(IDeviceContext, ReadOnlySpan{char}, Font?, Rectangle, Color, TextFormatFlags)"/>
/// overloads that takes a <see cref="TextFormatFlags"/> parameter. For example, the default behavior of the
/// <see cref="TextRenderer"/> is to add padding to the bounding rectangle of the drawn text to accommodate
/// overhanging glyphs. If you need to draw a line of text without these extra spaces you should use the
/// versions of <see cref="DrawText(IDeviceContext, ReadOnlySpan{char}, Font, Point, Color)"/> and
/// <see cref="MeasureText(IDeviceContext, ReadOnlySpan{char}, Font?)"/> that take a Size and
/// <see cref="TextFormatFlags"/> parameter. For an example, see
/// <see cref="MeasureText(IDeviceContext, string?, Font?, Size, TextFormatFlags)"/>.
/// </returns>
public static Size MeasureText(ReadOnlySpan<char> text, Font? font)
=> MeasureTextInternal(text, font, MaxSize);
public static Size MeasureText(string? text, Font? font, Size proposedSize)
=> MeasureTextInternal(text, font, proposedSize);
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with the specified font, using the
/// specified size to create an initial bounding rectangle.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="font">The <see cref="Font"/> to apply to the measured text.</param>
/// <param name="proposedSize">The <see cref="Size"/> of the initial bounding rectangle.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of <paramref name="text"/> drawn with the specified
/// <paramref name="font"/>.
/// </returns>
public static Size MeasureText(ReadOnlySpan<char> text, Font? font, Size proposedSize)
=> MeasureTextInternal(text, font, proposedSize);
public static Size MeasureText(string? text, Font? font, Size proposedSize, TextFormatFlags flags)
=> MeasureTextInternal(text, font, proposedSize, flags);
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with the specified font and formatting
/// instructions, using the specified size to create the initial bounding rectangle for the text.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="font">The <see cref="Font"/> to apply to the measured text.</param>
/// <param name="proposedSize">The <see cref="Size"/> of the initial bounding rectangle.</param>
/// <param name="flags">The formatting instructions to apply to the measured text.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of <paramref name="text"/> drawn with the specified
/// <paramref name="font"/> and format.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static Size MeasureText(ReadOnlySpan<char> text, Font? font, Size proposedSize, TextFormatFlags flags)
=> MeasureTextInternal(text, font, proposedSize, BlockModifyString(flags));
public static Size MeasureText(IDeviceContext dc, string? text, Font? font)
=> MeasureTextInternal(dc, text, font, MaxSize);
/// <summary>
/// Provides the size, in pixels, of the specified text drawn with the specified font in the specified device
/// context.
/// </summary>
/// <param name="dc">The device context in which to measure the text.</param>
/// <param name="text">The text to measure.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of <paramref name="text"/> drawn with the specified
/// <paramref name="font"/> in the specified device context.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static Size MeasureText(IDeviceContext dc, ReadOnlySpan<char> text, Font? font)
=> MeasureTextInternal(dc, text, font, MaxSize);
public static Size MeasureText(IDeviceContext dc, string? text, Font? font, Size proposedSize)
=> MeasureTextInternal(dc, text, font, proposedSize);
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with the specified font in the specified
/// device context, using the specified size to create an initial bounding rectangle for the text.
/// </summary>
/// <param name="dc">The device context in which to measure the text.</param>
/// <param name="text">The text to measure.</param>
/// <param name="font">The <see cref="Font"/> to apply to the measured text.</param>
/// <param name="proposedSize">The <see cref="Size"/> of the initial bounding rectangle.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of <paramref name="text"/> drawn with the specified
/// <paramref name="font"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
public static Size MeasureText(IDeviceContext dc, ReadOnlySpan<char> text, Font? font, Size proposedSize)
=> MeasureTextInternal(dc, text, font, proposedSize);
public static Size MeasureText(
IDeviceContext dc,
string? text,
Font? font,
Size proposedSize,
TextFormatFlags flags)
=> MeasureTextInternal(dc, text, font, proposedSize, flags);
/// <summary>
/// Provides the size, in pixels, of the specified text when drawn with the specified device context, font,
/// and formatting instructions, using the specified size to create the initial bounding rectangle for the text.
/// </summary>
/// <param name="dc">The device context in which to measure the text.</param>
/// <param name="text">The text to measure.</param>
/// <param name="font">The <see cref="Font"/> to apply to the measured text.</param>
/// <param name="proposedSize">The <see cref="Size"/> of the initial bounding rectangle.</param>
/// <param name="flags">The formatting instructions to apply to the measured text.</param>
/// <returns>
/// The <see cref="Size"/>, in pixels, of <paramref name="text"/> drawn with the specified
/// <paramref name="font"/> and format.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="dc"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <see cref="TextFormatFlags.ModifyString"/> is set.
/// </exception>
public static Size MeasureText(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Size proposedSize,
TextFormatFlags flags)
=> MeasureTextInternal(dc, text, font, proposedSize, BlockModifyString(flags));
private static Size MeasureTextInternal(
ReadOnlySpan<char> text,
Font? font,
Size proposedSize,
TextFormatFlags flags = TextFormatFlags.Bottom)
{
if (text.IsEmpty)
return Size.Empty;
using var screen = GdiCache.GetScreenHdc();
using var hfont = GetFontOrHdcHFONT(font, FONT_QUALITY.DEFAULT_QUALITY, screen);
return screen.HDC.MeasureText(text, hfont, proposedSize, flags);
}
private static Size MeasureTextInternal(
IDeviceContext dc,
ReadOnlySpan<char> text,
Font? font,
Size proposedSize,
TextFormatFlags flags = TextFormatFlags.Bottom)
{
ArgumentNullException.ThrowIfNull(dc);
if (text.IsEmpty)
return Size.Empty;
// This MUST come before retrieving the HDC, which locks the Graphics object
FONT_QUALITY quality = FontQualityFromTextRenderingHint(dc);
// Applying state may not impact text size measurements. Rather than risk missing some
// case we'll apply as we have historically to avoid surprise regressions.
using DeviceContextHdcScope hdc = dc.ToHdcScope(GetApplyStateFlags(dc, flags));
using var hfont = GetFontOrHdcHFONT(font, quality, hdc);
return hdc.HDC.MeasureText(text, hfont, proposedSize, flags);
}
internal static Color DisabledTextColor(Color backColor)
{
if (SystemInformation.HighContrast)
{
return SystemColors.GrayText;
}
// If the color is darker than SystemColors.Control make it slightly darker,
// otherwise use the standard control dark color.
return ControlPaint.IsDarker(backColor, SystemColors.Control)
? ControlPaint.Dark(backColor)
: SystemColors.ControlDark;
}
/// <summary>
/// Attempts to match the TextRenderingHint of the specified Graphics object with a LOGFONT.lfQuality value.
/// </summary>
internal static FONT_QUALITY FontQualityFromTextRenderingHint(IDeviceContext? deviceContext)
{
if (deviceContext is not Graphics g)
{
return FONT_QUALITY.DEFAULT_QUALITY;
}
return g.TextRenderingHint switch
{
TextRenderingHint.ClearTypeGridFit => FONT_QUALITY.CLEARTYPE_QUALITY,
TextRenderingHint.AntiAliasGridFit or TextRenderingHint.AntiAlias => FONT_QUALITY.ANTIALIASED_QUALITY,
TextRenderingHint.SingleBitPerPixelGridFit => FONT_QUALITY.PROOF_QUALITY,
TextRenderingHint.SingleBitPerPixel => FONT_QUALITY.DRAFT_QUALITY,
_ => FONT_QUALITY.DEFAULT_QUALITY,
};
}
/// <summary>
/// Returns what <see cref="FontQualityFromTextRenderingHint(IDeviceContext?)"/> would return in an
/// unmodified <see cref="Graphics"/> object (i.e. the default).
/// </summary>
private static FONT_QUALITY GetDefaultFontQuality()
{
if (!SystemInformation.IsFontSmoothingEnabled)
{
return FONT_QUALITY.PROOF_QUALITY;
}
// FE_FONTSMOOTHINGCLEARTYPE = 0x0002
return SystemInformation.FontSmoothingType == 0x0002
? FONT_QUALITY.CLEARTYPE_QUALITY : FONT_QUALITY.ANTIALIASED_QUALITY;
}
/// <summary>
/// Gets the proper <see cref="ApplyGraphicsProperties"/> flags for the given <paramref name="textFormatFlags"/>.
/// </summary>
internal static ApplyGraphicsProperties GetApplyStateFlags(IDeviceContext deviceContext, TextFormatFlags textFormatFlags)
{
if (deviceContext is not Graphics graphics)
{
return ApplyGraphicsProperties.None;
}
var apply = ApplyGraphicsProperties.None;
if (textFormatFlags.HasFlag(TextFormatFlags.PreserveGraphicsClipping))
{
apply |= ApplyGraphicsProperties.Clipping;
}
if (textFormatFlags.HasFlag(TextFormatFlags.PreserveGraphicsTranslateTransform))
{
apply |= ApplyGraphicsProperties.TranslateTransform;
}
#if DEBUG
if ((textFormatFlags & SkipAssertFlag) == 0)
{
// Clipping and translation transforms applied to Graphics objects are not done on the underlying HDC.
// When we're rendering text to the HDC we, by default, should apply both. If it is *known* that these
// aren't wanted we can get a _slight_ performance benefit by not applying them and in that case the
// SkipAssertFlag bit can be set to skip this check.
//
// This application of clipping and translation is meant to make Graphics.DrawText and TextRenderer.DrawText
// roughly equivalent in the way they render.
//
// Note that there aren't flags for other transforms. Windows 9x doesn't support HDC transforms outside of
// translation (rotation for example), and this likely impacted the decision to only have a translation
// flag when this was originally written.
Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.Clipping)
|| graphics.Clip is null
|| graphics.Clip.GetHrgn(graphics) == IntPtr.Zero,
"Must preserve Graphics clipping region!");
Debug.Assert(apply.HasFlag(ApplyGraphicsProperties.TranslateTransform)
|| graphics.Transform is null
|| graphics.Transform.IsIdentity,
"Must preserve Graphics transformation!");
}
#endif
return apply;
}
/// <inheritdoc cref="GdiCache.GetHFONTScope(Font?, FONT_QUALITY)"/>
/// <summary>
/// Get a cached <see cref="HFONT"/> based off of the given <paramref name="font"/> and <paramref name="quality"/>,
/// falling back to the <paramref name="hdc"/>'s current font if <paramref name="font"/> is <see langword="null"/>.
/// </summary>
/// <param name="hdc">
/// The <see cref="HDC"/> to get the font from if <paramref name="font"/> is <see langword="null"/>.
/// </param>
private static FontCache.Scope GetFontOrHdcHFONT(Font? font, FONT_QUALITY quality, HDC hdc)
{
if (font is not null)
{
return GdiCache.GetHFONTScope(font, quality);
}
// Font is null, build off of the specified HDC's current font.
HFONT hfont = (HFONT)PInvoke.GetCurrentObject(hdc, OBJ_TYPE.OBJ_FONT);
return new FontCache.Scope(hfont);
}
}
|