private unsafe void DrawRoundLine_None(Stadium stad, uint clr, uint* pxls, int w, int h)
        {
            // Compute the inclusive maximum bound for Y
            int maxy = (int)Math.Ceiling(stad.MaxY);
            if (maxy > h - 1)
            {
                maxy = h - 1;
            }
            else if (maxy < 0)
            {
                // We're not even on the image and don't need to draw anything
                return;
            }

            // Fill pixels within the stadium
            for (int y = (int)Math.Max(Math.Floor(stad.MinY), 0.0); y <= maxy; y++)
            {
                float minx, maxx;
                stad.HLineIntersection((float)y, out minx, out maxx);

                // If there's no intersection, then we can move to the next row
                if (float.IsNaN(minx) || float.IsNaN(maxx))
                {
                    continue;
                }

                // Compute the inclusive upper and lower bounds for X for this row
                int x = (int)Math.Floor(minx);
                if (x < 0)
                {
                    x = 0;
                }
                int rowMaxX = (int)Math.Ceiling(maxx);
                if (rowMaxX > w - 1)
                {
                    rowMaxX = w - 1;
                }

                // Get a pointer to the row of pixels
                uint* rowPtr = &pxls[y * w + x];

                while (x <= rowMaxX)
                {
                    float dist = stad.Axis.DistanceU(new Vector2D(x, y));
                    if (dist >= stad.Radius)
                    {
                        x++;
                        rowPtr++;
                        continue;
                    }

                    float distFromEdge = stad.Radius - dist;
                    if (distFromEdge < 2.0f)
                    {
                        // Soften alpha value
                        uint newa = (uint)((distFromEdge / 2.0f) * (float)((*rowPtr) >> 24));
                        *rowPtr = (clr & 0x00FFffFF) | (newa << 24);
                    }
                    else
                    {
                        *rowPtr = clr;
                    }

                    x++;
                    rowPtr++;
                }
            }
        }
        private unsafe void FillStadiums(SimpleImage img, uint clr, Stadium[] stads,
            float sr)
        {
            float clrAlphaFloat = (float)(clr >> 24) / 255f;

            // Find the start and end (inclusive) Y-values
            float minyFloat = float.MaxValue, maxyFloat = float.MinValue;
            foreach (Stadium sTemp in stads)
            {
                if (sTemp.MaxY > maxyFloat)
                {
                    maxyFloat = sTemp.MaxY;
                }
                if (sTemp.MinY < minyFloat)
                {
                    minyFloat = sTemp.MinY;
                }
            }
            int miny = (int)Math.Floor(minyFloat);
            if (miny >= img.Height)
            {
                // Content is completely off the image
                return;
            }
            miny = Math.Max(miny, 0);
            int maxy = Math.Min((int)Math.Ceiling(maxyFloat), img.Height - 1);

            // We need lists to keep track of minimum and maximum intersections per row
            List<float> mins = new List<float>();
            List<float> maxes = new List<float>();

            List<float> finals = new List<float>();

            // Loop through rows of pixels
            while (miny <= maxy)
            {
                // Clear intersection lists
                mins.Clear();
                maxes.Clear();
                finals.Clear();

                // Get all intersections for this row
                foreach (Stadium sTemp in stads)
                {
                    float tempMin, tempMax;
                    sTemp.HLineIntersection((float)miny, out tempMin, out tempMax);
                    if (!float.IsNaN(tempMin) && !float.IsNaN(tempMax))
                    {
                        mins.Add(tempMin);
                        maxes.Add(tempMax);
                    }
                }

                if (0 == mins.Count || 0 == maxes.Count)
                {
                    miny++;
                    continue;
                }

                // Sort the intersection lists
                mins.Sort();
                maxes.Sort();

                // Debug: plot a pixel at each intersection point
                //foreach (float f in mins)
                //{
                //    *img.GetRowAtX(miny, (int)f) = 0xFFFF0000;
                //}
                //foreach (float f in maxes)
                //{
                //    *img.GetRowAtX(miny, (int)f) = 0xFF00FF00;
                //}

                bool rowIsComplex = false;
                // When > 0, we're inside, otherwise we're outside the object. This gets incremented
                // when we hit a value in the "mins" list and decremented when we hit a value in
                // the "maxes" list.
                int in_out = 0;
                while (mins.Count > 0 || maxes.Count > 0)
                {
                    if (0 == mins.Count)
                    {
                        finals.Add(maxes[maxes.Count - 1]);
                        break;
                    }
                    else if (0 == maxes.Count)
                    {
                        // Should never occur
                        break;
                    }

                    if (mins[0] < maxes[0])
                    {
                        if (0 == in_out)
                        {
                            // 0 == in_out implies that we were outside and now we're entering
                            finals.Add(mins[0]);
                        }
                        else
                        {
                            // This means that in_out is > 0 and we've entered 2 stadiums (or
                            // more) at once. This implies a "complex" row, which gets rendered
                            // without certain optimizations.
                            rowIsComplex = true;
                        }
                        mins.RemoveAt(0);
                        in_out++;
                    }
                    else
                    {
                        in_out--;
                        if (0 == in_out)
                        {
                            // This is an exit point
                            finals.Add(maxes[0]);
                        }
                        maxes.RemoveAt(0);
                    }
                }

                int xstart, xend;

                // The "finals" list now contains pairs of enter-exit x-values
                for (int i = 0; i < finals.Count - 1; i += 2)
                {
                    xstart = Math.Max((int)Math.Floor(finals[i]), 0);
                    if (xstart >= img.Width)
                    {
                        continue;
                    }
                    xend = Math.Min((int)Math.Ceiling(finals[i + 1]), img.Width - 1);
                    if (xend < 0)
                    {
                        continue;
                    }
                    uint* row = &img.Pixels[miny * img.Width + xstart];

                    // Start by coming in from the left edge until we are no longer in the edge
                    // softening section
                    while (xstart <= xend)
                    {
                        float minDist = float.MaxValue;
                        Stadium minDistStadium = stads[0];
                        foreach (Stadium stadium in stads)
                        {
                            float dist = stadium.Axis.DistanceU(new Vector2D((float)xstart, (float)miny));
                            if (dist < minDist)
                            {
                                minDist = dist;
                                minDistStadium = stadium;
                            }
                        }

                        if (minDist < minDistStadium.Radius)
                        {
                            if (minDist > minDistStadium.Radius - sr)
                            {
                                float alpha = (minDistStadium.Radius - minDist) / sr;
                                alpha *= clrAlphaFloat;
                                uint newAlpha = (uint)(alpha * 255f);
                                newAlpha <<= 24;
                                BlendComposite((clr & 0x00FFffFF) | newAlpha, row);
                            }
                            else
                            {
                                if (rowIsComplex)
                                {
                                    BlendComposite(clr, row);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }

                        xstart++;
                        row++;
                    }

                    // It's possible that we completed this section in the above loop
                    if (xstart > xend)
                    {
                        continue;
                    }

                    // Get a pointer to pixels on the right edge
                    row = img.GetRowAtX(miny, xend);

                    // Now come in from the right edge until we are no longer in the edge
                    // softening section
                    while (xend >= xstart)
                    {
                        float minDist = float.MaxValue;
                        Stadium minDistStadium = stads[0];
                        foreach (Stadium stadium in stads)
                        {
                            float dist = stadium.Axis.DistanceU(new Vector2D((float)xend, (float)miny));
                            if (dist < minDist)
                            {
                                minDist = dist;
                                minDistStadium = stadium;
                            }
                        }

                        if (minDist < minDistStadium.Radius)
                        {
                            if (minDist > minDistStadium.Radius - sr)
                            {
                                float alpha = (minDistStadium.Radius - minDist) / sr;
                                alpha *= clrAlphaFloat;
                                uint newAlpha = (uint)(alpha * 255f);
                                newAlpha <<= 24;
                                BlendComposite((clr & 0x00FFffFF) | newAlpha, row);
                            }
                            else
                            {
                                break;
                            }
                        }

                        xend--;
                        row--;
                    }

                    // If there are remaining pixels between xstart and xend then render them
                    if (xstart <= xend)
                    {
                        DrawHorizontalLine(xstart, xend, miny, clr, img.Pixels, img.Width, img.Height,
                            BlendMode.AlphaComposite);
                    }
                }

                miny++;
            }
        }
 private unsafe void DrawRoundLine_Composite(Stadium stad, uint clr, uint* pxls, int w, int h)
 {
     FillStadiums(new SimpleImage(w, h, pxls), clr, new Stadium[] { stad }, 2f);
 }