Jump to content

Antialiased color wheel


midora

Recommended Posts

Rick (or who else has an idea about it :-).

 

The color wheel in Paint.NET has an antialiased border. This seems to be nothing special but if you are using a PathGradientBrush to fill the wheel the border is not antialiased.

 

MSDN tells:

The smoothing mode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing). One exception is that path gradient brushes do not obey the smoothing mode. Areas filled using a PathGradientBrush are rendered the same way (aliased) regardless of the SmoothingMode property.

 

OK, what I'm doing in the moment is to draw a circle in the background color around the wheel. This works because the antialiased circle smooths the border of the wheel.

 

Is there a better solution (especially for the case that the background is not solid?

 

midoras signature.gif

Link to comment
Share on other sites

Hmm ... try using Reflector to peek at what Paint.NET's doing.

 

For awhile I think I was just drawing the wheel at 2x the size and then blitting to the screen at 1/2 that size, which leverages bilinear/bicubic sampling to give the illusion of antialiasing.

 

4.0 does something different, it's worth checking out too. You want the class called ColorWheel.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Up to now I tried to avoid disassembly of Paint.NET code.

I'm pretty sure it is worth because most of the stuff I touched in Paint.NET is well designed and not just hacked ;-)

So snippet would be fine but it is not important.

Just needed for a color sheme selector.

midoras signature.gif

Link to comment
Share on other sites

  • 10 months later...

Took me some time to come back to this issue but now I figured out why anti-aliasing didn't worked.

The problem is that the radius of the surrounding colors must be one pixel larger than the radius of the circle.

No idea why MSDN tells that the smoothing mode does not work in this case.

I tested it on several computers yesterday evening and it looks fine.

 

Here is some code.

namespace System.Drawing
{
    using System;
    using System.Drawing.Drawing2D;

    public static partial class GraphicsExtensions
    {
        // ----------------------------------------------------------------------
        /// <summary>
        /// Draws a color ellipse inside the bounds of a rectangle,
        /// zeroDeg is the angle of the color red.
        /// </summary>
        public static void BoundedFillColorEllipse(this Graphics g, Rectangle bounds)
        {
            BoundedFillColorEllipse(g, bounds, 0, 60);
            // 0 means red is at the center of the right border
            // 60 is a compromise between quality and speed
        }

        // ----------------------------------------------------------------------
        /// <summary>
        /// Draws a color ellipse inside the bounds of a rectangle,
        /// zeroDeg is the angle of the color red.
        /// </summary>
        public static void BoundedFillColorEllipse(this Graphics g, Rectangle bounds, float zeroDeg)
        {
            BoundedFillColorEllipse(g, bounds, zeroDeg, 60);
            // 60 is a compromise between quality and speed
        }

        // ----------------------------------------------------------------------
        /// <summary>
        /// Draws a color ellipse inside the bounds of a rectangle,
        /// zeroDeg is the angle of the color red (use -120 to get the warm colors in the top half)
        /// nColor defines the number of different colors at the border.
        /// A large value provides good quality but a bad speed (and vice versa). 60 is a good compromise.
        /// </summary>
        public static void BoundedFillColorEllipse(this Graphics g, Rectangle bounds, float zeroDeg, int nColors)
        {
            SmoothingMode oldSmoothingMode = g.SmoothingMode;
            PixelOffsetMode oldPixelOffsetMode = g.PixelOffsetMode;

            // Get radius and center of rectangle
            SizeF radius = new SizeF(bounds.Width / 2f, bounds.Height / 2f);
            PointF center = (PointF)bounds.Location + radius;

            // Get locations and colors of the SurroundColors
            // We have to increase the radius a bit or antialiasing will not work as expected (increment should be >= 1 pixel)
            PointF[] points = GetEllipsePoints(center, radius + new SizeF(1f, 1f), zeroDeg, nColors);
            Color[] colors = GetEllipseColors(nColors);

            // Fill the rectangle with a path gradient
            using (PathGradientBrush pgb = new PathGradientBrush(points))
            {
                pgb.CenterColor = new HsvColor(0, 0, 100).ToColor();    // White
                pgb.CenterPoint = center;
                pgb.SurroundColors = colors;

                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.PixelOffsetMode = PixelOffsetMode.Half;
                g.FillEllipse(pgb, bounds);
            }

            g.PixelOffsetMode = oldPixelOffsetMode;
            g.SmoothingMode = oldSmoothingMode;
        }

        // ----------------------------------------------------------------------
        /// <summary>
        /// Calculates points at the ellipse circumference with equal angle distance
        /// </summary>
        public static PointF[] GetEllipsePoints(PointF center, SizeF radius, float zeroDeg, int nPoints)
        {
            double zeroRad = zeroDeg * Math.PI / 180;

            var points = new PointF[nPoints];

            for (int i = 0; i < nPoints; i++)
            {
                double theta = ((double)i / nPoints) * 2 * Math.PI + zeroRad;
                PointF p = SphericalToCartesian(radius, theta);
                p.X += center.X;
                p.Y += center.Y;
                points[i] = p;
            }

            return points;
        }


        // ----------------------------------------------------------------------
        /// <summary>
        /// Calculates colors at the ellipse circumference with equal angle distance
        /// </summary>
        public static Color[] GetEllipseColors(int nColors)
        {
            Color[] colors = new Color[nColors];
            for (int i = 0; i < nColors; i++)
            {
                int hue = (i * 360) / nColors;
                colors[i] = new HsvColor(hue, 100, 100).ToColor();  // max saturation and value
            }
            return colors;
        }

        // ----------------------------------------------------------------------
        /// <summary>
        /// Converts from spherical to cartesion coordinates
        /// </summary>
        public static PointF SphericalToCartesian(SizeF radius, double theta)
        {
            float x = radius.Width * (float)Math.Cos(theta);
            float y = radius.Height * (float)Math.Sin(theta);
            return new PointF(x, y);
        }

    }
}

midoras signature.gif

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...