<commit_before>package com.glitchcog.fontificator.sprite;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.ImageObserver;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.glitchcog.fontificator.bot.Message;
import com.glitchcog.fontificator.bot.MessageType;
import com.glitchcog.fontificator.config.ConfigColor;
import com.glitchcog.fontificator.config.ConfigEmoji;
import com.glitchcog.fontificator.config.ConfigFont;
import com.glitchcog.fontificator.config.ConfigMessage;
import com.glitchcog.fontificator.config.FontType;
import com.glitchcog.fontificator.emoji.EmojiManager;
import com.glitchcog.fontificator.emoji.LazyLoadEmoji;

/**
 * A font that is drawn with a sprite
 * 
 * @author <NAME>
 */
public class SpriteFont
{
    private static final Logger logger = Logger.getLogger(SpriteFont.class);

    public static final String NORMAL_ASCII_KEY = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + (char) 127;

    /**
     * The number of pixels between each individual badge and between badges and the username;
     */
    public static final int BADGE_MINIMUM_SPACING_PIXELS = 3;

    protected SpriteCache sprites;

    protected Map<Integer, Rectangle> characterBounds;

    /**
     * Characters that can be line breaks for wrapping to the next line
     */
    protected static final String WORD_BREAKS = " \n\r\t";

    /**
     * Characters that indicate a return to the start of the next line
     */
    protected static final String LINE_BREAKS = "\n";

    /**
     * Shift the messages down this many lines
     */
    protected int lineScrollOffset;

    protected ConfigFont config;

    public SpriteFont(ConfigFont config)
    {
        logger.trace("Creating sprite font using config font filename " + (config == null ? "null" : config.getFontFilename()));
        this.config = config;
        this.characterBounds = new HashMap<Integer, Rectangle>();
        this.sprites = new SpriteCache(config);
    }

    /**
     * Get the dimensions of an emoji image
     * 
     * @param img
     * @param emojiConfig
     * @return
     */
    private int[] getEmojiDimensions(LazyLoadEmoji emoji, ConfigEmoji emojiConfig)
    {
        Image img = emoji.getImage(emojiConfig.isAnimationEnabled());

        int iw;
        int ih;

        if (img == null)
        {
            switch (emojiConfig.getDisplayStrategy())
            {
            case SPACE:
            case BOX_FILL:
            case BOX_FRAME:
                iw = emoji.getWidth();
                ih = emoji.getHeight();
                break;
            case UNKNOWN:
                // Do not use the emoji scaling below because it's a character, not an emoji
                // We can pass a null in for the FontMetrics, because we know the unknown character falls within the
                // non-extended range
                return new int[] { getCharacterWidth(null, new SpriteCharacterKey(config.getUnknownChar()), emojiConfig), 1 };
            case NOTHING:
            default:
                iw = 0;
                ih = 1;
                break;
            }
        }
        else
        {
            iw = emoji.getWidth();
            ih = emoji.getHeight();
        }

        float h;
        float w;

        float eScale = emoji.getType().isBadge() ? (emojiConfig.getBadgeScale() / 100.0f) : (emojiConfig.getEmojiScale() / 100.0f);
        if ((emoji.getType().isBadge() && emojiConfig.isBadgeScaleToLine()) || (!emoji.getType().isBadge() && emojiConfig.isEmojiScaleToLine()))
        {
            final float emojiScaleRatio = eScale * getLineHeightScaled() / (float) ih;
            h = ih * emojiScaleRatio;
            w = iw * h / ih;
        }
        else
        {
            w = eScale * iw;
            h = eScale * ih;
        }

        return new int[] { (int) w, (int) h };
    }

    private char[] fontMetricCharArray = new char[1];

    /**
     * Return how wide a character is in pixels- must take scale into consideration scale
     * 
     * @param c
     * @return character width
     */
    private int getCharacterWidth(FontMetrics fontMetrics, SpriteCharacterKey c, ConfigEmoji emojiConfig)
    {
        if (c.isChar())
        {
            int baseWidth;

            // Extended characters are enabled
            if (c.isExtended())
            {
                if (config.isExtendedCharEnabled())
                {
                    // Return string width of extended char
                    //fontMetricCharArray[0] = c.getChar();
                    baseWidth = fontMetrics.charWidth(c.getCodepoint());
                    // Don't include scale in this calculation, because it's already built into the font size
                    return (int) (baseWidth + config.getCharSpacing() * config.getFontScale());
                }
                // The extended character should be replaced with the unknown character
                else
                {
                    baseWidth = getCharacterBounds(config.getUnknownChar()).width;
                }
            }
            // It's a normal character
            else
            {
                // Character
                baseWidth = getCharacterBounds(c.getChar()).width;
            }
            return (int) ((baseWidth + config.getCharSpacing()) * config.getFontScale());
        }
        else
        {
            // Emoji
            int[] eDim = getEmojiDimensions(c.getEmoji(), emojiConfig);
            final int charSpacing = (int) (config.getCharSpacing() * config.getFontScale());
            final int extraSpacing = (c.getEmoji().getType().isBadge() ? Math.max(charSpacing, (int) (BADGE_MINIMUM_SPACING_PIXELS * config.getFontScale())) : charSpacing);
            return eDim[0] + extraSpacing;
        }

    }

    public void calculateCharacterDimensions()
    {
        // Start from scratch
        characterBounds.clear();

        // For fixed width, just put the same sized box for all characters. The
        // only difference is the location on the sprite grid
        switch (config.getFontType())
        {
        case FIXED_WIDTH:
            calculateFixedCharacterDimensions();
            break;
        case VARIABLE_WIDTH:<commit_msg>Clean up SpriteFont<commit_after>package com.glitchcog.fontificator.sprite;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.ImageObserver;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.glitchcog.fontificator.bot.Message;
import com.glitchcog.fontificator.bot.MessageType;
import com.glitchcog.fontificator.config.ConfigColor;
import com.glitchcog.fontificator.config.ConfigEmoji;
import com.glitchcog.fontificator.config.ConfigFont;
import com.glitchcog.fontificator.config.ConfigMessage;
import com.glitchcog.fontificator.config.FontType;
import com.glitchcog.fontificator.emoji.EmojiManager;
import com.glitchcog.fontificator.emoji.LazyLoadEmoji;

/**
 * A font that is drawn with a sprite
 * 
 * @author <NAME>
 */
public class SpriteFont
{
    private static final Logger logger = Logger.getLogger(SpriteFont.class);

    public static final String NORMAL_ASCII_KEY = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + (char) 127;

    /**
     * The number of pixels between each individual badge and between badges and the username;
     */
    public static final int BADGE_MINIMUM_SPACING_PIXELS = 3;

    protected SpriteCache sprites;

    protected Map<Integer, Rectangle> characterBounds;

    /**
     * Characters that can be line breaks for wrapping to the next line
     */
    protected static final String WORD_BREAKS = " \n\r\t";

    /**
     * Characters that indicate a return to the start of the next line
     */
    protected static final String LINE_BREAKS = "\n";

    /**
     * Shift the messages down this many lines
     */
    protected int lineScrollOffset;

    protected ConfigFont config;

    public SpriteFont(ConfigFont config)
    {
        logger.trace("Creating sprite font using config font filename " + (config == null ? "null" : config.getFontFilename()));
        this.config = config;
        this.characterBounds = new HashMap<Integer, Rectangle>();
        this.sprites = new SpriteCache(config);
    }

    /**
     * Get the dimensions of an emoji image
     * 
     * @param img
     * @param emojiConfig
     * @return
     */
    private int[] getEmojiDimensions(LazyLoadEmoji emoji, ConfigEmoji emojiConfig)
    {
        Image img = emoji.getImage(emojiConfig.isAnimationEnabled());

        int iw;
        int ih;

        if (img == null)
        {
            switch (emojiConfig.getDisplayStrategy())
            {
            case SPACE:
            case BOX_FILL:
            case BOX_FRAME:
                iw = emoji.getWidth();
                ih = emoji.getHeight();
                break;
            case UNKNOWN:
                // Do not use the emoji scaling below because it's a character, not an emoji
                // We can pass a null in for the FontMetrics, because we know the unknown character falls within the
                // non-extended range
                return new int[] { getCharacterWidth(null, new SpriteCharacterKey(config.getUnknownChar()), emojiConfig), 1 };
            case NOTHING:
            default:
                iw = 0;
                ih = 1;
                break;
            }
        }
        else
        {
            iw = emoji.getWidth();
            ih = emoji.getHeight();
        }

        float h;
        float w;

        float eScale = emoji.getType().isBadge() ? (emojiConfig.getBadgeScale() / 100.0f) : (emojiConfig.getEmojiScale() / 100.0f);
        if ((emoji.getType().isBadge() && emojiConfig.isBadgeScaleToLine()) || (!emoji.getType().isBadge() && emojiConfig.isEmojiScaleToLine()))
        {
            final float emojiScaleRatio = eScale * getLineHeightScaled() / (float) ih;
            h = ih * emojiScaleRatio;
            w = iw * h / ih;
        }
        else
        {
            w = eScale * iw;
            h = eScale * ih;
        }

        return new int[] { (int) w, (int) h };
    }

    /**
     * Return how wide a character is in pixels- must take scale into consideration scale
     * 
     * @param c
     * @return character width
     */
    private int getCharacterWidth(FontMetrics fontMetrics, SpriteCharacterKey c, ConfigEmoji emojiConfig)
    {
        if (c.isChar())
        {
            int baseWidth;

            // Extended characters are enabled
            if (c.isExtended())
            {
                if (config.isExtendedCharEnabled())
                {
                    // Return string width of extended char
                    baseWidth = fontMetrics.charWidth(c.getCodepoint());
                    // Don't include scale in this calculation, because it's already built into the font size
                    return (int) (baseWidth + config.getCharSpacing() * config.getFontScale());
                }
                // The extended character should be replaced with the unknown character
                else
                {
                    baseWidth = getCharacterBounds(config.getUnknownChar()).width;
                }
            }
            // It's a normal character
            else
            {
                // Character
                baseWidth = getCharacterBounds(c.getCodepoint()).width;
            }
            return (int) ((baseWidth + config.getCharSpacing()) * config.getFontScale());
        }
        else
        {
            // Emoji
            int[] eDim = getEmojiDimensions(c.getEmoji(), emojiConfig);
            final int charSpacing = (int) (config.getCharSpacing() * config.getFontScale());
            final int extraSpacing = (c.getEmoji().getType().isBadge() ? Math.max(charSpacing, (int) (BADGE_MINIMUM_SPACING_PIXELS * config.getFontScale())) : charSpacing);
            return eDim[0] + extraSpacing;
        }

    }

    public void calculateCharacterDimensions()
    {
        // Start from scratch
        characterBounds.clear();

        // For fixed width, just put the same sized box for all characters. The
        // only difference is the location on the sprite grid
        switch (config.getFontType())
        {
        case FIXED_WIDTH:
            calculateFixedCharacterDimensions();
            break;
        case VARIABLE_WIDTH:
{'fraction_non_alphanumeric': 0.09799152675349129, 'fraction_numerical': 0.004550447199121293, 'mean_word_length': 2.019185220274751, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, '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': '3521898', 'n_tokens_mistral': 3717, 'n_tokens_neox': 3604, 'n_words': 930}