Managed Wrapper for a DirectX7 DirectDraw Surface.
상속: IGraphicsSurface
        /// <summary>
        ///
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="fullScreen"></param>
        /// <param name="doubleBuffer"></param>
        /// <returns></returns>
        public IGraphicsSurface CreatePrimarySurface(IntPtr handle, Boolean fullScreen, Boolean doubleBuffer)
        {
            DDSURFACEDESC2 tempDescr = new DDSURFACEDESC2();

            tempDescr.lFlags        = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS;
            tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE;

            if (doubleBuffer)
            {
                tempDescr.lFlags          |= CONST_DDSURFACEDESCFLAGS.DDSD_BACKBUFFERCOUNT;
                tempDescr.lBackBufferCount = 1;
                tempDescr.ddsCaps.lCaps   |= CONST_DDSURFACECAPSFLAGS.DDSCAPS_COMPLEX |
                                             CONST_DDSURFACECAPSFLAGS.DDSCAPS_FLIP;
            }

            DirectDrawSurface surface = new DirectDrawSurface(tempDescr);

            if (fullScreen)
            {
                return(surface);
            }

            DirectDrawClipper clipper = DirectDraw.CreateClipper(0);

            clipper.SetHWnd(handle.ToInt32());
            surface.Surface.SetClipper(clipper);

            return(surface);
        }
        /// <summary>
        ///  Creates a new sprite sheet with the given name from an image.
        ///  The sheet is broken into sprites given the number of horizontal
        ///  and vertical frames available on this sheet.
        /// </summary>
        /// <param name="spriteName">The name of the sprite sheet</param>
        /// <param name="spriteImagePath">The path to the image to load.</param>
        /// <param name="xFrames">The number of horizontal frames on this sheet.</param>
        /// <param name="yFrames">The number of vertical frames on this sheet.</param>
        public DirectDrawSpriteSurface(string spriteName, string spriteImagePath, int xFrames, int yFrames)
        {
            #if TRACE
            ManagedDirectX.Profiler.Start("DirectDrawSpriteSurface..ctor()");
            #endif
            this.spriteName = spriteName;

            this.ddsurface = new DirectDrawSurface(spriteImagePath);
            if (ddsurface == null)
            {
                this.ddsurface = new DirectDrawSurface(spriteImagePath, DirectDrawSurface.SystemMemorySurfaceDescription);
            }
            this.ddsurface.TransparencyKey = DirectDrawSurface.DefaultColorKey;

            this.animationFrames = xFrames;
            this.animationTypes = yFrames;

            this.frameHeight = this.ddsurface.Rect.Bottom / this.animationTypes;
            this.frameWidth = this.ddsurface.Rect.Right / this.animationFrames;
            #if TRACE
            ManagedDirectX.Profiler.End("DirectDrawSpriteSurface..ctor()");
            #endif
        }
        /// <summary>
        ///  Creates a new sprite sheet with the given name from an image.
        ///  The sheet is broken into sprites given the number of horizontal
        ///  and vertical frames available on this sheet.
        /// </summary>
        /// <param name="spriteName">The name of the sprite sheet</param>
        /// <param name="spriteImagePath">The path to the image to load.</param>
        /// <param name="xFrames">The number of horizontal frames on this sheet.</param>
        /// <param name="yFrames">The number of vertical frames on this sheet.</param>
        public DirectDrawSpriteSurface(string spriteName, string spriteImagePath, int xFrames, int yFrames)
        {
            #if TRACE
            GraphicsEngine.Profiler.Start("DirectDrawSpriteSurface..ctor()");
            #endif
            this.spriteName = spriteName;

            ddsurface = new DirectDrawSurface(spriteImagePath);
            if (ddsurface == null)
            {
                ddsurface = new DirectDrawSurface(spriteImagePath, DirectDrawSurface.SystemMemorySurfaceDescription);
            }
            ddsurface.TransparencyKey = new ColorKey(DirectDrawSurface.DefaultColorKey);

            animationFrames = xFrames;
            animationTypes = yFrames;

            frameHeight = ddsurface.Rect.Bottom/animationTypes;
            frameWidth = ddsurface.Rect.Right/animationFrames;
            #if TRACE
            GraphicsEngine.Profiler.End("DirectDrawSpriteSurface..ctor()");
            #endif
        }
        /// <summary>
        ///  Creates a new sprite sheet with the given name from an image.
        ///  The sheet is broken into sprites given the number of horizontal
        ///  and vertical frames available on this sheet.
        /// </summary>
        /// <param name="spriteName">The name of the sprite sheet</param>
        /// <param name="spriteImagePath">The path to the image to load.</param>
        /// <param name="xFrames">The number of horizontal frames on this sheet.</param>
        /// <param name="yFrames">The number of vertical frames on this sheet.</param>
        public DirectDrawSpriteSurface(string spriteName, string spriteImagePath, int xFrames, int yFrames)
        {
#if TRACE
            ManagedDirectX.Profiler.Start("DirectDrawSpriteSurface..ctor()");
#endif
            this.spriteName = spriteName;

            ddsurface = new DirectDrawSurface(spriteImagePath);
            if (ddsurface == null)
            {
                ddsurface = new DirectDrawSurface(spriteImagePath, DirectDrawSurface.SystemMemorySurfaceDescription);
            }
            ddsurface.TransparencyKey = DirectDrawSurface.DefaultColorKey;

            animationFrames = xFrames;
            animationTypes  = yFrames;

            frameHeight = ddsurface.Rect.Bottom / animationTypes;
            frameWidth  = ddsurface.Rect.Right / animationFrames;
#if TRACE
            ManagedDirectX.Profiler.End("DirectDrawSpriteSurface..ctor()");
#endif
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="fullScreen"></param>
        /// <param name="doubleBuffer"></param>
        /// <returns></returns>
        public IGraphicsSurface CreatePrimarySurface(IntPtr handle, Boolean fullScreen, Boolean doubleBuffer)
        {
            DDSURFACEDESC2 tempDescr    = new DDSURFACEDESC2();
            tempDescr.lFlags            = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS;
            tempDescr.ddsCaps.lCaps     = CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE;

            if (doubleBuffer)
            {
                tempDescr.lFlags |= CONST_DDSURFACEDESCFLAGS.DDSD_BACKBUFFERCOUNT;
                tempDescr.lBackBufferCount = 1;
                tempDescr.ddsCaps.lCaps |= CONST_DDSURFACECAPSFLAGS.DDSCAPS_COMPLEX |
                                           CONST_DDSURFACECAPSFLAGS.DDSCAPS_FLIP;
            }

            DirectDrawSurface surface = new DirectDrawSurface(tempDescr);

            if (fullScreen)
            {
                return surface;
            }

            DirectDrawClipper clipper = DirectDraw.CreateClipper(0);
            clipper.SetHWnd(handle.ToInt32());
            surface.Surface.SetClipper(clipper);

            return surface;
        }
        /// <summary>
        ///  Reinitialize all surfaces after they have been lost.
        ///  This method invokes the garbage collector to make sure
        ///  that any COM references have been cleaned up, else surface
        ///  renewal won't work correctly.
        /// </summary>
        private void ReInitSurfaces()
        {
            ScreenSurface = null;
            BackBufferSurface = null;
            backgroundSurface = null;
            stagingSurface = null;

            tsm.Clear();
            tfm.Clear();

            GC.Collect(2);
            GC.WaitForPendingFinalizers();

            if (fullscreen)
            {
                CreateFullScreenSurfaces();
            }
            else
            {
                CreateWindowedSurfaces();
            }
        }
        /// <summary>
        ///  Creates the surfaces required for full screen operation.
        /// </summary>
        /// <returns>True if the surfaces are initialized, false otherwise.</returns>
        public bool CreateFullScreenSurfaces()
        {
            if (doubleBuffer)
            {
                var tempDescr = new DDSURFACEDESC2();
                tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS;
                tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE;
                ScreenSurface = new DirectDrawSurface(tempDescr);

                if (ScreenSurface != null)
                {
                    tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS | CONST_DDSURFACEDESCFLAGS.DDSD_HEIGHT |
                                       CONST_DDSURFACEDESCFLAGS.DDSD_WIDTH;
                    tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_OFFSCREENPLAIN;
                    tempDescr.lWidth = 640;
                    tempDescr.lHeight = 480;
                    BackBufferSurface = new DirectDrawSurface(tempDescr);
                }

                ClearBackground();
            }
            else
            {
                var tempDescr = new DDSURFACEDESC2();
                tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS | CONST_DDSURFACEDESCFLAGS.DDSD_BACKBUFFERCOUNT;
                tempDescr.lBackBufferCount = 1;
                tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE |
                                          CONST_DDSURFACECAPSFLAGS.DDSCAPS_COMPLEX |
                                          CONST_DDSURFACECAPSFLAGS.DDSCAPS_FLIP;
                ScreenSurface = new DirectDrawSurface(tempDescr);

                if (ScreenSurface != null)
                {
                    tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_BACKBUFFER;
                    BackBufferSurface =
                        new DirectDrawSurface(ScreenSurface.Surface.GetAttachedSurface(ref tempDescr.ddsCaps));
                }
            }

            ResetTerrarium();

            return true;
        }
        /// <summary>
        ///  Method used to create the necessary surfaces required for windowed
        ///  mode.
        /// </summary>
        /// <returns>True if the surfaces are created, otherwise false.</returns>
        public bool CreateWindowedSurfaces()
        {
            var tempDescr = new DDSURFACEDESC2();
            tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS;
            tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_PRIMARYSURFACE;
            ScreenSurface = new DirectDrawSurface(tempDescr);

            Clipper = ManagedDirectX.DirectDraw.CreateClipper(0);
            Clipper.SetHWnd(Handle.ToInt32());
            ScreenSurface.Surface.SetClipper(Clipper);
            Trace.WriteLine("Primary Surface InVideo? " + ScreenSurface.InVideo);

            if (ScreenSurface != null)
            {
                var workSurfaceWidth = Math.Min(Width, actualsize.Right);
                var workSurfaceHeight = Math.Min(Height, actualsize.Bottom);

                tempDescr = new DDSURFACEDESC2();
                tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_HEIGHT |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_WIDTH;
                tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_OFFSCREENPLAIN;
                tempDescr.lWidth = workSurfaceWidth;
                tempDescr.lHeight = workSurfaceHeight;
                BackBufferSurface = new DirectDrawSurface(tempDescr);
                Trace.WriteLine("Back Buffer Surface InVideo? " + BackBufferSurface.InVideo);

                tempDescr = new DDSURFACEDESC2();
                tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_HEIGHT |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_WIDTH;
                tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_OFFSCREENPLAIN;
                tempDescr.lWidth = workSurfaceWidth;
                tempDescr.lHeight = workSurfaceHeight;
                backgroundSurface = new DirectDrawSurface(tempDescr);
                Trace.WriteLine("Background Surface InVideo? " + backgroundSurface.InVideo);

                tempDescr = new DDSURFACEDESC2();
                tempDescr.lFlags = CONST_DDSURFACEDESCFLAGS.DDSD_CAPS |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_HEIGHT |
                                   CONST_DDSURFACEDESCFLAGS.DDSD_WIDTH;
                tempDescr.ddsCaps.lCaps = CONST_DDSURFACECAPSFLAGS.DDSCAPS_OFFSCREENPLAIN;
                tempDescr.lWidth = workSurfaceWidth;
                tempDescr.lHeight = workSurfaceHeight;
                stagingSurface = new DirectDrawSurface(tempDescr);
                Trace.WriteLine("Staging Surface InVideo? " + stagingSurface.InVideo);
            }

            if (!ScreenSurface.InVideo || !BackBufferSurface.InVideo || !backgroundSurface.InVideo ||
                !stagingSurface.InVideo)
            {
                videomemory = false;
                drawtext = true; // For now, turn this to false for a perf increase on slower machines
            }

            ResetTerrarium();
            ClearBackground();

            return true;
        }
        /// <summary>
        ///  Paint sprites on the given surface.  This method is the meat
        ///  of the graphics engine.  Normally, creatures are painted to
        ///  the work surface using this method.  To increase performance plants
        ///  are rendered to the background surface only once every 10 frames.
        ///  Lots of work happening in this function so either read through the
        ///  code or examine the Terrarium Graphics Engine whitepaper for more
        ///  information.
        /// </summary>
        /// <param name="dds">The surface to render to.</param>
        /// <param name="PlantsOnly">True to render plants, false to render animals.</param>
        private void PaintSprites(DirectDrawSurface dds, bool PlantsOnly)
        {
#if TRACE
            Profiler.Start("TerrariumDirectDrawGameView.PaintSprites()");
#endif
            if (wv == null)
            {
                return;
            }

            if (tfm.Count > 100)
            {
                tfm.Clear();
            }

            var TeleporterZIndices = TeleporterZIndex();
            var lastTeleporterZIndex = 0;

            foreach (OrganismState orgState in wv.State.ZOrderedOrganisms)
            {
                if (orgState.RenderInfo != null)
                {
                    var tsSprite = (TerrariumSprite) orgState.RenderInfo;

                    if ((PlantsOnly && !(orgState.Species is PlantSpecies)) ||
                        (!PlantsOnly && orgState.Species is PlantSpecies))
                    {
                        continue;
                    }

                    if (orgState.Species is AnimalSpecies)
                    {
                        tsSprite.AdvanceFrame();
                        orgState.RenderInfo = tsSprite;
                    }

                    if (!videomemory && skipframe)
                    {
                        continue;
                    }

                    TerrariumSpriteSurface workTss = null;

                    if (workTss == null && tsSprite.SpriteKey != null)
                    {
                        workTss = tsm[tsSprite.SpriteKey, orgState.Radius, (orgState.Species is AnimalSpecies)];
                    }

                    if (workTss == null && tsSprite.SkinFamily != null)
                    {
                        workTss = tsm[tsSprite.SkinFamily, orgState.Radius, (orgState.Species is AnimalSpecies)];
                    }

                    if (workTss == null)
                    {
                        if (orgState.Species is AnimalSpecies)
                        {
                            workTss = tsm["ant", orgState.Radius, (orgState.Species is AnimalSpecies)];
                        }
                        else
                        {
                            workTss = tsm["plant", orgState.Radius, (orgState.Species is AnimalSpecies)];
                        }
                    }

                    if (workTss != null)
                    {
                        var direction = orgState.ActualDirection;
                        var radius = orgState.Radius;
                        var framedir = 1;
                        var workSurface = workTss.GetClosestSurface((radius*2));
                        radius = workSurface.FrameWidth;

                        if (direction >= 68 && direction < 113)
                        {
                            framedir = 1;
                        }
                        else if (direction >= 113 && direction < 158)
                        {
                            framedir = 2;
                        }
                        else if (direction >= 158 && direction < 203)
                        {
                            framedir = 3;
                        }
                        else if (direction >= 203 && direction < 248)
                        {
                            framedir = 4;
                        }
                        else if (direction >= 248 && direction < 293)
                        {
                            framedir = 5;
                        }
                        else if (direction >= 293 && direction < 338)
                        {
                            framedir = 6;
                        }
                        else if (direction >= 338 && direction < 23)
                        {
                            framedir = 7;
                        }
                        else
                        {
                            framedir = 8;
                        }

                        var dest = new RECT();
                        dest.Top = (int) tsSprite.YPosition - (viewsize.Top + (radius >> 1));
                        dest.Bottom = dest.Top + radius;
                        dest.Left = (int) tsSprite.XPosition - (viewsize.Left + (radius >> 1));
                        dest.Right = dest.Left + radius;

                        if (updateMiniMap)
                        {
                            var miniMapX = (int) (tsSprite.XPosition*miniMap.Width)/actualsize.Right;
                            miniMapX = (miniMapX > (miniMap.Width - 1)) ? (miniMap.Width - 1) : miniMapX;
                            var miniMapY = (int) (tsSprite.YPosition*miniMap.Height)/actualsize.Bottom;
                            miniMapY = (miniMapY > (miniMap.Height - 1)) ? (miniMap.Height - 1) : miniMapY;

                            var brushColor = Color.Fuchsia;

                            if (orgState.Species.GetType() == typeof (PlantSpecies))
                            {
                                brushColor = Color.Lime;
                            }
                            else if (orgState.IsAlive == false)
                            {
                                brushColor = Color.Black;
                            }
                            else
                            {
                                var orgSpecies = (Species) orgState.Species;
                                if (orgSpecies.MarkingColor == KnownColor.Green ||
                                    orgSpecies.MarkingColor == KnownColor.Black)
                                {
                                    brushColor = Color.Red;
                                }
                                else
                                {
                                    brushColor = Color.FromKnownColor(orgSpecies.MarkingColor);
                                }
                            }

                            Brush orgBrush = new SolidBrush(brushColor);

                            var miniMapGraphics = Graphics.FromImage(miniMap);
                            miniMapGraphics.FillRectangle(orgBrush, miniMapX, miniMapY, 12, 12);
                            miniMapGraphics.Dispose();
                            orgBrush.Dispose();

                            //this.miniMap.SetPixel(
                            //    miniMapX,
                            //    miniMapY,
                            //    (orgState.Species is PlantSpecies) ? Color.Green : (!orgState.IsAlive) ? Color.Black : (((Species) orgState.Species).MarkingColor == KnownColor.Green || ((Species) orgState.Species).MarkingColor == KnownColor.Black) ? Color.Red : Color.FromKnownColor(((Species) orgState.Species).MarkingColor));
                        }

                        DirectDrawClippedRect ddClipRect;
                        if (orgState.Species is PlantSpecies)
                        {
                            ddClipRect = workSurface.GrabSprite((int) tsSprite.CurFrame, 0, dest, clipRect);
                        }
                        else
                        {
                            if (tsSprite.PreviousAction == DisplayAction.NoAction ||
                                tsSprite.PreviousAction == DisplayAction.Reproduced ||
                                tsSprite.PreviousAction == DisplayAction.Teleported ||
                                tsSprite.PreviousAction == DisplayAction.Dead)
                            {
                                if (tsSprite.PreviousAction == DisplayAction.Dead)
                                {
                                    ddClipRect = workSurface.GrabSprite(
                                        9,
                                        ((int) DisplayAction.Died) + framedir,
                                        dest,
                                        clipRect);
                                }
                                else
                                {
                                    ddClipRect = workSurface.GrabSprite(0, ((int) DisplayAction.Moved) + framedir, dest,
                                                                        clipRect);
                                }
                            }
                            else
                            {
                                ddClipRect = workSurface.GrabSprite((int) tsSprite.CurFrame,
                                                                    ((int) tsSprite.PreviousAction) + framedir, dest,
                                                                    clipRect);
                            }
                        }

                        if (!ddClipRect.Invisible)
                        {
                            if (!PlantsOnly)
                            {
                                RenderTeleporter(lastTeleporterZIndex, (int) tsSprite.YPosition);
                                lastTeleporterZIndex = (int) tsSprite.YPosition;
                            }

                            dds.Surface.BltFast(
                                ddClipRect.Destination.Left,
                                ddClipRect.Destination.Top,
                                workSurface.SpriteSurface.Surface,
                                ref ddClipRect.Source,
                                CONST_DDBLTFASTFLAGS.DDBLTFAST_SRCCOLORKEY);

                            if (drawtext && !PlantsOnly)
                            {
                                var textSurface = tfm[((Species) orgState.Species).Name];
                                if (textSurface != null && textSurface.Surface != null)
                                {
                                    dds.Surface.BltFast(
                                        ddClipRect.Destination.Left,
                                        ddClipRect.Destination.Top - 14,
                                        textSurface.Surface,
                                        ref TerrariumTextSurfaceManager.StandardFontRect,
                                        CONST_DDBLTFASTFLAGS.DDBLTFAST_SRCCOLORKEY);
                                }
                            }

                            if (!ddClipRect.ClipLeft &&
                                !ddClipRect.ClipRight &&
                                !ddClipRect.ClipTop &&
                                !ddClipRect.ClipBottom)
                            {
                                if (drawDestinationLines)
                                {
                                    if (orgState.CurrentMoveToAction != null)
                                    {
                                        var start = orgState.Position;
                                        var end = orgState.CurrentMoveToAction.MovementVector.Destination;
                                        dds.Surface.SetForeColor(0);
                                        dds.Surface.DrawLine(start.X - viewsize.Left, start.Y - viewsize.Top,
                                                             end.X - viewsize.Left, end.Y - viewsize.Top);
                                    }
                                }

                                if (drawBoundingBox)
                                {
                                    var boundingBox = GetBoundsOfState(orgState);
                                    dds.Surface.SetForeColor(0);
                                    dds.Surface.DrawBox(
                                        boundingBox.Left - viewsize.Left,
                                        boundingBox.Top - viewsize.Top,
                                        (boundingBox.Right + 1) - viewsize.Left,
                                        (boundingBox.Bottom + 1) - viewsize.Top
                                        );
                                }

                                if (tsSprite.Selected)
                                {
                                    dds.Surface.SetForeColor(0);
                                    dds.Surface.DrawBox(ddClipRect.Destination.Left, ddClipRect.Destination.Top,
                                                        ddClipRect.Destination.Right, ddClipRect.Destination.Bottom);

                                    // red  Maybe we want some cool graphic here though
                                    var lineheight =
                                        (int)
                                        ((ddClipRect.Destination.Bottom - ddClipRect.Destination.Top)*
                                         orgState.PercentEnergy);
                                    dds.Surface.SetForeColor(63488);
                                    dds.Surface.DrawLine(ddClipRect.Destination.Left - 1, ddClipRect.Destination.Top,
                                                         ddClipRect.Destination.Left - 1,
                                                         ddClipRect.Destination.Top + lineheight);

                                    //green  Maybe we want some cool graphic here though (or an actual bar?)
                                    lineheight =
                                        (int)
                                        ((ddClipRect.Destination.Bottom - ddClipRect.Destination.Top)*
                                         orgState.PercentInjured);
                                    dds.Surface.SetForeColor(2016);
                                    dds.Surface.DrawLine(ddClipRect.Destination.Right + 1, ddClipRect.Destination.Top,
                                                         ddClipRect.Destination.Right + 1,
                                                         ddClipRect.Destination.Top + lineheight);
                                }
                            }
                        }
                    }
                }
            }

            RenderTeleporter(lastTeleporterZIndex, actualsize.Bottom);

#if TRACE
            Profiler.End("TerrariumDirectDrawGameView.PaintSprites()");
#endif
        }
        /// <summary>
        ///  Adds a new string to the text surface manager.  This creates
        ///  the associated text surface so that text can be rendered with
        ///  a fast Blt rather than with a DrawText call.  Note that caching
        ///  could be done in a much more efficient manner and some text
        ///  surfaces will have identical contents.
        /// </summary>
        /// <param name="key">The string to add.</param>
        internal void Add(string key)
        {
            if (key == null || key.Length == 0)
            {
                return;
            }

            // Set up the surface
            RECT rect = new RECT();
            DirectDrawSurface ddSurface = new DirectDrawSurface(StandardFontRect.Right,StandardFontRect.Bottom);
            ddSurface.TransparencyKey = DirectDrawSurface.MagentaColorKey;

            // Color in the back and add the text
            ddSurface.Surface.BltColorFill(ref rect, DirectDrawSurface.MagentaColorKey.low);
            ddSurface.Surface.SetForeColor(0);

            string text = key;
            if (text.Length > 16)
            {
                text = text.Substring(0,16);
                text += "...";
            }

            IntPtr dcHandle = new IntPtr(ddSurface.Surface.GetDC());

            System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHdc( dcHandle );

            System.Drawing.Font font = new System.Drawing.Font( "Verdana", 6.75f, System.Drawing.FontStyle.Regular );

            graphics.DrawString( text, font, System.Drawing.Brushes.Black, 1, 1 );
            graphics.DrawString( text, font, System.Drawing.Brushes.WhiteSmoke, 0, 0 );

            font.Dispose();

            graphics.Dispose();

            ddSurface.Surface.ReleaseDC( dcHandle.ToInt32() );

            sprites.Add(key, ddSurface);
        }
        /// <summary>
        ///  Adds a new string to the text surface manager.  This creates
        ///  the associated text surface so that text can be rendered with
        ///  a fast Blt rather than with a DrawText call.  Note that caching
        ///  could be done in a much more efficient manner and some text
        ///  surfaces will have identical contents.
        /// </summary>
        /// <param name="key">The string to add.</param>
        internal void Add(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return;
            }

            // Set up the surface
            var rect = new RECT();
            var ddSurface = new DirectDrawSurface(StandardFontRect.Right, StandardFontRect.Bottom)
                                {
                                    TransparencyKey = DirectDrawSurface.MagentaColorKey
                                };

            // Color in the back and add the text
            ddSurface.Surface.BltColorFill(ref rect, DirectDrawSurface.MagentaColorKey.low);
            ddSurface.Surface.SetForeColor(0);

            var text = key;
            if (text.Length > 16)
            {
                text = text.Substring(0, 16);
                text += "...";
            }

            var dcHandle = new IntPtr(ddSurface.Surface.GetDC());

            var graphics = Graphics.FromHdc(dcHandle);

            var font = new Font("Verdana", 6.75f, FontStyle.Regular);

            graphics.DrawString(text, font, Brushes.Black, 1, 1);
            graphics.DrawString(text, font, Brushes.WhiteSmoke, 0, 0);

            font.Dispose();

            graphics.Dispose();

            ddSurface.Surface.ReleaseDC(dcHandle.ToInt32());

            sprites.Add(key, ddSurface);
        }