<commit_before>﻿// Copyright (c) 2007-2018 ppy Pty Ltd <<EMAIL>>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System;
using System.Collections.Generic;
using osu.Framework.Graphics.OpenGL.Textures;
using osuTK.Graphics.ES30;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

namespace osu.Framework.Graphics.Textures
{
    public class TextureAtlas
    {
        // We are adding an extra padding on top of the padding required by
        // mipmap blending in order to support smooth edges without antialiasing which requires
        // inflating texture rectangles.
        private const int padding = (1 << TextureGLSingle.MAX_MIPMAP_LEVELS) + Sprite.MAX_EDGE_SMOOTHNESS * 2;

        private readonly List<RectangleI> subTextureBounds = new List<RectangleI>();
        internal TextureGLSingle AtlasTexture;

        private readonly int atlasWidth;
        private readonly int atlasHeight;

        private int currentY;

        private int mipmapLevels => (int)Math.Log(atlasWidth, 2);

        internal TextureWhitePixel WhitePixel
        {
            get
            {
                if (AtlasTexture == null)
                    Reset();

                return new TextureWhitePixel(new TextureGLAtlasWhite(AtlasTexture));
            }
        }

        private readonly bool manualMipmaps;
        private readonly All filteringMode;
        private readonly object textureRetrievalLock = new object();

        public TextureAtlas(int width, int height, bool manualMipmaps = false, All filteringMode = All.Linear)
        {
            atlasWidth = width;
            atlasHeight = height;
            this.manualMipmaps = manualMipmaps;
            this.filteringMode = filteringMode;
        }

        public void Reset()
        {
            subTextureBounds.Clear();
            currentY = 0;

            //may be zero in a headless context.
            if (atlasWidth == 0 || atlasHeight == 0)
                return;

            if (AtlasTexture == null)
                Logger.Log($"New TextureAtlas initialised {atlasWidth}x{atlasHeight}", LoggingTarget.Runtime, LogLevel.Debug);

            AtlasTexture = new TextureGLAtlas(atlasWidth, atlasHeight, manualMipmaps, filteringMode);

            using (var whiteTex = Add(3, 3))
                whiteTex.SetData(new TextureUpload(new Image<Rgba32>(SixLabors.ImageSharp.Configuration.Default, whiteTex.Width, whiteTex.Height, Rgba32.White)));
        }

        private Vector2I findPosition(int width, int height)
        {
            if (atlasHeight == 0 || atlasWidth == 0) return Vector2I.Zero;

            if (currentY + height > atlasHeight)
            {
                Logger.Log($"TextureAtlas size exceeded; generating new {atlasWidth}x{atlasHeight} texture", LoggingTarget.Performance);
                Reset();
            }

            // Super naive implementation only going from left to right.
            Vector2I res = new Vector2I(0, currentY);

            int maxY = currentY;
            foreach (RectangleI bounds in subTextureBounds)
            {
                // +1 is required to prevent aliasing issues with sub-pixel positions while drawing. Bordering edged of other textures can show without it.
                res.X = Math.Max(res.X, bounds.Right + padding);
                maxY = Math.Max(maxY, bounds.Bottom);
            }

            if (res.X + width > atlasWidth)
            {
                // +1 is required to prevent aliasing issues with sub-pixel positions while drawing. Bordering edged of other textures can show without it.
                currentY = maxY + padding;
                subTextureBounds.Clear();
                res = findPosition(width, height);
            }

            return res;
        }

        internal TextureGL Add(int width, int height)
        {
            lock (textureRetrievalLock)
            {
                if (width > atlasWidth || height > atlasHeight)
                    throw new InvalidOperationException($"Size of allocation request ({width}x{height}) exceeds the maximum size of this {nameof(TextureAtlas)} ({atlasWidth}x{atlasHeight}).");
<commit_msg>Move check outside of lock<commit_after>﻿// Copyright (c) 2007-2018 ppy Pty Ltd <<EMAIL>>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System;
using System.Collections.Generic;
using osu.Framework.Graphics.OpenGL.Textures;
using osuTK.Graphics.ES30;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

namespace osu.Framework.Graphics.Textures
{
    public class TextureAtlas
    {
        // We are adding an extra padding on top of the padding required by
        // mipmap blending in order to support smooth edges without antialiasing which requires
        // inflating texture rectangles.
        private const int padding = (1 << TextureGLSingle.MAX_MIPMAP_LEVELS) + Sprite.MAX_EDGE_SMOOTHNESS * 2;

        private readonly List<RectangleI> subTextureBounds = new List<RectangleI>();
        internal TextureGLSingle AtlasTexture;

        private readonly int atlasWidth;
        private readonly int atlasHeight;

        private int currentY;

        private int mipmapLevels => (int)Math.Log(atlasWidth, 2);

        internal TextureWhitePixel WhitePixel
        {
            get
            {
                if (AtlasTexture == null)
                    Reset();

                return new TextureWhitePixel(new TextureGLAtlasWhite(AtlasTexture));
            }
        }

        private readonly bool manualMipmaps;
        private readonly All filteringMode;
        private readonly object textureRetrievalLock = new object();

        public TextureAtlas(int width, int height, bool manualMipmaps = false, All filteringMode = All.Linear)
        {
            atlasWidth = width;
            atlasHeight = height;
            this.manualMipmaps = manualMipmaps;
            this.filteringMode = filteringMode;
        }

        public void Reset()
        {
            subTextureBounds.Clear();
            currentY = 0;

            //may be zero in a headless context.
            if (atlasWidth == 0 || atlasHeight == 0)
                return;

            if (AtlasTexture == null)
                Logger.Log($"New TextureAtlas initialised {atlasWidth}x{atlasHeight}", LoggingTarget.Runtime, LogLevel.Debug);

            AtlasTexture = new TextureGLAtlas(atlasWidth, atlasHeight, manualMipmaps, filteringMode);

            using (var whiteTex = Add(3, 3))
                whiteTex.SetData(new TextureUpload(new Image<Rgba32>(SixLabors.ImageSharp.Configuration.Default, whiteTex.Width, whiteTex.Height, Rgba32.White)));
        }

        private Vector2I findPosition(int width, int height)
        {
            if (atlasHeight == 0 || atlasWidth == 0) return Vector2I.Zero;

            if (currentY + height > atlasHeight)
            {
                Logger.Log($"TextureAtlas size exceeded; generating new {atlasWidth}x{atlasHeight} texture", LoggingTarget.Performance);
                Reset();
            }

            // Super naive implementation only going from left to right.
            Vector2I res = new Vector2I(0, currentY);

            int maxY = currentY;
            foreach (RectangleI bounds in subTextureBounds)
            {
                // +1 is required to prevent aliasing issues with sub-pixel positions while drawing. Bordering edged of other textures can show without it.
                res.X = Math.Max(res.X, bounds.Right + padding);
                maxY = Math.Max(maxY, bounds.Bottom);
            }

            if (res.X + width > atlasWidth)
            {
                // +1 is required to prevent aliasing issues with sub-pixel positions while drawing. Bordering edged of other textures can show without it.
                currentY = maxY + padding;
                subTextureBounds.Clear();
                res = findPosition(width, height);
            }

            return res;
        }

        internal TextureGL Add(int width, int height)
        {
            if (width > atlasWidth || height > atlasHeight)
                throw new InvalidOperationException($"Size of allocation request ({width}x{height}) exceeds the maximum size of this {nameof(TextureAtlas)} ({atlasWidth}x{atlasHeight}).");

            lock (textureRetrievalLock)
            {
{'fraction_non_alphanumeric': 0.08084074373484236, 'fraction_numerical': 0.007160180159371752, 'mean_word_length': 2.16636197440585, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5004297', 'n_tokens_mistral': 2394, 'n_tokens_neox': 2229, 'n_words': 656}