namespace IMLibrary.Controls.CustomScrollBar
{
   using System;
   using System.Drawing;
   using System.Drawing.Drawing2D;
   using System.Drawing.Imaging;
   using IMLibrary.Controls;

   /// <summary>
   /// The scrollbar renderer class.
   /// </summary>
   internal static class ScrollBarExRenderer
   {
      #region fields

      /// <summary>
      /// The colors of the thumb in the 3 states.
      /// </summary>
      private static Color[,] thumbColors = new Color[3, 8];

      /// <summary>
      /// The arrow colors in the three states.
      /// </summary>
      private static Color[,] arrowColors = new Color[3, 8];

      /// <summary>
      /// The arrow border colors.
      /// </summary>
      private static Color[] arrowBorderColors = new Color[4];

      /// <summary>
      /// The background colors.
      /// </summary>
      private static Color[] backgroundColors = new Color[5];

      /// <summary>
      /// The track colors.
      /// </summary>
      private static Color[] trackColors = new Color[2];

      #endregion

      #region constructor

      /// <summary>
      /// Initializes static members of the <see cref="ScrollBarExRenderer"/> class.
      /// </summary>
      static ScrollBarExRenderer()
      {
         // hot state
         thumbColors[0, 0] = Color.FromArgb(96, 111, 148); // border color
         thumbColors[0, 1] = Color.FromArgb(232, 233, 233); // left/top start color
         thumbColors[0, 2] = Color.FromArgb(230, 233, 241); // left/top end color
         thumbColors[0, 3] = Color.FromArgb(233, 237, 242); // right/bottom line color
         thumbColors[0, 4] = Color.FromArgb(209, 218, 228); // right/bottom start color
         thumbColors[0, 5] = Color.FromArgb(218, 227, 235); // right/bottom end color
         thumbColors[0, 6] = Color.FromArgb(190, 202, 219); // right/bottom middle color
         thumbColors[0, 7] = Color.FromArgb(96, 11, 148); // left/top line color

         // over state
         thumbColors[1, 0] = Color.FromArgb(60, 110, 176);
         thumbColors[1, 1] = Color.FromArgb(187, 204, 228);
         thumbColors[1, 2] = Color.FromArgb(205, 227, 254);
         thumbColors[1, 3] = Color.FromArgb(252, 253, 255);
         thumbColors[1, 4] = Color.FromArgb(170, 207, 247);
         thumbColors[1, 5] = Color.FromArgb(219, 232, 251);
         thumbColors[1, 6] = Color.FromArgb(190, 202, 219);
         thumbColors[1, 7] = Color.FromArgb(233, 233, 235);

         // pressed state
         thumbColors[2, 0] = Color.FromArgb(23, 73, 138);
         thumbColors[2, 1] = Color.FromArgb(154, 184, 225);
         thumbColors[2, 2] = Color.FromArgb(166, 202, 250);
         thumbColors[2, 3] = Color.FromArgb(221, 235, 251);
         thumbColors[2, 4] = Color.FromArgb(110, 166, 240);
         thumbColors[2, 5] = Color.FromArgb(194, 218, 248);
         thumbColors[2, 6] = Color.FromArgb(190, 202, 219);
         thumbColors[2, 7] = Color.FromArgb(194, 211, 231);

         /* picture of colors and indices
          *(0,0)
          * -----------------------------------------------
          * |                                             |
          * | |-----------------------------------------| |
          * | |                  (2)                    | |
          * | | |-------------------------------------| | |
          * | | |                (0)                  | | |
          * | | |                                     | | |
          * | | |                                     | | |
          * | |3|                (1)                  |3| |
          * | |6|                (4)                  |6| |
          * | | |                                     | | |
          * | | |                (5)                  | | |
          * | | |-------------------------------------| | |
          * | |                  (12)                   | |
          * | |-----------------------------------------| |
          * |                                             |
          * ----------------------------------------------- (15,17)
          */

         // hot state
         arrowColors[0, 0] = Color.FromArgb(223, 236, 252);
         arrowColors[0, 1] = Color.FromArgb(207, 225, 248);
         arrowColors[0, 2] = Color.FromArgb(245, 249, 255);
         arrowColors[0, 3] = Color.FromArgb(237, 244, 252);
         arrowColors[0, 4] = Color.FromArgb(244, 249, 255);
         arrowColors[0, 5] = Color.FromArgb(244, 249, 255);
         arrowColors[0, 6] = Color.FromArgb(251, 253, 255);
         arrowColors[0, 7] = Color.FromArgb(251, 253, 255);

         // over state
         arrowColors[1, 0] = Color.FromArgb(205, 222, 243);
         arrowColors[1, 1] = Color.FromArgb(186, 208, 235);
         arrowColors[1, 2] = Color.FromArgb(238, 244, 252);
         arrowColors[1, 3] = Color.FromArgb(229, 237, 247);
         arrowColors[1, 4] = Color.FromArgb(223, 234, 247);
         arrowColors[1, 5] = Color.FromArgb(241, 246, 254);
         arrowColors[1, 6] = Color.FromArgb(243, 247, 252);
         arrowColors[1, 7] = Color.FromArgb(250, 252, 255);

         // pressed state
         arrowColors[2, 0] = Color.FromArgb(215, 220, 225);
         arrowColors[2, 1] = Color.FromArgb(195, 202, 210);
         arrowColors[2, 2] = Color.FromArgb(242, 244, 245);
         arrowColors[2, 3] = Color.FromArgb(232, 235, 238);
         arrowColors[2, 4] = Color.FromArgb(226, 228, 230);
         arrowColors[2, 5] = Color.FromArgb(230, 233, 236);
         arrowColors[2, 6] = Color.FromArgb(244, 245, 245);
         arrowColors[2, 7] = Color.FromArgb(245, 247, 248);

         // background colors
         backgroundColors[0] = Color.FromArgb(235, 237, 239);
         backgroundColors[1] = Color.FromArgb(252, 252, 252);
         backgroundColors[2] = Color.FromArgb(247, 247, 247);
         backgroundColors[3] = Color.FromArgb(238, 238, 238);
         backgroundColors[4] = Color.FromArgb(240, 240, 240);

         // track colors
         trackColors[0] = Color.FromArgb(204, 204, 204);
         trackColors[1] = Color.FromArgb(220, 220, 220);

         // arrow border colors
         arrowBorderColors[0] = Color.FromArgb(135, 146, 160);
         arrowBorderColors[1] = Color.FromArgb(140, 151, 165);
         arrowBorderColors[2] = Color.FromArgb(128, 139, 153);
         arrowBorderColors[3] = Color.FromArgb(99, 110, 125);
      }

      #endregion

      #region methods

      #region public methods

      /// <summary>
      /// Draws the background.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="orientation">The <see cref="ScrollBarOrientation"/>.</param>
      public static void DrawBackground(
         Graphics g,
         Rectangle rect,
         ScrollBarOrientation orientation)
      {
         if (g == null)
         {
            throw new ArgumentNullException("g");
         }

         if (rect.IsEmpty || g.IsVisibleClipEmpty
            || !g.VisibleClipBounds.IntersectsWith(rect))
         {
            return;
         }

         if (orientation == ScrollBarOrientation.Vertical)
         {
            DrawBackgroundVertical(g, rect);
         }
         else
         {
            DrawBackgroundHorizontal(g, rect);
         }
      }

      /// <summary>
      /// Draws the channel ( or track ).
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The scrollbar state.</param>
      /// <param name="orientation">The <see cref="ScrollBarOrientation"/>.</param>
      public static void DrawTrack(
         Graphics g,
         Rectangle rect,
         ScrollBarState state,
         ScrollBarOrientation orientation)
      {
         if (g == null)
         {
            throw new ArgumentNullException("g");
         }

         if (rect.Width <= 0 || rect.Height <= 0
            || state != ScrollBarState.Pressed || g.IsVisibleClipEmpty
            || !g.VisibleClipBounds.IntersectsWith(rect))
         {
            return;
         }

         if (orientation == ScrollBarOrientation.Vertical)
         {
            DrawTrackVertical(g, rect);
         }
         else
         {
            DrawTrackHorizontal(g, rect);
         }
      }

      /// <summary>
      /// Draws the thumb.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarState"/> of the thumb.</param>
      /// <param name="orientation">The <see cref="ScrollBarOrientation"/>.</param>
      public static void DrawThumb(
         Graphics g,
         Rectangle rect,
         ScrollBarState state,
         ScrollBarOrientation orientation)
      {
         if (g == null)
         {
            throw new ArgumentNullException("g");
         }

         if (rect.IsEmpty || g.IsVisibleClipEmpty
            || !g.VisibleClipBounds.IntersectsWith(rect)
            || state == ScrollBarState.Disabled)
         {
            return;
         }

         if (orientation == ScrollBarOrientation.Vertical)
         {
            DrawThumbVertical(g, rect, state);
         }
         else
         {
            DrawThumbHorizontal(g, rect, state);
         }
      }

      /// <summary>
      /// Draws the grip of the thumb.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="orientation">The <see cref="ScrollBarOrientation"/>.</param>
      public static void DrawThumbGrip(
         Graphics g,
         Rectangle rect,
         ScrollBarOrientation orientation)
      {
         if (g == null)
         {
            throw new ArgumentNullException("g");
         }

         if (rect.IsEmpty || g.IsVisibleClipEmpty
            || !g.VisibleClipBounds.IntersectsWith(rect))
         {
            return;
         }

         // get grip image
         using (Image gripImage = Resources.GripNormal)
         {
            // adjust rectangle and rotate grip image if necessary
            Rectangle r = AdjustThumbGrip(rect, orientation, gripImage);

            // adjust alpha channel of grip image
            using (ImageAttributes attr = new ImageAttributes())
            {
               attr.SetColorMatrix(
                  new ColorMatrix(new float[][] {
                  new[] { 1F, 0, 0, 0, 0 },
                  new[] { 0, 1F, 0, 0, 0 },
                  new[] { 0, 0, 1F, 0, 0 },
                  new[] { 0, 0, 0,  .8F, 0 },
                  new[] { 0, 0, 0, 0, 1F }
                  }),
                  ColorMatrixFlag.Default,
                  ColorAdjustType.Bitmap
               );

               // draw grip image
               g.DrawImage(gripImage, r, 0, 0, r.Width, r.Height, GraphicsUnit.Pixel, attr);
            }
         }
      }

      /// <summary>
      /// Draws an arrow button.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarArrowButtonState"/> of the arrow button.</param>
      /// <param name="arrowUp">true for an up arrow, false otherwise.</param>
      /// <param name="orientation">The <see cref="ScrollBarOrientation"/>.</param>
      public static void DrawArrowButton(
         Graphics g,
         Rectangle rect,
         ScrollBarArrowButtonState state,
         bool arrowUp,
         ScrollBarOrientation orientation)
      {
         if (g == null)
         {
            throw new ArgumentNullException("g");
         }

         if (rect.IsEmpty || g.IsVisibleClipEmpty
            || !g.VisibleClipBounds.IntersectsWith(rect))
         {
            return;
         }

         if (orientation == ScrollBarOrientation.Vertical)
         {
            DrawArrowButtonVertical(g, rect, state, arrowUp);
         }
         else
         {
            DrawArrowButtonHorizontal(g, rect, state, arrowUp);
         }
      }

      #endregion

      #region private methods

      /// <summary>
      /// Draws the background.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      private static void DrawBackgroundVertical(Graphics g, Rectangle rect)
      {
         using (Pen p = new Pen(backgroundColors[0]))
         {
            g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Left + 1, rect.Bottom - 1);
            g.DrawLine(p, rect.Right - 2, rect.Top + 1, rect.Right - 2, rect.Bottom - 1);
         }

         using (Pen p = new Pen(backgroundColors[1]))
         {
            g.DrawLine(p, rect.Left + 2, rect.Top + 1, rect.Left + 2, rect.Bottom - 1);
         }

         Rectangle firstRect = new Rectangle(
            rect.Left + 3,
            rect.Top,
            8,
            rect.Height);

         Rectangle secondRect = new Rectangle(
            firstRect.Right - 1,
            firstRect.Top,
            7,
            firstRect.Height);

         using (LinearGradientBrush brush = new LinearGradientBrush(
            firstRect,
            backgroundColors[2],
            backgroundColors[3],
            LinearGradientMode.Horizontal))
         {
            g.FillRectangle(brush, firstRect);
         }

         using (LinearGradientBrush brush = new LinearGradientBrush(
            secondRect,
            backgroundColors[3],
            backgroundColors[4],
            LinearGradientMode.Horizontal))
         {
            g.FillRectangle(brush, secondRect);
         }
      }

      /// <summary>
      /// Draws the background.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      private static void DrawBackgroundHorizontal(Graphics g, Rectangle rect)
      {
         using (Pen p = new Pen(backgroundColors[0]))
         {
            g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Right - 1, rect.Top + 1);
            g.DrawLine(p, rect.Left + 1, rect.Bottom - 2, rect.Right - 1, rect.Bottom - 2);
         }

         using (Pen p = new Pen(backgroundColors[1]))
         {
            g.DrawLine(p, rect.Left + 1, rect.Top + 2, rect.Right - 1, rect.Top + 2);
         }

         Rectangle firstRect = new Rectangle(
            rect.Left,
            rect.Top + 3,
            rect.Width,
            8);

         Rectangle secondRect = new Rectangle(
            firstRect.Left,
            firstRect.Bottom - 1,
            firstRect.Width,
            7);

         using (LinearGradientBrush brush = new LinearGradientBrush(
            firstRect,
            backgroundColors[2],
            backgroundColors[3],
            LinearGradientMode.Vertical))
         {
            g.FillRectangle(brush, firstRect);
         }

         using (LinearGradientBrush brush = new LinearGradientBrush(
            secondRect,
            backgroundColors[3],
            backgroundColors[4],
            LinearGradientMode.Vertical))
         {
            g.FillRectangle(brush, secondRect);
         }
      }

      /// <summary>
      /// Draws the channel ( or track ).
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      private static void DrawTrackVertical(Graphics g, Rectangle rect)
      {
         Rectangle innerRect = new Rectangle(rect.Left + 1, rect.Top, 15, rect.Height);

         using (LinearGradientBrush brush = new LinearGradientBrush(
            innerRect,
            trackColors[0],
            trackColors[1],
            LinearGradientMode.Horizontal))
         {
            g.FillRectangle(brush, innerRect);
         }
      }

      /// <summary>
      /// Draws the channel ( or track ).
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      private static void DrawTrackHorizontal(Graphics g, Rectangle rect)
      {
         Rectangle innerRect = new Rectangle(rect.Left, rect.Top + 1, rect.Width, 15);

         using (LinearGradientBrush brush = new LinearGradientBrush(
            innerRect,
            trackColors[0],
            trackColors[1],
            LinearGradientMode.Vertical))
         {
            g.FillRectangle(brush, innerRect);
         }
      }

      /// <summary>
      /// Adjusts the thumb grip according to the specified <see cref="ScrollBarOrientation"/>.
      /// </summary>
      /// <param name="rect">The rectangle to adjust.</param>
      /// <param name="orientation">The scrollbar orientation.</param>
      /// <param name="gripImage">The grip image.</param>
      /// <returns>The adjusted rectangle.</returns>
      /// <remarks>Also rotates the grip image if necessary.</remarks>
      private static Rectangle AdjustThumbGrip(
         Rectangle rect,
         ScrollBarOrientation orientation,
         Image gripImage)
      {
         Rectangle r = rect;

         r.Inflate(-1, -1);

         if (orientation == ScrollBarOrientation.Vertical)
         {
            r.X += 3;
            r.Y += (r.Height / 2) - (gripImage.Height / 2);
         }
         else
         {
            gripImage.RotateFlip(RotateFlipType.Rotate90FlipNone);

            r.X += (r.Width / 2) - (gripImage.Width / 2);
            r.Y += 3;
         }

         r.Width = gripImage.Width;
         r.Height = gripImage.Height;

         return r;
      }

      /// <summary>
      /// Draws the thumb.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarState"/> of the thumb.</param>
      private static void DrawThumbVertical(
         Graphics g,
         Rectangle rect,
         ScrollBarState state)
      {
         int index = 0;

         switch (state)
         {
            case ScrollBarState.Hot:
               {
                  index = 1;
                  break;
               }

            case ScrollBarState.Pressed:
               {
                  index = 2;
                  break;
               }
         }

         Rectangle innerRect = rect;
         innerRect.Inflate(-1, -1);

         Rectangle r = innerRect;
         r.Width = 6;
         r.Height++;

         // draw left gradient
         using (LinearGradientBrush brush = new LinearGradientBrush(
            r,
            thumbColors[index, 1],
            thumbColors[index, 2],
            LinearGradientMode.Horizontal))
         {
            g.FillRectangle(brush, r);
         }

         r.X = r.Right;

         // draw right gradient
         if (index == 0)
         {
            using (LinearGradientBrush brush = new LinearGradientBrush(
               r,
               thumbColors[index, 4],
               thumbColors[index, 5],
               LinearGradientMode.Horizontal))
            {
               brush.InterpolationColors = new ColorBlend(3)
               {
                  Colors = new[]
                            {
                               thumbColors[index, 4],
                               thumbColors[index, 6],
                               thumbColors[index, 5]
                            },
                  Positions = new[] { 0f, .5f, 1f }
               };

               g.FillRectangle(brush, r);
            }
         }
         else
         {
            using (LinearGradientBrush brush = new LinearGradientBrush(
               r,
               thumbColors[index, 4],
               thumbColors[index, 5],
               LinearGradientMode.Horizontal))
            {
               g.FillRectangle(brush, r);
            }

            // draw left line
            using (Pen p = new Pen(thumbColors[index, 7]))
            {
               g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.X, innerRect.Bottom);
            }
         }

         // draw right line
         using (Pen p = new Pen(thumbColors[index, 3]))
         {
            g.DrawLine(p, innerRect.Right, innerRect.Y, innerRect.Right, innerRect.Bottom);
         }

         g.SmoothingMode = SmoothingMode.AntiAlias;

         // draw border
         using (Pen p = new Pen(thumbColors[index, 0]))
         {
            using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f))
            {
               g.DrawPath(p, path);
            }
         }

         g.SmoothingMode = SmoothingMode.None;
      }

      /// <summary>
      /// Draws the thumb.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarState"/> of the thumb.</param>
      private static void DrawThumbHorizontal(
         Graphics g,
         Rectangle rect,
         ScrollBarState state)
      {
         int index = 0;

         switch (state)
         {
            case ScrollBarState.Hot:
               {
                  index = 1;
                  break;
               }

            case ScrollBarState.Pressed:
               {
                  index = 2;
                  break;
               }
         }

         Rectangle innerRect = rect;
         innerRect.Inflate(-1, -1);

         Rectangle r = innerRect;
         r.Height = 6;
         r.Width++;

         // draw left gradient
         using (LinearGradientBrush brush = new LinearGradientBrush(
            r, thumbColors[index, 1],
            thumbColors[index, 2],
            LinearGradientMode.Vertical))
         {
            g.FillRectangle(brush, r);
         }

         r.Y = r.Bottom;

         // draw right gradient
         if (index == 0)
         {
            using (LinearGradientBrush brush = new LinearGradientBrush(
               r,
               thumbColors[index, 4],
               thumbColors[index, 5],
               LinearGradientMode.Vertical))
            {
               brush.InterpolationColors = new ColorBlend(3)
               {
                  Colors = new[]
                            {
                               thumbColors[index, 4],
                               thumbColors[index, 6],
                               thumbColors[index, 5]
                            },
                  Positions = new[] { 0f, .5f, 1f }
               };

               g.FillRectangle(brush, r);
            }
         }
         else
         {
            using (LinearGradientBrush brush = new LinearGradientBrush(
               r, thumbColors[index, 4],
               thumbColors[index, 5],
               LinearGradientMode.Vertical))
            {
               g.FillRectangle(brush, r);
            }

            // draw left line
            using (Pen p = new Pen(thumbColors[index, 7]))
            {
               g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.Right, innerRect.Y);
            }
         }

         // draw right line
         using (Pen p = new Pen(thumbColors[index, 3]))
         {
            g.DrawLine(p, innerRect.X, innerRect.Bottom, innerRect.Right, innerRect.Bottom);
         }

         g.SmoothingMode = SmoothingMode.AntiAlias;

         // draw border
         using (Pen p = new Pen(thumbColors[index, 0]))
         {
            using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f))
            {
               g.DrawPath(p, path);
            }
         }

         g.SmoothingMode = SmoothingMode.None;
      }

      /// <summary>
      /// Draws an arrow button.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarArrowButtonState"/> of the arrow button.</param>
      /// <param name="arrowUp">true for an up arrow, false otherwise.</param>
      private static void DrawArrowButtonVertical(
         Graphics g,
         Rectangle rect,
         ScrollBarArrowButtonState state,
         bool arrowUp)
      {
         using (Image arrowImage = GetArrowDownButtonImage(state))
         {
            if (arrowUp)
            {
               arrowImage.RotateFlip(RotateFlipType.Rotate180FlipNone);
            }

            g.DrawImage(arrowImage, rect);
         }
      }

      /// <summary>
      /// Draws an arrow button.
      /// </summary>
      /// <param name="g">The <see cref="Graphics"/> used to paint.</param>
      /// <param name="rect">The rectangle in which to paint.</param>
      /// <param name="state">The <see cref="ScrollBarArrowButtonState"/> of the arrow button.</param>
      /// <param name="arrowUp">true for an up arrow, false otherwise.</param>
      private static void DrawArrowButtonHorizontal(
         Graphics g,
         Rectangle rect,
         ScrollBarArrowButtonState state,
         bool arrowUp)
      {
         using (Image arrowImage = GetArrowDownButtonImage(state))
         {
            if (arrowUp)
            {
               arrowImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
            }
            else
            {
               arrowImage.RotateFlip(RotateFlipType.Rotate270FlipNone);
            }

            g.DrawImage(arrowImage, rect);
         }
      }

      /// <summary>
      /// Draws the arrow down button for the scrollbar.
      /// </summary>
      /// <param name="state">The button state.</param>
      /// <returns>The arrow down button as <see cref="Image"/>.</returns>
      private static Image GetArrowDownButtonImage(
         ScrollBarArrowButtonState state)
      {
         Rectangle rect = new Rectangle(0, 0, 15, 17);
         Bitmap bitmap = new Bitmap(15, 17, PixelFormat.Format32bppArgb);
         bitmap.SetResolution(72f, 72f);

         using (Graphics g = Graphics.FromImage(bitmap))
         {
            g.SmoothingMode = SmoothingMode.None;
            g.InterpolationMode = InterpolationMode.Low;

            int index = -1;

            switch (state)
            {
               case ScrollBarArrowButtonState.UpHot:
               case ScrollBarArrowButtonState.DownHot:
                  {
                     index = 1;

                     break;
                  }

               case ScrollBarArrowButtonState.UpActive:
               case ScrollBarArrowButtonState.DownActive:
                  {
                     index = 0;

                     break;
                  }

               case ScrollBarArrowButtonState.UpPressed:
               case ScrollBarArrowButtonState.DownPressed:
                  {
                     index = 2;

                     break;
                  }
            }

            if (index != -1)
            {
               using (Pen p1 = new Pen(arrowBorderColors[0]),
                  p2 = new Pen(arrowBorderColors[1]))
               {
                  g.DrawLine(p1, rect.X, rect.Y, rect.Right - 1, rect.Y);
                  g.DrawLine(p2, rect.X, rect.Bottom - 1, rect.Right - 1, rect.Bottom - 1);
               }

               rect.Inflate(0, -1);

               using (LinearGradientBrush brush = new LinearGradientBrush(
                  rect,
                  arrowBorderColors[2],
                  arrowBorderColors[1],
                  LinearGradientMode.Vertical))
               {
                  ColorBlend blend = new ColorBlend(3)
                  {
                     Positions = new[] { 0f, .5f, 1f },
                     Colors = new[] {
                     arrowBorderColors[2],
                     arrowBorderColors[3],
                     arrowBorderColors[0] }
                  };

                  brush.InterpolationColors = blend;

                  using (Pen p = new Pen(brush))
                  {
                     g.DrawLine(p, rect.X, rect.Y, rect.X, rect.Bottom - 1);
                     g.DrawLine(p, rect.Right - 1, rect.Y, rect.Right - 1, rect.Bottom - 1);
                  }
               }

               rect.Inflate(0, 1);

               Rectangle upper = rect;
               upper.Inflate(-1, 0);
               upper.Y++;
               upper.Height = 7;

               using (LinearGradientBrush brush = new LinearGradientBrush(
                  upper,
                  arrowColors[index, 2],
                  arrowColors[index, 3],
                  LinearGradientMode.Vertical))
               {
                  g.FillRectangle(brush, upper);
               }

               upper.Inflate(-1, 0);
               upper.Height = 6;

               using (LinearGradientBrush brush = new LinearGradientBrush(
                  upper,
                  arrowColors[index, 0],
                  arrowColors[index, 1],
                  LinearGradientMode.Vertical))
               {
                  g.FillRectangle(brush, upper);
               }

               Rectangle lower = rect;
               lower.Inflate(-1, 0);
               lower.Y = 8;
               lower.Height = 8;

               using (LinearGradientBrush brush = new LinearGradientBrush(
                  lower,
                  arrowColors[index, 6],
                  arrowColors[index, 7],
                  LinearGradientMode.Vertical))
               {
                  g.FillRectangle(brush, lower);
               }

               lower.Inflate(-1, 0);

               using (LinearGradientBrush brush = new LinearGradientBrush(
                  lower,
                  arrowColors[index, 4],
                  arrowColors[index, 5],
                  LinearGradientMode.Vertical))
               {
                  g.FillRectangle(brush, lower);
               }
            }

            using (Image arrowIcon = Resources.ScrollBarArrowDown)
            {
               if (state == ScrollBarArrowButtonState.DownDisabled
                  || state == ScrollBarArrowButtonState.UpDisabled)
               {
                  System.Windows.Forms.ControlPaint.DrawImageDisabled(
                     g,
                     arrowIcon,
                     3,
                     6,
                     Color.Transparent);
               }
               else
               {
                  g.DrawImage(arrowIcon, 3, 6);
               }
            }
         }

         return bitmap;
      }

      /// <summary>
      /// Creates a rounded rectangle.
      /// </summary>
      /// <param name="r">The rectangle to create the rounded rectangle from.</param>
      /// <param name="radiusX">The x-radius.</param>
      /// <param name="radiusY">The y-radius.</param>
      /// <returns>A <see cref="GraphicsPath"/> object representing the rounded rectangle.</returns>
      private static GraphicsPath CreateRoundPath(
         Rectangle r,
         float radiusX,
         float radiusY)
      {
         // create new graphics path object
         GraphicsPath path = new GraphicsPath();

         // calculate radius of edges
         PointF d = new PointF(Math.Min(radiusX * 2, r.Width), Math.Min(radiusY * 2, r.Height));

         // make sure radius is valid
         d.X = Math.Max(1, d.X);
         d.Y = Math.Max(1, d.Y);

         // add top left arc
         path.AddArc(r.X, r.Y, d.X, d.Y, 180, 90);

         // add top right arc
         path.AddArc(r.Right - d.X, r.Y, d.X, d.Y, 270, 90);

         // add bottom right arc
         path.AddArc(r.Right - d.X, r.Bottom - d.Y, d.X, d.Y, 0, 90);

         // add bottom left arc
         path.AddArc(r.X, r.Bottom - d.Y, d.X, d.Y, 90, 90);

         // close path
         path.CloseFigure();

         return path;
      }

      #endregion

      #endregion
   }
}
