File: System\Windows\Forms\Controls\ToolStrips\ToolStripRenderer.cs
Web Access
Project: src\src\System.Windows.Forms\src\System.Windows.Forms.csproj (System.Windows.Forms)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms.Layout;
 
namespace System.Windows.Forms;
 
public abstract class ToolStripRenderer
{
    private static readonly object s_renderSplitButtonBackgroundEvent = new();
    private static readonly object s_renderItemBackgroundEvent = new();
    private static readonly object s_renderItemImageEvent = new();
    private static readonly object s_renderItemTextEvent = new();
    private static readonly object s_renderToolStripBackgroundEvent = new();
    private static readonly object s_renderGripEvent = new();
    private static readonly object s_renderButtonBackgroundEvent = new();
    private static readonly object s_renderLabelBackgroundEvent = new();
    private static readonly object s_renderMenuItemBackgroundEvent = new();
    private static readonly object s_renderDropDownButtonBackgroundEvent = new();
    private static readonly object s_renderOverflowButtonBackgroundEvent = new();
    private static readonly object s_renderImageMarginEvent = new();
    private static readonly object s_renderBorderEvent = new();
    private static readonly object s_renderArrowEvent = new();
    private static readonly object s_renderToolStripStatusLabelBackgroundEvent = new();
    private static readonly object s_renderSeparatorEvent = new();
    private static readonly object s_renderItemCheckEvent = new();
    private static readonly object s_renderToolStripPanelBackgroundEvent = new();
    private static readonly object s_renderToolStripContentPanelBackgroundEvent = new();
 
    private static readonly object s_renderStatusStripSizingGripEvent = new();
 
    private static ColorMatrix? s_disabledImageColorMatrix;
 
    private EventHandlerList? _events;
    private readonly bool _isAutoGenerated;
 
    private static bool s_isScalingInitialized;
    internal int _previousDeviceDpi = ScaleHelper.InitialSystemDpi;
 
    // arrows are rendered as isosceles triangles, whose heights are half the base in order to have 45 degree angles
    // Offset2X is half of the base
    // Offset2Y is height of the isosceles triangle
    private const int OFFSET_2PIXELS = 2;
    private const int OFFSET_4PIXELS = 4;
    protected static int Offset2X = OFFSET_2PIXELS;
    protected static int Offset2Y = OFFSET_2PIXELS;
    private static int s_offset4X = OFFSET_4PIXELS;
    private static int s_offset4Y = OFFSET_4PIXELS;
 
    // Used in building up the half pyramid of rectangles that are drawn in a
    // status strip sizing grip.
    private static readonly Rectangle[] s_baseSizeGripRectangles = [
        new(8, 0, 2, 2),
        new(8, 4, 2, 2),
        new(8, 8, 2, 2),
        new(4, 4, 2, 2),
        new(4, 8, 2, 2),
        new(0, 8, 2, 2)
    ];
 
    protected ToolStripRenderer()
    {
    }
 
    internal ToolStripRenderer(bool isAutoGenerated)
    {
        _isAutoGenerated = isAutoGenerated;
    }
 
    // Used in building disabled images.
    private static ColorMatrix DisabledImageColorMatrix
    {
        get
        {
            if (s_disabledImageColorMatrix is null)
            {
                // This is the result of a GreyScale matrix multiplied by a transparency matrix of .5
 
                float[][] greyscale =
                [
                    [0.2125f, 0.2125f, 0.2125f, 0, 0],
                    [0.2577f, 0.2577f, 0.2577f, 0, 0],
                    [0.0361f, 0.0361f, 0.0361f, 0, 0],
                    [0, 0, 0, 1, 0],
                    [0.38f, 0.38f, 0.38f, 0, 1],
                ];
 
                float[][] transparency =
                [
                    [1, 0, 0, 0, 0],
                    [0, 1, 0, 0, 0],
                    [0, 0, 1, 0, 0],
                    [0, 0, 0, .7F, 0],
                    [0, 0, 0, 0, 0],
                ];
 
                s_disabledImageColorMatrix = ControlPaint.MultiplyColorMatrix(transparency, greyscale);
            }
 
            return s_disabledImageColorMatrix;
        }
    }
 
    /// <summary>
    ///  Gets the list of event handlers that are attached to this component.
    /// </summary>
    private EventHandlerList Events
    {
        get
        {
            _events ??= new EventHandlerList();
 
            return _events;
        }
    }
 
    internal bool IsAutoGenerated
    {
        get { return _isAutoGenerated; }
    }
 
    // if we're in a low contrast, high resolution situation, use this renderer under the covers instead.
    internal virtual ToolStripRenderer? RendererOverride
    {
        get
        {
            return null;
        }
    }
 
    public event ToolStripArrowRenderEventHandler RenderArrow
    {
        add => AddHandler(s_renderArrowEvent, value);
        remove => RemoveHandler(s_renderArrowEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripRenderEventHandler RenderToolStripBackground
    {
        add => AddHandler(s_renderToolStripBackgroundEvent, value);
        remove => RemoveHandler(s_renderToolStripBackgroundEvent, value);
    }
 
    public event ToolStripPanelRenderEventHandler RenderToolStripPanelBackground
    {
        add => AddHandler(s_renderToolStripPanelBackgroundEvent, value);
        remove => RemoveHandler(s_renderToolStripPanelBackgroundEvent, value);
    }
 
    public event ToolStripContentPanelRenderEventHandler RenderToolStripContentPanelBackground
    {
        add => AddHandler(s_renderToolStripContentPanelBackgroundEvent, value);
        remove => RemoveHandler(s_renderToolStripContentPanelBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripRenderEventHandler RenderToolStripBorder
    {
        add => AddHandler(s_renderBorderEvent, value);
        remove => RemoveHandler(s_renderBorderEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderButtonBackground
    {
        add => AddHandler(s_renderButtonBackgroundEvent, value);
        remove => RemoveHandler(s_renderButtonBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderDropDownButtonBackground
    {
        add => AddHandler(s_renderDropDownButtonBackgroundEvent, value);
        remove => RemoveHandler(s_renderDropDownButtonBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderOverflowButtonBackground
    {
        add => AddHandler(s_renderOverflowButtonBackgroundEvent, value);
        remove => RemoveHandler(s_renderOverflowButtonBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripGripRenderEventHandler RenderGrip
    {
        add => AddHandler(s_renderGripEvent, value);
        remove => RemoveHandler(s_renderGripEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderItemBackground
    {
        add => AddHandler(s_renderItemBackgroundEvent, value);
        remove => RemoveHandler(s_renderItemBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Draws the split button
    /// </summary>
    public event ToolStripItemImageRenderEventHandler RenderItemImage
    {
        add => AddHandler(s_renderItemImageEvent, value);
        remove => RemoveHandler(s_renderItemImageEvent, value);
    }
 
    /// <summary>
    ///  Draws the checkmark
    /// </summary>
    public event ToolStripItemImageRenderEventHandler RenderItemCheck
    {
        add => AddHandler(s_renderItemCheckEvent, value);
        remove => RemoveHandler(s_renderItemCheckEvent, value);
    }
 
    /// <summary>
    ///  Draws the split button
    /// </summary>
    public event ToolStripItemTextRenderEventHandler RenderItemText
    {
        add => AddHandler(s_renderItemTextEvent, value);
        remove => RemoveHandler(s_renderItemTextEvent, value);
    }
 
    public event ToolStripRenderEventHandler RenderImageMargin
    {
        add => AddHandler(s_renderImageMarginEvent, value);
        remove => RemoveHandler(s_renderImageMarginEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderLabelBackground
    {
        add => AddHandler(s_renderLabelBackgroundEvent, value);
        remove => RemoveHandler(s_renderLabelBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderMenuItemBackground
    {
        add => AddHandler(s_renderMenuItemBackgroundEvent, value);
        remove => RemoveHandler(s_renderMenuItemBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Draws the split button
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderToolStripStatusLabelBackground
    {
        add => AddHandler(s_renderToolStripStatusLabelBackgroundEvent, value);
        remove => RemoveHandler(s_renderToolStripStatusLabelBackgroundEvent, value);
    }
 
    /// <summary>
    ///  Occurs when the display style has changed
    /// </summary>
    public event ToolStripRenderEventHandler RenderStatusStripSizingGrip
    {
        add => AddHandler(s_renderStatusStripSizingGripEvent, value);
        remove => RemoveHandler(s_renderStatusStripSizingGripEvent, value);
    }
 
    /// <summary>
    ///  Draws the split button
    /// </summary>
    public event ToolStripItemRenderEventHandler RenderSplitButtonBackground
    {
        add => AddHandler(s_renderSplitButtonBackgroundEvent, value);
        remove => RemoveHandler(s_renderSplitButtonBackgroundEvent, value);
    }
 
    public event ToolStripSeparatorRenderEventHandler RenderSeparator
    {
        add => AddHandler(s_renderSeparatorEvent, value);
        remove => RemoveHandler(s_renderSeparatorEvent, value);
    }
 
    #region EventHandlerSecurity
    ///  -----------------------------------------------------------------------------
    ///
    private void AddHandler(object key, Delegate value)
    {
        Events.AddHandler(key, value);
    }
 
    private void RemoveHandler(object key, Delegate value)
    {
        Events.RemoveHandler(key, value);
    }
    #endregion
 
    public static Image CreateDisabledImage(Image normalImage)
    {
        return CreateDisabledImage(normalImage, null);
    }
 
    public void DrawArrow(ToolStripArrowRenderEventArgs e)
    {
        OnRenderArrow(e);
 
        if (Events[s_renderArrowEvent] is ToolStripArrowRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawToolStripBackground(ToolStripRenderEventArgs e)
    {
        OnRenderToolStripBackground(e);
 
        if (Events[s_renderToolStripBackgroundEvent] is ToolStripRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawGrip(ToolStripGripRenderEventArgs e)
    {
        OnRenderGrip(e);
        if (Events[s_renderGripEvent] is ToolStripGripRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    public void DrawItemBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderItemBackground(e);
 
        if (Events[s_renderItemBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawImageMargin(ToolStripRenderEventArgs e)
    {
        OnRenderImageMargin(e);
 
        if (Events[s_renderImageMarginEvent] is ToolStripRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawLabelBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderLabelBackground(e);
        if (Events[s_renderLabelBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    public void DrawButtonBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderButtonBackground(e);
 
        if (Events[s_renderButtonBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    public void DrawToolStripBorder(ToolStripRenderEventArgs e)
    {
        OnRenderToolStripBorder(e);
 
        if (Events[s_renderBorderEvent] is ToolStripRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    public void DrawDropDownButtonBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderDropDownButtonBackground(e);
 
        if (Events[s_renderDropDownButtonBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    public void DrawOverflowButtonBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderOverflowButtonBackground(e);
 
        if (Events[s_renderOverflowButtonBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw image
    /// </summary>
    public void DrawItemImage(ToolStripItemImageRenderEventArgs e)
    {
        OnRenderItemImage(e);
 
        if (Events[s_renderItemImageEvent] is ToolStripItemImageRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw image
    /// </summary>
    public void DrawItemCheck(ToolStripItemImageRenderEventArgs e)
    {
        OnRenderItemCheck(e);
 
        if (Events[s_renderItemCheckEvent] is ToolStripItemImageRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw text
    /// </summary>
    public void DrawItemText(ToolStripItemTextRenderEventArgs e)
    {
        OnRenderItemText(e);
 
        if (Events[s_renderItemTextEvent] is ToolStripItemTextRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    public void DrawMenuItemBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderMenuItemBackground(e);
 
        if (Events[s_renderMenuItemBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawSplitButton(ToolStripItemRenderEventArgs e)
    {
        OnRenderSplitButtonBackground(e);
 
        if (Events[s_renderSplitButtonBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the background color
    /// </summary>
    public void DrawToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e)
    {
        OnRenderToolStripStatusLabelBackground(e);
 
        if (Events[s_renderToolStripStatusLabelBackgroundEvent] is ToolStripItemRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    //
    public void DrawStatusStripSizingGrip(ToolStripRenderEventArgs e)
    {
        OnRenderStatusStripSizingGrip(e);
 
        if (Events[s_renderStatusStripSizingGripEvent] is ToolStripRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    /// <summary>
    ///  Draw the separator
    /// </summary>
    public void DrawSeparator(ToolStripSeparatorRenderEventArgs e)
    {
        OnRenderSeparator(e);
        if (Events[s_renderSeparatorEvent] is ToolStripSeparatorRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    public void DrawToolStripPanelBackground(ToolStripPanelRenderEventArgs e)
    {
        OnRenderToolStripPanelBackground(e);
        if (Events[s_renderToolStripPanelBackgroundEvent] is ToolStripPanelRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    public void DrawToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e)
    {
        OnRenderToolStripContentPanelBackground(e);
        if (Events[s_renderToolStripContentPanelBackgroundEvent] is ToolStripContentPanelRenderEventHandler eh)
        {
            eh(this, e);
        }
    }
 
    // consider make public
    internal virtual Region? GetTransparentRegion(ToolStrip toolStrip)
    {
        return null;
    }
 
    protected internal virtual void Initialize(ToolStrip toolStrip)
    {
    }
 
    protected internal virtual void InitializePanel(ToolStripPanel toolStripPanel)
    {
    }
 
    protected internal virtual void InitializeContentPanel(ToolStripContentPanel contentPanel)
    {
    }
 
    protected internal virtual void InitializeItem(ToolStripItem item)
    {
    }
 
    protected static void ScaleArrowOffsetsIfNeeded()
    {
        if (s_isScalingInitialized)
        {
            return;
        }
 
        Offset2X = ScaleHelper.ScaleToInitialSystemDpi(OFFSET_2PIXELS);
        Offset2Y = ScaleHelper.ScaleToInitialSystemDpi(OFFSET_2PIXELS);
        s_offset4X = ScaleHelper.ScaleToInitialSystemDpi(OFFSET_4PIXELS);
        s_offset4Y = ScaleHelper.ScaleToInitialSystemDpi(OFFSET_4PIXELS);
 
        s_isScalingInitialized = true;
    }
 
    protected static void ScaleArrowOffsetsIfNeeded(int dpi)
    {
        Offset2X = ScaleHelper.ScaleToDpi(OFFSET_2PIXELS, dpi);
        Offset2Y = ScaleHelper.ScaleToDpi(OFFSET_2PIXELS, dpi);
        s_offset4X = ScaleHelper.ScaleToDpi(OFFSET_4PIXELS, dpi);
        s_offset4Y = ScaleHelper.ScaleToDpi(OFFSET_4PIXELS, dpi);
    }
 
    protected virtual void OnRenderArrow(ToolStripArrowRenderEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderArrow(e);
            return;
        }
 
        Graphics g = e.Graphics;
        Rectangle dropDownRect = e.ArrowRectangle;
        using var brush = e.ArrowColor.GetCachedSolidBrushScope();
        Point middle = new(dropDownRect.Left + dropDownRect.Width / 2, dropDownRect.Top + dropDownRect.Height / 2);
        // if the width is odd - favor pushing it over one pixel right.
        // middle.X += (dropDownRect.Width % 2);
 
        Point[]? arrow = null;
 
        // We need to check for null here, since at design time at this point Item can be null.
        if (e.Item is not null && e.Item.DeviceDpi != _previousDeviceDpi && ScaleHelper.IsThreadPerMonitorV2Aware)
        {
            _previousDeviceDpi = e.Item.DeviceDpi;
            ScaleArrowOffsetsIfNeeded(e.Item.DeviceDpi);
        }
        else
        {
            ScaleArrowOffsetsIfNeeded();
        }
 
        // using (offset4X - Offset2X) instead of (Offset2X) to compensate for rounding error in scaling
        int horizontalOffset = ScaleHelper.IsScalingRequirementMet ? s_offset4X - Offset2X : Offset2X;
 
        arrow = e.Direction switch
        {
            ArrowDirection.Up =>
            [
                new(middle.X - Offset2X, middle.Y + 1),
                new(middle.X + Offset2X + 1, middle.Y + 1),
                new(middle.X, middle.Y - Offset2Y)
            ],
            ArrowDirection.Left =>
            [
                new(middle.X + Offset2X, middle.Y - s_offset4Y),
                new(middle.X + Offset2X, middle.Y + s_offset4Y),
                new(middle.X - horizontalOffset, middle.Y)
            ],
            ArrowDirection.Right =>
            [
                new(middle.X - Offset2X, middle.Y - s_offset4Y),
                new(middle.X - Offset2X, middle.Y + s_offset4Y),
                new(middle.X + horizontalOffset, middle.Y)
            ],
            _ =>
            [
                new(middle.X - Offset2X, middle.Y - 1),
                new(middle.X + Offset2X + 1, middle.Y - 1),
                new(middle.X, middle.Y + Offset2Y)
            ],
        };
        g.FillPolygon(brush, arrow);
    }
 
    /// <summary>
    ///  Draw the ToolStrip background. ToolStrip users should override this if they want to draw differently.
    /// </summary>
    protected virtual void OnRenderToolStripBackground(ToolStripRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderToolStripBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the border around the ToolStrip. This should be done as the last step.
    /// </summary>
    protected virtual void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderToolStripBorder(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the grip. ToolStrip users should override this if they want to draw differently.
    /// </summary>
    protected virtual void OnRenderGrip(ToolStripGripRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderGrip(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the items background
    /// </summary>
    protected virtual void OnRenderItemBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderItemBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the items background
    /// </summary>
    protected virtual void OnRenderImageMargin(ToolStripRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderImageMargin(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the button background
    /// </summary>
    protected virtual void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderButtonBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the button background
    /// </summary>
    protected virtual void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderDropDownButtonBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the button background
    /// </summary>
    protected virtual void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderOverflowButtonBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the item's image. ToolStrip users should override this function to change the
    ///  drawing of all images.
    /// </summary>
    protected virtual void OnRenderItemImage(ToolStripItemImageRenderEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderItemImage(e);
            return;
        }
 
        Rectangle imageRect = e.ImageRectangle;
        Image? image = e.Image;
 
        if (imageRect != Rectangle.Empty && image is not null)
        {
            bool disposeImage = false;
            if (e.ShiftOnPress && e.Item is not null && e.Item.Pressed)
            {
                imageRect.X++;
            }
 
            if (e.Item is not null && !e.Item.Enabled)
            {
                image = CreateDisabledImage(image, e.ImageAttributes);
                disposeImage = true;
            }
 
            if (e.Item?.ImageScaling == ToolStripItemImageScaling.None)
            {
                e.Graphics.DrawImage(image, imageRect, new Rectangle(Point.Empty, imageRect.Size), GraphicsUnit.Pixel);
            }
            else
            {
                e.Graphics.DrawImage(image, imageRect);
            }
 
            if (disposeImage)
            {
                image.Dispose();
            }
        }
    }
 
    protected virtual void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderItemCheck(e);
            return;
        }
 
        Rectangle imageRect = e.ImageRectangle;
        Image? image = e.Image;
 
        if (imageRect != Rectangle.Empty && image is not null)
        {
            if (e.Item is not null)
            {
                if (!e.Item.Enabled)
                {
                    image = CreateDisabledImage(image, e.ImageAttributes);
                }
 
                if (SystemInformation.HighContrast && image is Bitmap bitmap)
                {
                    Color backgroundColor = e.Item.Selected ? SystemColors.Highlight : e.Item.BackColor;
 
                    if (ControlPaint.IsDark(backgroundColor))
                    {
                        Image invertedImage = ControlPaint.CreateBitmapWithInvertedForeColor(bitmap, e.Item.BackColor);
                        image = invertedImage;
                    }
                }
            }
 
            e.Graphics.DrawImage(image, imageRect, 0, 0, imageRect.Width,
            imageRect.Height, GraphicsUnit.Pixel, e.ImageAttributes);
        }
    }
 
    /// <summary>
    ///  Draw the item's text. ToolStrip users should override this function to change the
    ///  drawing of all text.
    /// </summary>
    protected virtual void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderItemText(e);
            return;
        }
 
        ToolStripItem? item = e.Item;
        Graphics g = e.Graphics;
        Color textColor = e.TextColor;
        Font? textFont = e.TextFont;
        string? text = e.Text;
        Rectangle textRect = e.TextRectangle;
        TextFormatFlags textFormat = e.TextFormat;
        // if we're disabled draw in a different color.
        textColor = (item is not null && item.Enabled) ? textColor : SystemColors.GrayText;
 
        if (e.TextDirection != ToolStripTextDirection.Horizontal && textRect.Width > 0 && textRect.Height > 0)
        {
            // Perf: this is a bit heavy handed.. perhaps we can share the bitmap.
            Size textSize = LayoutUtils.FlipSize(textRect.Size);
            using Bitmap textBmp = new(textSize.Width, textSize.Height, PixelFormat.Format32bppPArgb);
            using Graphics textGraphics = Graphics.FromImage(textBmp);
            // now draw the text..
            textGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
            TextRenderer.DrawText(textGraphics, text, textFont, new Rectangle(Point.Empty, textSize), textColor, textFormat);
            textBmp.RotateFlip((e.TextDirection == ToolStripTextDirection.Vertical90) ? RotateFlipType.Rotate90FlipNone : RotateFlipType.Rotate270FlipNone);
            g.DrawImage(textBmp, textRect);
        }
        else
        {
            TextRenderer.DrawText(g, text, textFont, textRect, textColor, textFormat);
        }
    }
 
    /// <summary>
    ///  Draw the button background
    /// </summary>
    protected virtual void OnRenderLabelBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderLabelBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draw the items background
    /// </summary>
    protected virtual void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderMenuItemBackground(e);
            return;
        }
    }
 
    /// <summary>
    ///  Draws a toolbar separator. ToolStrip users should override this function to change the
    ///  drawing of all separators.
    /// </summary>
    protected virtual void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderSeparator(e);
            return;
        }
    }
 
    protected virtual void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderToolStripPanelBackground(e);
            return;
        }
    }
 
    protected virtual void OnRenderToolStripContentPanelBackground(ToolStripContentPanelRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderToolStripContentPanelBackground(e);
            return;
        }
    }
 
    protected virtual void OnRenderToolStripStatusLabelBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderToolStripStatusLabelBackground(e);
            return;
        }
    }
 
    protected virtual void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderStatusStripSizingGrip(e);
            return;
        }
 
        Graphics g = e.Graphics;
 
        // we have a set of stock rectangles. Translate them over to where the grip is to be drawn
        // for the white set, then translate them up and right one pixel for the grey.
 
        if (e.ToolStrip is StatusStrip statusStrip)
        {
            Rectangle sizeGripBounds = statusStrip.SizeGripBounds;
 
            if (!LayoutUtils.IsZeroWidthOrHeight(sizeGripBounds))
            {
                Rectangle[] whiteRectangles = new Rectangle[s_baseSizeGripRectangles.Length];
                Rectangle[] greyRectangles = new Rectangle[s_baseSizeGripRectangles.Length];
 
                for (int i = 0; i < s_baseSizeGripRectangles.Length; i++)
                {
                    Rectangle baseRect = s_baseSizeGripRectangles[i];
                    if (statusStrip.RightToLeft == RightToLeft.Yes)
                    {
                        baseRect.X = sizeGripBounds.Width - baseRect.X - baseRect.Width;
                    }
 
                    baseRect.Offset(sizeGripBounds.X, sizeGripBounds.Bottom - 12 /*height of pyramid (10px) + 2px padding from bottom*/);
                    whiteRectangles[i] = baseRect;
                    if (statusStrip.RightToLeft == RightToLeft.Yes)
                    {
                        baseRect.Offset(1, -1);
                    }
                    else
                    {
                        baseRect.Offset(-1, -1);
                    }
 
                    greyRectangles[i] = baseRect;
                }
 
                g.FillRectangles(SystemBrushes.ButtonHighlight, whiteRectangles);
                g.FillRectangles(SystemBrushes.ButtonShadow, greyRectangles);
            }
        }
    }
 
    /// <summary>
    ///  Draw the item's background.
    /// </summary>
    protected virtual void OnRenderSplitButtonBackground(ToolStripItemRenderEventArgs e)
    {
        if (RendererOverride is not null)
        {
            RendererOverride.OnRenderSplitButtonBackground(e);
            return;
        }
    }
 
    // Only paint background effects if no BackColor has been set or no background image has been set.
    internal static bool ShouldPaintBackground(Control control)
    {
        return (control.RawBackColor == Color.Empty && control.BackgroundImage is null);
    }
 
    private static Bitmap CreateDisabledImage(Image normalImage, ImageAttributes? imgAttrib)
    {
        ArgumentNullException.ThrowIfNull(normalImage);
 
        imgAttrib ??= new ImageAttributes();
 
        imgAttrib.ClearColorKey();
        imgAttrib.SetColorMatrix(DisabledImageColorMatrix);
 
        Size size = normalImage.Size;
        Bitmap disabledBitmap = new(size.Width, size.Height);
        using (Graphics graphics = Graphics.FromImage(disabledBitmap))
        {
            graphics.DrawImage(normalImage,
                               new Rectangle(0, 0, size.Width, size.Height),
                               0, 0, size.Width, size.Height,
                               GraphicsUnit.Pixel,
                               imgAttrib);
        }
 
        return disabledBitmap;
    }
}