Пример #1
0
        /// <summary>Sweeps all marked entries as part of mark-and-sweep garbage collection.</summary>
        /// <remarks>This method should be called at the end of each frame.</remarks>
        public void RemoveUnusedEntries()
        {
            bool unusedEntriesFound;

            do
            {
                unusedEntriesFound = false;
                foreach (KeyValuePair <string, TextCacheEntry> dictionaryEntry in entries)
                {
                    TextCacheEntry entry = dictionaryEntry.Value;
                    if (entry.Unused)
                    {
                        for (int i = 0; i < entry.TexturePoolEntries.Length; ++i)
                        {
                            TexturePoolEntry poolEntry = entry.TexturePoolEntries[i];
                            poolEntry.StripeAllocated[entry.StripeIndexes[i]] = false;

                            if (!poolEntry.Used)
                            {
                                poolEntry.Texture.Dispose();
                                texturePool.Remove(poolEntry);
                            }
                        }

                        entries.Remove(dictionaryEntry.Key);
                        unusedEntriesFound = true;
                        break;                          // the iterator has been invalidated, so we need a new foreach loop
                    }
                }
            } while(unusedEntriesFound);
        }
Пример #2
0
        /// <summary>Returns a cached text resource, or creates it if it doesn't exist.</summary>
        /// <param name="text">Text to render.</param>
        /// <returns>The cached text resource as an array of text fragments.</returns>
        public DXCachedTextFragment[] GetText(string text, out int textWidthInPixels)
        {
            TextCacheEntry entry = getOrCreateEntry(text);

            textWidthInPixels = entry.TextWidthInPixels;
            return(entry.TextFragments);
        }
Пример #3
0
        /// <summary>Returns a cached text resource, or creates it if it doesn't exist.</summary>
        /// <param name="text">Text to render.</param>
        /// <returns>The cached text resource as an instance of TextCacheEntry.</returns>
        private TextCacheEntry getOrCreateEntry(string text)
        {
            TextCacheEntry entry;

            if (!entries.TryGetValue(text, out entry))
            {
                // The text was not found in the cache -> create it
                SizeF textSize;
                using (Bitmap bitmap = new Bitmap(1, 1, PixelFormat.Format24bppRgb)) {
                    using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap)) {
                        textSize = graphics.MeasureString(text, font);
                    }
                }

                entry = new TextCacheEntry();
                entries.Add(text, entry);
                entry.Unused            = false;
                entry.TextWidthInPixels = 2 + (int)Math.Ceiling(textSize.Width);
                int fragmentsCount = (entry.TextWidthInPixels + 255) / 256;
                entry.TextFragments      = new DXCachedTextFragment[fragmentsCount];
                entry.TexturePoolEntries = new TexturePoolEntry[fragmentsCount];
                entry.StripeIndexes      = new int[fragmentsCount];

                for (int i = 0; i < fragmentsCount; ++i)
                {
                    allocateTextureFragment(out entry.TexturePoolEntries[i], out entry.StripeIndexes[i], out entry.TextFragments[i]);
                }

                renderTextToTextures(entry, text);
            }
            entry.Unused = false;
            return(entry);
        }
Пример #4
0
    // ======================================================================================
    // Text drawing
    // ======================================================================================

    /// <summary>
    /// Draws a text string. Returns the bounds of the drawn text.
    /// </summary>
    /// <param name="text">The text to draw.</param>
    /// <param name="position">The position where the text will be drawn.</param>
    /// <param name="color">The color of the text.</param>
    /// <param name="font">The font to use to draw the text.</param>
    /// <param name="alignment">The alignment of the text relative to the position. If unspecified the text will be drawn left-aligned.</param>
    /// <param name="measureOnly">If true the text will only be measured and not actually drawn to the screen.</param>
    public static Bounds2 DrawString(string text, Vector2 position, Color color, Font font, TextAlignment alignment = TextAlignment.Left, bool measureOnly = false)
    {
        // Every time we draw a string we have to render it into a texture, which isn't the fastest thing in the world.
        // To make this faster we keep a cache of recently rendered strings and reuse them when possible.

        Tuple <Font, string> textCacheKey = Tuple.Create(font, text);
        TextCacheEntry       entry;

        if (TextCache.TryGetValue(textCacheKey, out entry))
        {
            // We found the text in the cache, so use it and reset its age so that it doesn't get freed this frame:
            entry.Age = 0;
        }
        else
        {
            // We were unable to find the text in the cache, so render it to a texture and cache it for later:
            SDL.SDL_Color white = new SDL.SDL_Color()
            {
                r = 255, g = 255, b = 255, a = 255
            };
            IntPtr surface = SDL_ttf.TTF_RenderText_Blended(font.Handle, text, white);
            IntPtr handle  = SDL.SDL_CreateTextureFromSurface(Renderer, surface);
            SDL.SDL_FreeSurface(surface);
            entry = new TextCacheEntry(handle);
            TextCache[textCacheKey] = entry;
        }

        // Query the texture dimensions:
        uint format;
        int  access, width, height;

        SDL.SDL_QueryTexture(entry.Handle, out format, out access, out width, out height);

        // Apply text alignment relative to the draw position:
        if (alignment == TextAlignment.Center)
        {
            position.X -= width / 2;
        }
        else if (alignment == TextAlignment.Right)
        {
            position.X -= width;
        }

        // If we're not only measuring the text, draw it:
        if (!measureOnly)
        {
            Texture texture = new Texture(entry.Handle, width, height);
            DrawTexture(texture, position, color);
        }

        // Return the bounds of the text:
        return(new Bounds2(position, new Vector2(width, height)));
    }
Пример #5
0
        /// <summary>Creates a new text resource.</summary>
        /// <param name="cacheEntry">An instance of TextCacheEntry.</param>
        /// <param name="text">Text.</param>
        /// <remarks>The text is white, surrounded by a one pixel wide black edge.</remarks>
        private unsafe void renderTextToTextures(TextCacheEntry cacheEntry, string text)
        {
            using (Bitmap bitmap = new Bitmap(cacheEntry.TextWidthInPixels, textHeight, PixelFormat.Format24bppRgb)) {
                using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap)) {
                    graphics.FillRectangle(new SolidBrush(Color.Black), 0, 0, bitmap.Width, textHeight);
                    graphics.DrawString(text, font, new SolidBrush(Color.White), 1.0f, 2.0f - descentInPixels);
                }

                BitmapData bitmapData = bitmap.LockBits(
                    new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                    ImageLockMode.ReadOnly,
                    PixelFormat.Format24bppRgb);

                int width  = bitmapData.Width;
                int stride = bitmapData.Stride;

                // build transparency mask (using a 3x3 filter grid)
                ushort[] transparencyMask = new ushort[(width + 2) * (textHeight + 2)];
                fixed(ushort *mask2 = transparencyMask)
                {
                    {
                        ushort *firstLine  = mask2;
                        ushort *secondLine = firstLine + (width + 2);
                        ushort *thirdLine  = secondLine + (width + 2);

                        byte *source = (byte *)bitmapData.Scan0;
                        for (int y = 0; y < textHeight; ++y)
                        {
                            for (int x = 0; x < width; ++x)
                            {
                                ushort blue = *source;
                                *(firstLine + 0)  += blue; *(firstLine + 1) += blue; *(firstLine + 2) += blue;
                                *(secondLine + 0) += blue; *(secondLine + 1) += blue; *(secondLine + 2) += blue;
                                *(thirdLine + 0)  += blue; *(thirdLine + 1) += blue; *(thirdLine + 2) = blue;                           // *(thirdLine+2) == 0 -> no need to add
                                ++firstLine; ++secondLine; ++thirdLine;
                                source += 3;
                            }
                            firstLine += 2; secondLine += 2; thirdLine += 2;
                            source    += stride - width * 3;
                        }
                    }

                    // copy bitmap to textures
                    int fragmentsCount = cacheEntry.TexturePoolEntries.Length;

                    for (int i = 0; i < fragmentsCount; ++i)
                    {
                        Texture texture     = cacheEntry.TexturePoolEntries[i].Texture;
                        int     stripeIndex = cacheEntry.StripeIndexes[i];

                        int fragmentOrigin = i * 256;
                        int fragmentWidth  = (i < fragmentsCount - 1 ? 256 : width % 256);

                        int   texturePitch;
                        byte *textureBits = (byte *)texture.LockRectangle(0, LockFlags.None, out texturePitch).InternalData.ToPointer();

                        byte *  source = (byte *)bitmapData.Scan0 + fragmentOrigin * 3;
                        ushort *mask   = mask2 + (width + 3) + fragmentOrigin;

                        byte *dest = textureBits + (stripeIndex * textHeight) * texturePitch;
                        for (int y = 0; y < textHeight; ++y)
                        {
                            for (int x = 0; x < fragmentWidth; ++x)
                            {
                                *(dest + 0) = *(source + 0);
                                *(dest + 1) = *(source + 1);
                                *(dest + 2) = *(source + 2);
                                *(dest + 3) = (*mask > 0xFF ? (byte)0xFF : (byte)*mask);
                                dest       += 4;
                                source     += 3;
                                ++mask;
                            }
                            for (int x = fragmentWidth; x < 256; ++x)
                            {
                                *(uint *)dest = 0x00000000;
                                dest         += 4;
                            }
                            dest   += texturePitch - 256 * 4;
                            source += stride - fragmentWidth * 3;
                            mask   += width + 2 - fragmentWidth;
                        }

                        texture.UnlockRectangle(0);
                    }
                }
            }
        }