public unsafe void FillCircle(SimpleImage img, uint clr, Circle2D circle)
        {
            // Optimization: For composite-blending, we don't have do draw anything if the
            // drawing color has an alpha value of zero
            if (0 == (clr & 0xFF000000))
            {
                return;
            }

            // Soften radius (hard-coded for now)
            float sr = 2.0f;

            // Compute the inclusive y-starting point
            int ystart = (int)Math.Floor(circle.MinY);
            // Compute the inclusive y-ending point
            int yend = (int)Math.Ceiling(circle.MaxY);

            // Do a quick check to see if we're completely off the image
            if (yend < 0 || ystart >= img.Height)
            {
                return;
            }

            // Constrain y-values, if needed
            if (ystart < 0)
            {
                ystart = 0;
            }
            if (yend > img.Height - 1)
            {
                yend = img.Height - 1;
            }

            // Compute a float alpha value in the range [0.0, 1.0]
            float alpha = (float)(clr >> 24) / 255.0f;

            // Loop through rows of pixels
            for (; ystart <= yend; ystart++)
            {
                // Compute intersections for this row
                float minx, maxx;
                circle.HLineIntersection((float)ystart, out minx, out maxx);

                // Skip to the next row if there was no intersection
                if (float.IsNaN(minx) || float.IsNaN(maxx))
                {
                    continue;
                }

                // Compute the starting point on the left edge of the circle
                int xLeft = (int)Math.Floor(minx);
                // Skip this row if it's off the image
                if (xLeft >= img.Width)
                {
                    continue;
                }
                // Constrain if necessary
                if (xLeft < 0)
                {
                    xLeft = 0;
                }

                // Now compute the starting point on the right edge of the circle
                int xRight = (int)Math.Ceiling(maxx);
                // Skip if we're completely off the image
                if (xRight < 0)
                {
                    continue;
                }
                // Constrain, if necessary
                if (xRight >= img.Width)
                {
                    xRight = img.Width - 1;
                }

                // Make a pointer to this row of pixels
                uint* row = &img.Pixels[ystart * img.Width + xLeft];

                // Draw from the left edge until we hit a point where we're no longer within
                // the softening radius distance from the edge
                while (xLeft <= xRight)
                {
                    float dist = circle.DistanceFromCenter((float)xLeft, (float)ystart);
                    if (dist >= circle.Radius)
                    {
                        // Advance to next pixel
                        row++;
                        xLeft++;
                        continue;
                    }
                    if (dist <= circle.Radius - sr)
                    {
                        break;
                    }

                    // Compute the alpha value
                    float newAFloat = (circle.Radius - dist) / sr * alpha * 255.0f;
                    // Blend
                    BlendComposite((clr & 0x00FFffFF) | (((uint)newAFloat) << 24), row);

                    // Advance to next pixel
                    row++;
                    xLeft++;

                    // See if we end up heading off the right edge of the image
                    if (xLeft >= img.Width)
                    {
                        break;
                    }
                }

                // If we drew pixels until we went off the image, then are done with this row
                if (xLeft >= img.Width)
                {
                    continue;
                }

                // It's possible, depending on the softening radius and which part of the circle we're
                // rendering, that the entire row has already been processed. Remember that xLeft at
                // this point is one to the right of the last pixel that was processed.
                if (xRight < xLeft)
                {
                    continue;
                }

                // Set the pointer to where we're starting
                row = &img.Pixels[ystart * img.Width + xRight];

                // Draw from the right edge until we hit a point where we're:
                //  a) no longer within the softening radius distance from the edge
                //  b) we're off the image (xRight < 0)
                //  or
                //  c) we're less than xLeft
                while (xRight >= 0)
                {
                    float dist = circle.DistanceFromCenter((float)xRight, (float)ystart);
                    if (dist >= circle.Radius)
                    {
                        // Advance to next pixel
                        row--;
                        xRight--;
                        continue;
                    }
                    if (dist <= circle.Radius - sr)
                    {
                        break;
                    }

                    // Compute the alpha value
                    float newAFloat = (circle.Radius - dist) / sr * alpha * 255.0f;
                    // Blend
                    BlendComposite((clr & 0x00FFffFF) | (((uint)newAFloat) << 24), row);

                    // Advance to next pixel
                    row--;
                    xRight--;

                    if (xRight < xLeft)
                    {
                        break;
                    }
                }

                // At this point we've processed from the left and right edges of the circle
                // on this row and have taken care of all pixels that need edge softening. All
                // that remains is filling pixels in between where we left off on the left and
                // right.
                if (xRight >= xLeft)
                {
                    DrawHorizontalLine(xLeft, xRight, ystart, clr, img.Pixels, img.Width, img.Height,
                        BlendMode.AlphaComposite);
                }
            }
        }
        protected unsafe void FrameCircle_Composite(Circle2D circle, float frameThickness,
            uint clr, uint* pxls, int w, int h)
        {
            // Softening radius (hard-coded for now)
            float sr = 2.0f;

            // Handle case where the frame is so thick that we just fill the whole circle
            if (frameThickness / 2.0f >= circle.Radius + sr)
            {
                Circle2D newCircle = new Circle2D(circle.Center, circle.Radius + frameThickness / 2f);
                FillCircle(new SimpleImage(w, h, pxls), clr, circle);
                return;
            }

            // First make the outer and inner circle objects
            Circle2D outer = new Circle2D(circle.Center, circle.Radius + frameThickness / 2.0f);
            Circle2D inner = new Circle2D(circle.Center, circle.Radius - frameThickness / 2.0f);

            int y = (int)Math.Max(0.0f, Math.Floor(outer.MinY));
            for (; y < h; y++)
            {
                // Compute the x-intersection points for this y-value
                float outMin, outMax, inMin, inMax;
                outer.HLineIntersection((float)y, out outMin, out outMax);
                inner.HLineIntersection((float)y, out inMin, out inMax);

                // If there's not a valid intersection, then skip this row. Note that there
                // could be an intersection with the outer circle and not the inner, but not
                // the other way around.
                if (float.IsNaN(outMin) || float.IsNaN(outMax))
                {
                    continue;
                }

                float[] ranges;
                if (float.IsNaN(inMin) || float.IsNaN(inMax))
                {
                    // This means there's an intersection with the outer circle and not the
                    // inner. In this case we only have one continuous range of pixels to
                    // process for this row.

                    // Check to see if we're off the image
                    if (outMax < 0.0f || outMin > (float)(w - 1))
                    {
                        continue;
                    }

                    ranges = new float[] { outMin, outMax };
                }
                else
                {
                    ranges = new float[] { outMin, inMin, inMax, outMax };
                }

                // Process the pixel ranges
                for (int i = 0; i < ranges.Length; i += 2)
                {
                    int x = (int)Math.Max(0.0f, Math.Floor(ranges[i]));
                    int maxx = (int)Math.Min((float)(w - 1), Math.Ceiling(ranges[i + 1]));

                    // Make a pointer to the pixel where we start rendering
                    uint* row = &pxls[y * w + x];

                    while (x <= maxx)
                    {
                        // Compute the distance from this pixel to the edge of the circle and
                        // soften if needed
                        float dist = circle.DistanceFromCenter(new Vector2D(x, y));
                        if (dist >= outer.Radius || dist <= inner.Radius)
                        {
                            x++;
                            row++;
                            continue;
                        }
                        if ((outer.Radius - dist) <= sr)
                        {
                            float newA = ((outer.Radius - dist) / sr) * 255.0f;
                            BlendComposite((clr & 0x00FFffFF) | (((uint)newA) << 24), row);
                        }
                        else if ((dist - inner.Radius) <= sr)
                        {
                            float newA = ((dist - inner.Radius) / sr) * 255.0f;
                            BlendComposite((clr & 0x00FFffFF) | (((uint)newA) << 24), row);
                        }
                        else
                        {
                            BlendComposite(clr, row);
                        }

                        x++;
                        row++;
                    }
                }
            }
        }